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(-)
(limited to 'app/src')
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