From 9d8846e105904b31478ed19d3a34c0d62708abcf Mon Sep 17 00:00:00 2001 From: pacien Date: Tue, 7 Aug 2018 01:08:22 +0200 Subject: Revert "Rename source directory" This reverts commit dbba24e --- .../main/java/org/pacien/tincapp/context/App.kt | 85 ++++++++++++++++++++++ .../java/org/pacien/tincapp/context/AppInfo.kt | 47 ++++++++++++ .../java/org/pacien/tincapp/context/AppLogger.kt | 63 ++++++++++++++++ .../tincapp/context/AppNotificationManager.kt | 84 +++++++++++++++++++++ .../java/org/pacien/tincapp/context/AppPaths.kt | 71 ++++++++++++++++++ .../org/pacien/tincapp/context/CrashRecorder.kt | 48 ++++++++++++ 6 files changed, 398 insertions(+) create mode 100644 app/src/main/java/org/pacien/tincapp/context/App.kt create mode 100644 app/src/main/java/org/pacien/tincapp/context/AppInfo.kt create mode 100644 app/src/main/java/org/pacien/tincapp/context/AppLogger.kt create mode 100644 app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt create mode 100644 app/src/main/java/org/pacien/tincapp/context/AppPaths.kt create mode 100644 app/src/main/java/org/pacien/tincapp/context/CrashRecorder.kt (limited to 'app/src/main/java/org/pacien/tincapp/context') diff --git a/app/src/main/java/org/pacien/tincapp/context/App.kt b/app/src/main/java/org/pacien/tincapp/context/App.kt new file mode 100644 index 0000000..359cd23 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/context/App.kt @@ -0,0 +1,85 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.context + +import android.app.Application +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Handler +import android.support.annotation.StringRes +import org.pacien.tincapp.BuildConfig +import org.pacien.tincapp.R +import org.slf4j.LoggerFactory +import java.io.File + +/** + * @author pacien + */ +class App : Application() { + override fun onCreate() { + super.onCreate() + appContext = applicationContext + handler = Handler() + AppLogger.configure() + setupCrashHandler() + + val logger = LoggerFactory.getLogger(this.javaClass) + logger.info("Starting tinc app {} ({} build), running on {} ({})", + BuildConfig.VERSION_NAME, BuildConfig.BUILD_TYPE, Build.VERSION.CODENAME, Build.VERSION.RELEASE) + } + + private fun setupCrashHandler() { + val logger = LoggerFactory.getLogger(this.javaClass) + val systemCrashHandler = Thread.getDefaultUncaughtExceptionHandler() + val crashRecorder = CrashRecorder(logger, systemCrashHandler) + Thread.setDefaultUncaughtExceptionHandler(crashRecorder) + } + + companion object { + private var appContext: Context? = null + private var handler: Handler? = null + + val notificationManager: AppNotificationManager by lazy { AppNotificationManager(appContext!!) } + + fun getContext() = appContext!! + fun getResources() = getContext().resources!! + + fun alert(@StringRes title: Int, msg: String, manualLink: String? = null) = + notificationManager.notifyError(appContext!!.getString(title), msg, manualLink) + + fun openURL(url: String) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + val chooser = Intent.createChooser(intent, getResources().getString(R.string.action_open_web_page)) + appContext?.startActivity(chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) + } + + fun sendMail(recipient: String, subject: String, body: String? = null, attachment: File? = null) { + val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")) + .putExtra(Intent.EXTRA_EMAIL, arrayOf(recipient)) + .putExtra(Intent.EXTRA_SUBJECT, subject) + .apply { if (body != null) putExtra(Intent.EXTRA_TEXT, body) } + .apply { if (attachment != null) putExtra(Intent.EXTRA_STREAM, Uri.fromFile(attachment)) } + + val chooser = Intent.createChooser(intent, getResources().getString(R.string.action_send_email)) + appContext?.startActivity(chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) + } + } +} diff --git a/app/src/main/java/org/pacien/tincapp/context/AppInfo.kt b/app/src/main/java/org/pacien/tincapp/context/AppInfo.kt new file mode 100644 index 0000000..e0d49f1 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/context/AppInfo.kt @@ -0,0 +1,47 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.context + +import android.os.Build +import org.pacien.tincapp.BuildConfig +import org.pacien.tincapp.R + +/** + * @author pacien + */ +object AppInfo { + private fun appVersion(): String = App.getResources().getString( + R.string.info_version_format, + BuildConfig.VERSION_NAME, + BuildConfig.BUILD_TYPE) + + private fun androidVersion(): String = App.getResources().getString( + R.string.info_running_on_format, + Build.VERSION.CODENAME, + Build.VERSION.RELEASE) + + private fun supportedABIs(): String = App.getResources().getString( + R.string.info_supported_abis_format, + Build.SUPPORTED_ABIS.joinToString(",")) + + fun all(): String = listOf( + appVersion(), + androidVersion(), + supportedABIs()).joinToString("\n") +} diff --git a/app/src/main/java/org/pacien/tincapp/context/AppLogger.kt b/app/src/main/java/org/pacien/tincapp/context/AppLogger.kt new file mode 100644 index 0000000..3c1be44 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/context/AppLogger.kt @@ -0,0 +1,63 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.context + +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.LoggerContext +import ch.qos.logback.classic.android.LogcatAppender +import ch.qos.logback.classic.encoder.PatternLayoutEncoder +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.Context +import ch.qos.logback.core.FileAppender +import org.slf4j.LoggerFactory + +/** + * @author pacien + */ +object AppLogger { + private const val LOGCAT_PATTERN = "[%thread] %msg%n%rEx" + private const val LOGFILE_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%rEx" + + fun configure() { + (LoggerFactory.getILoggerFactory() as LoggerContext) + .apply { reset() } + .let { loggerContext -> + (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger) + .apply { + addAppender(LogcatAppender() + .apply { context = loggerContext } + .apply { encoder = patternEncoder(loggerContext, LOGCAT_PATTERN) } + .apply { start() }) + } + .apply { + addAppender(FileAppender() + .apply { context = loggerContext } + .apply { encoder = patternEncoder(loggerContext, LOGFILE_PATTERN) } + .apply { file = AppPaths.appLogFile().absolutePath } + .apply { start() }) + } + } + } + + private fun patternEncoder(ctx: Context, pat: String) = + PatternLayoutEncoder() + .apply { context = ctx } + .apply { pattern = pat } + .apply { start() } +} diff --git a/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt b/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt new file mode 100644 index 0000000..d543210 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt @@ -0,0 +1,84 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.context + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.support.annotation.RequiresApi +import android.support.v4.app.NotificationCompat +import android.support.v4.app.NotificationManagerCompat +import org.pacien.tincapp.R + +/** + * @author pacien + */ +class AppNotificationManager(private val context: Context) { + companion object { + private const val CHANNEL_ID = "org.pacien.tincapp.notification.channels.error" + private const val ERROR_NOTIFICATION_ID = 0 + } + + init { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) registerChannel() + } + + fun notifyError(title: String, message: String, manualLink: String? = null) { + val notification = NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_warning_primary_24dp) + .setContentTitle(title) + .setContentText(message) + .setStyle(NotificationCompat.BigTextStyle().bigText(message)) + .setHighPriority() + .setAutoCancel(true) + .apply { if (manualLink != null) setManualLink(manualLink) } + .build() + + NotificationManagerCompat.from(context) + .notify(ERROR_NOTIFICATION_ID, notification) + } + + fun dismissAll() { + NotificationManagerCompat.from(context).cancelAll() + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun registerChannel() { + val name = context.getString(R.string.notification_channel_error_name) + val importance = NotificationManager.IMPORTANCE_HIGH + val channel = NotificationChannel(CHANNEL_ID, name, importance) + val notificationManager = context.getSystemService(NotificationManager::class.java) + notificationManager.createNotificationChannel(channel) + } + + private fun NotificationCompat.Builder.setHighPriority() = apply { + priority = NotificationCompat.PRIORITY_MAX + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) setDefaults(NotificationCompat.DEFAULT_SOUND) // force heads-up notification + } + + private fun NotificationCompat.Builder.setManualLink(manualLink: String) = apply { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(manualLink)) + val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) + addAction(R.drawable.ic_help_primary_24dp, context.getString(R.string.action_open_manual), pendingIntent) + } +} diff --git a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt new file mode 100644 index 0000000..0b85565 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt @@ -0,0 +1,71 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.context + +import android.os.Environment +import java.io.File +import java.io.FileNotFoundException + +/** + * @author pacien + * + * @implNote Logs and PID files are stored in the cache directory for easy clean up. + */ +object AppPaths { + private const val TINCD_BIN = "libtincd.so" + private const val TINC_BIN = "libtinc.so" + + private const val APPLOG_FILE = "tincapp.log" + private const val CRASHFLAG_FILE = "crash.flag" + private const val LOGFILE_FORMAT = "tinc.%s.log" + private const val PIDFILE_FORMAT = "tinc.%s.pid" + + private const val NET_CONF_FILE = "network.conf" + private const val NET_TINC_CONF_FILE = "tinc.conf" + private const val NET_HOSTS_DIR = "hosts" + private const val NET_INVITATION_FILE = "invitation-data" + private const val NET_DEFAULT_ED25519_PRIVATE_KEY_FILE = "ed25519_key.priv" + private const val NET_DEFAULT_RSA_PRIVATE_KEY_FILE = "rsa_key.priv" + + fun storageAvailable() = + Environment.getExternalStorageState().let { it == Environment.MEDIA_MOUNTED && it != Environment.MEDIA_MOUNTED_READ_ONLY } + + private fun internalCacheDir() = App.getContext().cacheDir!! + fun cacheDir() = App.getContext().externalCacheDir!! + fun confDir() = App.getContext().getExternalFilesDir(null)!! + private fun binDir() = File(App.getContext().applicationInfo.nativeLibraryDir) + + fun confDir(netName: String) = File(confDir(), netName) + fun hostsDir(netName: String) = File(confDir(netName), NET_HOSTS_DIR) + fun netConfFile(netName: String) = File(confDir(netName), NET_CONF_FILE) + fun tincConfFile(netName: String) = File(confDir(netName), NET_TINC_CONF_FILE) + fun invitationFile(netName: String) = File(confDir(netName), NET_INVITATION_FILE) + fun logFile(netName: String) = File(cacheDir(), String.format(LOGFILE_FORMAT, netName)) + fun pidFile(netName: String) = File(App.getContext().cacheDir, String.format(PIDFILE_FORMAT, netName)) + fun appLogFile() = File(cacheDir(), APPLOG_FILE) + fun crashFlagFile() = File(internalCacheDir(), CRASHFLAG_FILE) + + fun existing(f: File) = f.apply { if (!exists()) throw FileNotFoundException(f.absolutePath) } + + fun defaultEd25519PrivateKeyFile(netName: String) = File(confDir(netName), NET_DEFAULT_ED25519_PRIVATE_KEY_FILE) + fun defaultRsaPrivateKeyFile(netName: String) = File(confDir(netName), NET_DEFAULT_RSA_PRIVATE_KEY_FILE) + + fun tincd() = File(binDir(), TINCD_BIN) + fun tinc() = File(binDir(), TINC_BIN) +} diff --git a/app/src/main/java/org/pacien/tincapp/context/CrashRecorder.kt b/app/src/main/java/org/pacien/tincapp/context/CrashRecorder.kt new file mode 100644 index 0000000..d0310c2 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/context/CrashRecorder.kt @@ -0,0 +1,48 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.context + +import org.slf4j.Logger + +/** + * @author pacien + */ +class CrashRecorder(private val logger: Logger, + private val upstreamCrashHandler: Thread.UncaughtExceptionHandler) : Thread.UncaughtExceptionHandler { + + companion object { + private val flagFile = AppPaths.crashFlagFile() + + fun hasPreviouslyCrashed() = flagFile.exists() + + fun flagCrash() { + flagFile.apply { if (!exists()) createNewFile() } + } + + fun dismissPreviousCrash() { + flagFile.delete() + } + } + + override fun uncaughtException(thread: Thread, throwable: Throwable) { + logger.error("Fatal application error.", throwable) + flagCrash() + upstreamCrashHandler.uncaughtException(thread, throwable) + } +} -- cgit v1.2.3