From 8ca054dfcdf32d66b46113139ed6ba65a18bef7c Mon Sep 17 00:00:00 2001 From: euxane Date: Wed, 18 Sep 2024 23:41:13 +0200 Subject: errors: add log and config dirs openers to error messages --- .../activities/start/ErrorNotificationFragment.kt | 6 ++- .../main/java/org/pacien/tincapp/context/App.kt | 16 +++++- .../tincapp/context/AppNotificationManager.kt | 22 ++++++-- .../org/pacien/tincapp/service/TincVpnService.kt | 60 ++++++++++++++++++---- .../tincapp/storageprovider/BrowseFilesIntents.kt | 6 +++ .../main/res/layout/start_error_notification.xml | 30 +++++++++++ app/src/main/res/values/strings.xml | 2 + 7 files changed, 125 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt b/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt index 08dab8d..f629623 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt @@ -1,6 +1,6 @@ /* * Tinc Mesh VPN: Android client and user interface - * Copyright (C) 2017-2023 Euxane P. TRAN-GIRARD + * Copyright (C) 2017-2024 Euxane P. TRAN-GIRARD * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,8 @@ import org.pacien.tincapp.activities.BaseFragment import org.pacien.tincapp.context.App import org.pacien.tincapp.context.AppNotificationManager import org.pacien.tincapp.databinding.StartErrorNotificationBinding +import org.pacien.tincapp.storageprovider.BrowseFilesIntents +import org.pacien.tincapp.storageprovider.FilesDocumentsProvider /** * @author euxane @@ -56,5 +58,7 @@ class ErrorNotificationFragment : BaseFragment() { val maybeError = notificationManager.getError() viewBinding.errorNotification = maybeError viewBinding.openManualAction = { App.openURL(maybeError?.manualLink!!) } + viewBinding.openConfigDirAction = { openDocumentTree(BrowseFilesIntents.networkConfigDirDocId(maybeError?.configDir)) } + viewBinding.openLogDirAction = { openDocumentTree(FilesDocumentsProvider.VIRTUAL_ROOT_LOGS) } } } diff --git a/app/src/main/java/org/pacien/tincapp/context/App.kt b/app/src/main/java/org/pacien/tincapp/context/App.kt index 6145c65..e6402d2 100644 --- a/app/src/main/java/org/pacien/tincapp/context/App.kt +++ b/app/src/main/java/org/pacien/tincapp/context/App.kt @@ -70,8 +70,20 @@ class App : Application() { .packageManager .getApplicationInfo(BuildConfig.APPLICATION_ID, 0) - fun alert(@StringRes title: Int, msg: String, manualLink: String? = null) = - notificationManager.notifyError(appContext!!.getString(title), msg, manualLink) + fun alert( + @StringRes title: Int, + msg: String, + manualLink: String? = null, + configDir: String? = null, + proposeLogs: Boolean = false, + ) = + notificationManager.notifyError( + appContext!!.getString(title), + msg, + manualLink, + configDir, + proposeLogs, + ) fun openURL(url: String) { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) diff --git a/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt b/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt index c7a5ade..f4332e9 100644 --- a/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt +++ b/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt @@ -1,6 +1,6 @@ /* * Tinc Mesh VPN: Android client and user interface - * Copyright (C) 2017-2023 Euxane P. TRAN-GIRARD + * Copyright (C) 2017-2024 Euxane P. TRAN-GIRARD * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +28,9 @@ class AppNotificationManager(private val context: Context) { data class ErrorNotification( val title: String, val message: String, - val manualLink: String? + val manualLink: String?, + val configDir: String?, + val proposeLogs: Boolean, ) companion object { @@ -36,6 +38,8 @@ class AppNotificationManager(private val context: Context) { private const val STORE_KEY_TITLE = "title" private const val STORE_KEY_MESSAGE = "message" private const val STORE_KEY_MANUAL_LINK = "manual_link" + private const val STORE_CONFIG_DIR = "config_dir" + private const val STORE_PROPOSE_LOGS = "propose_logs_dir" } private val store by lazy { context.getSharedPreferences(STORE_NAME, Context.MODE_PRIVATE)!! } @@ -46,16 +50,26 @@ class AppNotificationManager(private val context: Context) { return ErrorNotification( store.getString(STORE_KEY_TITLE, null)!!, store.getString(STORE_KEY_MESSAGE, null)!!, - store.getString(STORE_KEY_MANUAL_LINK, null) + store.getString(STORE_KEY_MANUAL_LINK, null), + store.getString(STORE_CONFIG_DIR, null), + store.getBoolean(STORE_PROPOSE_LOGS, false), ) } - fun notifyError(title: String, message: String, manualLink: String? = null) { + fun notifyError( + title: String, + message: String, + manualLink: String? = null, + configDir: String? = null, + proposeLogs: Boolean = false, + ) { store .edit() .putString(STORE_KEY_TITLE, title) .putString(STORE_KEY_MESSAGE, message) .putString(STORE_KEY_MANUAL_LINK, manualLink) + .putString(STORE_CONFIG_DIR, configDir) + .putBoolean(STORE_PROPOSE_LOGS, proposeLogs) .apply() } diff --git a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt index 47ba0ec..77d7e96 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt @@ -123,11 +123,23 @@ class TincVpnService : VpnService() { val interfaceCfg = try { VpnInterfaceConfiguration.fromIfaceConfiguration(AppPaths.existing(AppPaths.netConfFile(netName))) } catch (e: FileNotFoundException) { - return reportError(resources.getString(R.string.notification_error_message_network_config_not_found_format, e.defaultMessage()), e, "configuration") + return reportError( + resources.getString(R.string.notification_error_message_network_config_not_found_format, e.defaultMessage()), + e, + docTopic = "configuration" + ) } catch (e: ConversionException) { - return reportError(resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), e, "network-interface") + return reportError( + resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), + e, + docTopic = "network-interface", + ) } catch (e: Exception) { - return reportError(resources.getString(R.string.notification_error_message_could_not_read_network_configuration_format, e.defaultMessage()), e) + return reportError( + resources.getString(R.string.notification_error_message_could_not_read_network_configuration_format, e.defaultMessage()), + e, + configDir = netName, + ) } val deviceFd = try { @@ -138,11 +150,24 @@ class TincVpnService : VpnService() { .also { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) it.setMetered(false) } .establish()!! } catch (e: IllegalArgumentException) { - return reportError(resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), e, "network-interface") + return reportError( + resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), + e, + docTopic = "network-interface", + configDir = netName, + ) } catch (e: NullPointerException) { - return reportError(resources.getString(R.string.notification_error_message_could_not_bind_iface), e) + return reportError( + resources.getString(R.string.notification_error_message_could_not_bind_iface), + e, + proposeLogs = true, + ) } catch (e: Exception) { - return reportError(resources.getString(R.string.notification_error_message_could_not_configure_iface, e.defaultMessage()), e) + return reportError( + resources.getString(R.string.notification_error_message_could_not_configure_iface, e.defaultMessage()), + e, + proposeLogs = true, + ) } val serverSocket = LocalServerSocket(DEVICE_FD_ABSTRACT_SOCKET) @@ -156,7 +181,11 @@ class TincVpnService : VpnService() { deviceFd.close() if (exception != null) { - reportError(resources.getString(R.string.notification_error_message_daemon_exited, exception.cause!!.defaultMessage()), exception) + reportError( + resources.getString(R.string.notification_error_message_daemon_exited, exception.cause!!.defaultMessage()), + exception, + proposeLogs = true, + ) } else { log.info("tinc daemon started.") broadcastEvent(Actions.EVENT_CONNECTED) @@ -181,15 +210,26 @@ class TincVpnService : VpnService() { } ?: CompletableFuture.completedFuture(Unit) } - private fun reportError(msg: String, e: Throwable? = null, docTopic: String? = null) { + private fun reportError( + msg: String, + e: Throwable? = null, + docTopic: String? = null, + configDir: String? = null, + proposeLogs: Boolean = false, + ) { if (e != null) log.error(msg, e) else log.error(msg) broadcastEvent(Actions.EVENT_ABORTED) - App.alert(R.string.notification_error_title_unable_to_start_tinc, msg, - if (docTopic != null) resources.getString(R.string.app_doc_url_format, docTopic) else null) + App.alert( + R.string.notification_error_title_unable_to_start_tinc, + msg, + if (docTopic != null) resources.getString(R.string.app_doc_url_format, docTopic) else null, + configDir, + proposeLogs, + ) } private fun broadcastEvent(event: String) { diff --git a/app/src/main/java/org/pacien/tincapp/storageprovider/BrowseFilesIntents.kt b/app/src/main/java/org/pacien/tincapp/storageprovider/BrowseFilesIntents.kt index 59a4246..e9fabce 100644 --- a/app/src/main/java/org/pacien/tincapp/storageprovider/BrowseFilesIntents.kt +++ b/app/src/main/java/org/pacien/tincapp/storageprovider/BrowseFilesIntents.kt @@ -24,6 +24,12 @@ import android.net.Uri import android.provider.DocumentsContract.Document object BrowseFilesIntents { + fun networkConfigDirDocId(networkName: String?): String = + when (networkName) { + null -> FilesDocumentsProvider.VIRTUAL_ROOT_NETWORKS + else -> FilesDocumentsProvider.VIRTUAL_ROOT_NETWORKS + "/" + networkName + } + fun openDocumentTree(context: Context, documentId: String) = openDocumentTree(context, FilesDocumentsProvider.documentUri(documentId)) diff --git a/app/src/main/res/layout/start_error_notification.xml b/app/src/main/res/layout/start_error_notification.xml index 543cd8c..4499357 100644 --- a/app/src/main/res/layout/start_error_notification.xml +++ b/app/src/main/res/layout/start_error_notification.xml @@ -33,6 +33,14 @@ name="openManualAction" type="kotlin.jvm.functions.Function0<kotlin.Unit>"/> + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ded24f..a678832 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,6 +61,8 @@ Errors Open manual + Open configuration directory + Open logs directory Could not start tinc Could not read private tinc keys:\n%1$s Could not read network interface configuration:\n%1$s -- cgit v1.2.3