From 0039db860299a0470e7596f7b6157896aea1c935 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Fri, 9 Oct 2020 11:54:07 -0400 Subject: [PATCH] Fix: Developer logs feature on older phones. This also happens to address the security finding in issue #121 by putting logs directly into a file rather than scraping them from the device logs. --- .../cash/z/ecc/android/di/module/AppModule.kt | 3 +- .../java/cash/z/ecc/android/ext/Extensions.kt | 9 ++++ .../ecc/android/ui/profile/ProfileFragment.kt | 43 ++++++------------- .../z/ecc/android/ui/util/DebugFileTwig.kt | 21 +++++++++ 4 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/cash/z/ecc/android/ui/util/DebugFileTwig.kt diff --git a/app/src/main/java/cash/z/ecc/android/di/module/AppModule.kt b/app/src/main/java/cash/z/ecc/android/di/module/AppModule.kt index f1d4259..2d70e81 100644 --- a/app/src/main/java/cash/z/ecc/android/di/module/AppModule.kt +++ b/app/src/main/java/cash/z/ecc/android/di/module/AppModule.kt @@ -10,6 +10,7 @@ import cash.z.ecc.android.lockbox.LockBox import cash.z.ecc.android.sdk.ext.SilentTwig import cash.z.ecc.android.sdk.ext.TroubleshootingTwig import cash.z.ecc.android.sdk.ext.Twig +import cash.z.ecc.android.ui.util.DebugFileTwig import dagger.Module import dagger.Provides import dagger.multibindings.IntoSet @@ -53,7 +54,7 @@ class AppModule { ): FeedbackCoordinator { return prefs.getBoolean(Const.Pref.FEEDBACK_ENABLED).let { isEnabled -> // observe nothing unless feedback is enabled - Twig.plant(if (isEnabled) TroubleshootingTwig() else SilentTwig()) + Twig.plant(if (isEnabled) DebugFileTwig() else SilentTwig()) FeedbackCoordinator(feedback, if (isEnabled) defaultObservers else setOf()) } } diff --git a/app/src/main/java/cash/z/ecc/android/ext/Extensions.kt b/app/src/main/java/cash/z/ecc/android/ext/Extensions.kt index d77ae71..a967aa7 100644 --- a/app/src/main/java/cash/z/ecc/android/ext/Extensions.kt +++ b/app/src/main/java/cash/z/ecc/android/ext/Extensions.kt @@ -3,6 +3,9 @@ package cash.z.ecc.android.ext import android.content.Context import android.os.Build import androidx.fragment.app.Fragment +import cash.z.ecc.android.sdk.ext.Bush +import cash.z.ecc.android.sdk.ext.CompositeTwig +import cash.z.ecc.android.sdk.ext.Twig import cash.z.ecc.android.sdk.ext.twig import java.util.* @@ -26,4 +29,10 @@ inline fun Context.locale(): Locale { //noinspection deprecation resources.configuration.locale } +} + +// TODO: add this to the SDK and if the trunk is a CompositeTwig, search through there before returning null +inline fun Twig.find(): T? { + return if (Bush.trunk::class.java.isAssignableFrom(T::class.java)) Bush.trunk as T + else null } \ No newline at end of file diff --git a/app/src/main/java/cash/z/ecc/android/ui/profile/ProfileFragment.kt b/app/src/main/java/cash/z/ecc/android/ui/profile/ProfileFragment.kt index 007166f..c0c3bf6 100644 --- a/app/src/main/java/cash/z/ecc/android/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/cash/z/ecc/android/ui/profile/ProfileFragment.kt @@ -11,6 +11,7 @@ import cash.z.ecc.android.R import cash.z.ecc.android.ZcashWalletApp import cash.z.ecc.android.databinding.FragmentProfileBinding import cash.z.ecc.android.di.viewmodel.viewModel +import cash.z.ecc.android.ext.find import cash.z.ecc.android.ext.onClick import cash.z.ecc.android.ext.onClickNavBack import cash.z.ecc.android.ext.onClickNavTo @@ -18,14 +19,12 @@ import cash.z.ecc.android.feedback.FeedbackFile import cash.z.ecc.android.feedback.Report import cash.z.ecc.android.feedback.Report.Funnel.UserFeedback import cash.z.ecc.android.feedback.Report.Tap.* -import cash.z.ecc.android.ui.base.BaseFragment +import cash.z.ecc.android.sdk.ext.Bush import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress -import cash.z.ecc.android.sdk.ext.twig +import cash.z.ecc.android.ui.base.BaseFragment +import cash.z.ecc.android.ui.util.DebugFileTwig import kotlinx.coroutines.launch -import okio.Okio import java.io.File -import java.io.IOException -import java.lang.IllegalArgumentException class ProfileFragment : BaseFragment() { @@ -70,7 +69,13 @@ class ProfileFragment : BaseFragment() { } private fun onViewDevLogs() { - shareFile(writeLogcat()) + developerLogFile().let { + if (it == null) { + mainActivity?.showSnackbar("Error: No developer log found!") + } else { + shareFile(it) + } + } } private fun shareFiles(vararg files: File?) { @@ -102,29 +107,7 @@ class ProfileFragment : BaseFragment() { return mainActivity?.feedbackCoordinator?.findObserver()?.file } - private fun loadLogFileAsText(): String? { - val feedbackFile: File = userLogFile() ?: return null - Okio.buffer(Okio.source(feedbackFile)).use { - return it.readUtf8() - } - } - - private fun writeLogcat(): File? { - try { - // Note: the /logs directory has been configured as a file provider under @xml/file_paths which allows the temporary sharing of this file - val outputFile = File("${ZcashWalletApp.instance.filesDir}/logs", "developer_log.txt").also { it.parentFile.mkdirs() } - if (!outputFile.parentFile.isDirectory) { - // addresses security finding in issue #121 - throw IllegalArgumentException("Invalid path: ${outputFile.parentFile}. Verify" + - " that the default files directory is not being manipulated.") - } - val cmd = arrayOf("/bin/sh", "-c", "logcat -v time -d | grep '@TWIG' > '${outputFile.absolutePath}'") - Runtime.getRuntime().exec(cmd) - return outputFile - } catch (e: IOException) { - e.printStackTrace() - twig("Failed to create log") - } - return null + private fun developerLogFile(): File? { + return Bush.trunk.find()?.file } } \ No newline at end of file diff --git a/app/src/main/java/cash/z/ecc/android/ui/util/DebugFileTwig.kt b/app/src/main/java/cash/z/ecc/android/ui/util/DebugFileTwig.kt new file mode 100644 index 0000000..5ee0534 --- /dev/null +++ b/app/src/main/java/cash/z/ecc/android/ui/util/DebugFileTwig.kt @@ -0,0 +1,21 @@ +package cash.z.ecc.android.ui.util + +import cash.z.ecc.android.ZcashWalletApp +import cash.z.ecc.android.sdk.ext.TroubleshootingTwig +import okio.Okio +import java.io.File + +class DebugFileTwig(fileName: String = "developer_log.txt") : TroubleshootingTwig() { + val file = File("${ZcashWalletApp.instance.filesDir}/logs", fileName) + + override fun twig(logMessage: String) { + super.twig(logMessage) + appendToFile(formatter(logMessage)) + } + + private fun appendToFile(message: String) { + Okio.buffer(Okio.appendingSink(file)).use { + it.writeUtf8("$message\n") + } + } +} \ No newline at end of file