commit 9eb549294279c8eebded56cc94892dd364c110eb
parent 51e33eccd6f0ef4bb8d87140e5755925369d4296
Author: t3sserakt <t3ss@posteo.de>
Date: Wed, 13 May 2026 11:17:08 +0200
WIP: server crash for group, saving client from being crashed
Diffstat:
3 files changed, 58 insertions(+), 12 deletions(-)
diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/boundimpl/GnunetChatBoundService.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/boundimpl/GnunetChatBoundService.kt
@@ -138,6 +138,35 @@ class GnunetChatBoundService(
return appContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
+ /** True if the AIDL binder is currently connected. UI can use this to
+ * short-circuit before issuing a sync call that would otherwise crash
+ * the main thread when the server has died. */
+ fun isRemoteAlive(): Boolean = remoteRef.get() != null
+
+ /** Wraps a `runBlocking { withReadyRemote(...) }` body and converts the
+ * IllegalStateException / DeadObjectException-wrapped RuntimeException
+ * that fires when the server is gone into a logged warning + the
+ * caller-supplied default. Use only for methods whose callers can
+ * tolerate a stale answer (isGroup → false, getGroupFromContext → null,
+ * etc). For methods whose callers can't, let the throw propagate and
+ * the UI layer is expected to wrap them. */
+ private inline fun <T> safeSync(default: T, label: String, block: () -> T): T = try {
+ block()
+ } catch (e: IllegalStateException) {
+ Log.w(TAG, "$label: remote unavailable, returning default", e)
+ default
+ } catch (e: RuntimeException) {
+ val cause = e.cause
+ if (cause is IllegalStateException || cause is DeadObjectException ||
+ cause is RemoteException
+ ) {
+ Log.w(TAG, "$label: remote call failed, returning default", e)
+ default
+ } else {
+ throw e
+ }
+ }
+
fun unbind() {
val remote = remoteRef.get()
val dr = deathRecipient
@@ -569,8 +598,8 @@ class GnunetChatBoundService(
}
}
- override fun getGroupFromContext(context: ChatContext): ChatGroup? {
- return runBlocking {
+ override fun getGroupFromContext(context: ChatContext): ChatGroup? = safeSync(null, "getGroupFromContext") {
+ runBlocking {
val groupDto = withReadyRemote(lastHandle) { remote, _ ->
remote.getGroupFromContext(context.toDto())
}
@@ -848,16 +877,16 @@ class GnunetChatBoundService(
}
}
- override fun isGroup(context: ChatContext): Boolean {
- return runBlocking {
+ override fun isGroup(context: ChatContext): Boolean = safeSync(false, "isGroup") {
+ runBlocking {
withReadyRemote(lastHandle) { remote, _ ->
remote.isGroup(context.toDto())
}
}
}
- override fun isPlatform(context: ChatContext): Boolean {
- return runBlocking {
+ override fun isPlatform(context: ChatContext): Boolean = safeSync(false, "isPlatform") {
+ runBlocking {
withReadyRemote(lastHandle) { remote, _ ->
remote.isPlatform(context.toDto())
}
diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/chat/ChatFragment.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/chat/ChatFragment.kt
@@ -34,6 +34,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
+import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.MenuHost
import androidx.core.view.MenuProvider
@@ -151,13 +152,28 @@ class ChatFragment : Fragment(R.layout.fragment_chat) {
}
}
}, viewLifecycleOwner)
- val title = when {
- gnunetChat.isGroup(chatContext) || gnunetChat.isPlatform(chatContext) -> {
- gnunetChat.getGroupFromContext(chatContext)?.name ?: getString(R.string.placeholder_label_chat)
- }
- else -> {
- gnunetChat.getContextContact(chatContext).name
+ val title = try {
+ when {
+ gnunetChat.isGroup(chatContext) || gnunetChat.isPlatform(chatContext) -> {
+ gnunetChat.getGroupFromContext(chatContext)?.name
+ ?: getString(R.string.placeholder_label_chat)
+ }
+ else -> {
+ gnunetChat.getContextContact(chatContext).name
+ }
}
+ } catch (t: Throwable) {
+ // The server died (or the binder hasn't reattached yet). Bounce
+ // the user back to the chat list instead of letting the
+ // IllegalStateException kill the whole app on the main thread.
+ Log.w("ChatFragment", "Server unreachable while opening chat; popping back stack", t)
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.toast_server_unreachable),
+ Toast.LENGTH_SHORT
+ ).show()
+ runCatching { findNavController().popBackStack() }
+ return
}
(requireActivity() as AppCompatActivity).supportActionBar?.title = title
}
diff --git a/GNUnetMessenger/app/src/main/res/values/strings.xml b/GNUnetMessenger/app/src/main/res/values/strings.xml
@@ -35,6 +35,7 @@
<string name="gnunet_connection_failed">Could not connect to GNUnet.</string>
<string name="account_connect_failed">Could not activate the selected account.</string>
<string name="account_list_load_failed">Could not load the account list.</string>
+ <string name="toast_server_unreachable">GNUnet server unreachable — please retry.</string>
<string-array name="lobby_lifetimes">
<item>Off</item>