Introduce realtime log viewer

This contains a share button and a save button, the former using a
custom content provider.

Co-authored-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Harsh Shandilya
2020-03-26 00:49:01 -06:00
committed by Jason A. Donenfeld
co-authored by Jason A. Donenfeld
parent 6f973afa36
commit 63a395125a
20 changed files with 449 additions and 117 deletions
@@ -1,101 +0,0 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.AttributeSet
import android.util.Log
import androidx.preference.Preference
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.util.DownloadsFileSaver
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.FragmentUtils
import java.io.BufferedReader
import java.io.InputStreamReader
/**
* Preference implementing a button that asynchronously exports logs.
*/
class LogExporterPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
private var exportedFilePath: String? = null
private fun exportLog() {
Application.getAsyncWorker().supplyAsync {
val outputFile = DownloadsFileSaver.save(context, "wireguard-log.txt", "text/plain", true)
try {
val process = Runtime.getRuntime().exec(arrayOf(
"logcat", "-b", "all", "-d", "-v", "threadtime", "*:V"))
BufferedReader(InputStreamReader(process.inputStream)).use { stdout ->
BufferedReader(InputStreamReader(process.errorStream)).use { stderr ->
while (true) {
val line = stdout.readLine() ?: break
outputFile.outputStream.write(line.toByteArray())
outputFile.outputStream.write('\n'.toInt())
}
outputFile.outputStream.close()
if (process.waitFor() != 0) {
val errors = StringBuilder()
errors.append(R.string.logcat_error)
while (true) {
val line = stderr.readLine() ?: break
errors.append(line)
}
throw Exception(errors.toString())
}
}
}
} catch (e: Exception) {
outputFile.delete()
throw e
}
outputFile.fileName
}.whenComplete(this::exportLogComplete)
}
private fun exportLogComplete(filePath: String, throwable: Throwable?) {
if (throwable != null) {
val error = ErrorMessages.get(throwable)
val message = context.getString(R.string.log_export_error, error)
Log.e(TAG, message, throwable)
Snackbar.make(
FragmentUtils.getPrefActivity(this).findViewById(android.R.id.content),
message, Snackbar.LENGTH_LONG).show()
isEnabled = true
} else {
exportedFilePath = filePath
notifyChanged()
}
}
override fun getSummary() = if (exportedFilePath == null)
context.getString(R.string.log_export_summary)
else
context.getString(R.string.log_export_success, exportedFilePath)
override fun getTitle() = context.getString(R.string.log_export_title)
override fun onClick() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
FragmentUtils.getPrefActivity(this)
.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults ->
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isEnabled = false
exportLog()
}
}
} else {
isEnabled = false
exportLog()
}
}
companion object {
private val TAG = "WireGuard/" + LogExporterPreference::class.java.simpleName
}
}