summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2018-06-02 18:19:42 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2018-06-02 18:35:09 +0200
commitc3246060f53c0b751975278da98ad81fc982c6bc (patch)
tree7ea0d6c7002d2d15a1cb379e7a32c85de07d0dc3
parentb7e025e3819577efb92604fba71e04092c946f72 (diff)
Preferences: add log exporter
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--app/src/main/java/com/wireguard/android/preference/LogExporterPreference.java123
-rw-r--r--app/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java19
-rw-r--r--app/src/main/res/values/strings.xml10
-rw-r--r--app/src/main/res/xml/preferences.xml3
4 files changed, 137 insertions, 18 deletions
diff --git a/app/src/main/java/com/wireguard/android/preference/LogExporterPreference.java b/app/src/main/java/com/wireguard/android/preference/LogExporterPreference.java
new file mode 100644
index 00000000..d73c41bc
--- /dev/null
+++ b/app/src/main/java/com/wireguard/android/preference/LogExporterPreference.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2018 Samuel Holland <samuel@sholland.org>
+ * Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+package com.wireguard.android.preference;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.support.design.widget.Snackbar;
+import android.support.v7.preference.Preference;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+
+import com.wireguard.android.Application;
+import com.wireguard.android.R;
+import com.wireguard.android.activity.SettingsActivity;
+import com.wireguard.android.util.ExceptionLoggers;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * Preference implementing a button that asynchronously exports logs.
+ */
+
+public class LogExporterPreference extends Preference {
+ private static final String TAG = "WireGuard/" + LogExporterPreference.class.getSimpleName();
+
+ private String exportedFilePath;
+
+ public LogExporterPreference(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ private static SettingsActivity getPrefActivity(final Preference preference) {
+ final Context context = preference.getContext();
+ if (context instanceof ContextThemeWrapper) {
+ if (((ContextThemeWrapper) context).getBaseContext() instanceof SettingsActivity) {
+ return ((SettingsActivity) ((ContextThemeWrapper) context).getBaseContext());
+ }
+ }
+ return null;
+ }
+
+ private void exportLog() {
+ Application.getComponent().getAsyncWorker().supplyAsync(() -> {
+ final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ final File file = new File(path, "wireguard-log.txt");
+ if (!path.isDirectory() && !path.mkdirs())
+ throw new IOException("Cannot create output directory");
+
+ /* We would like to simply run `builder.redirectOutput(file);`, but this is API 26.
+ * Instead we have to do this dance, since logcat appends.
+ */
+ new FileOutputStream(file).close();
+
+ try {
+ final Process process = Runtime.getRuntime().exec(new String[]{
+ "logcat", "-b", "all", "-d", "-v", "threadtime", "--pid", Integer.toString(android.os.Process.myPid()), "-f", file.getAbsolutePath(), "*:V"});
+ if (process.waitFor() != 0) {
+ try (final BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
+ final StringBuilder errors = new StringBuilder();
+ errors.append("Unable to run logcat: ");
+ String line;
+ while ((line = reader.readLine()) != null)
+ errors.append(line);
+ throw new Exception(errors.toString());
+ }
+ }
+ } catch (Exception e) {
+ file.delete();
+ throw e;
+ }
+ return file.getAbsolutePath();
+ }).whenComplete(this::exportLogComplete);
+ }
+
+ private void exportLogComplete(final String filePath, final Throwable throwable) {
+ if (throwable != null) {
+ final String error = ExceptionLoggers.unwrapMessage(throwable);
+ final String message = getContext().getString(R.string.log_export_error, error);
+ Log.e(TAG, message, throwable);
+ Snackbar.make(
+ getPrefActivity(this).findViewById(android.R.id.content),
+ message, Snackbar.LENGTH_LONG).show();
+ } else {
+ exportedFilePath = filePath;
+ setEnabled(false);
+ notifyChanged();
+ }
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return exportedFilePath == null ?
+ getContext().getString(R.string.log_export_summary) :
+ getContext().getString(R.string.log_export_success, exportedFilePath);
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return getContext().getString(R.string.log_exporter_title);
+ }
+
+ @Override
+ protected void onClick() {
+ getPrefActivity(this).ensurePermissions(
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ (permissions, granted) -> {
+ if (granted.length > 0 && granted[0] == PackageManager.PERMISSION_GRANTED)
+ exportLog();
+ });
+ }
+
+}
diff --git a/app/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java b/app/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java
index 8013279c..70357e44 100644
--- a/app/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java
+++ b/app/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java
@@ -17,12 +17,9 @@ import android.util.Log;
import android.view.ContextThemeWrapper;
import com.wireguard.android.Application;
-import com.wireguard.android.Application.ApplicationComponent;
import com.wireguard.android.R;
import com.wireguard.android.activity.SettingsActivity;
import com.wireguard.android.model.Tunnel;
-import com.wireguard.android.model.TunnelManager;
-import com.wireguard.android.util.AsyncWorker;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.config.Config;
@@ -44,16 +41,10 @@ import java9.util.concurrent.CompletableFuture;
public class ZipExporterPreference extends Preference {
private static final String TAG = "WireGuard/" + ZipExporterPreference.class.getSimpleName();
- private final AsyncWorker asyncWorker;
- private final TunnelManager tunnelManager;
private String exportedFilePath;
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
public ZipExporterPreference(final Context context, final AttributeSet attrs) {
super(context, attrs);
- final ApplicationComponent applicationComponent = Application.getComponent();
- asyncWorker = applicationComponent.getAsyncWorker();
- tunnelManager = applicationComponent.getTunnelManager();
}
private static SettingsActivity getPrefActivity(final Preference preference) {
@@ -67,7 +58,7 @@ public class ZipExporterPreference extends Preference {
}
private void exportZip() {
- final List<Tunnel> tunnels = new ArrayList<>(tunnelManager.getTunnels());
+ final List<Tunnel> tunnels = new ArrayList<>(Application.getComponent().getTunnelManager().getTunnels());
final List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size());
for (final Tunnel tunnel : tunnels)
futureConfigs.add(tunnel.getConfigAsync().toCompletableFuture());
@@ -76,7 +67,7 @@ public class ZipExporterPreference extends Preference {
return;
}
CompletableFuture.allOf(futureConfigs.toArray(new CompletableFuture[futureConfigs.size()]))
- .whenComplete((ignored1, exception) -> asyncWorker.supplyAsync(() -> {
+ .whenComplete((ignored1, exception) -> Application.getComponent().getAsyncWorker().supplyAsync(() -> {
if (exception != null)
throw exception;
final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
@@ -103,7 +94,7 @@ public class ZipExporterPreference extends Preference {
private void exportZipComplete(final String filePath, final Throwable throwable) {
if (throwable != null) {
final String error = ExceptionLoggers.unwrapMessage(throwable);
- final String message = getContext().getString(R.string.export_error, error);
+ final String message = getContext().getString(R.string.zip_export_error, error);
Log.e(TAG, message, throwable);
Snackbar.make(
getPrefActivity(this).findViewById(android.R.id.content),
@@ -118,8 +109,8 @@ public class ZipExporterPreference extends Preference {
@Override
public CharSequence getSummary() {
return exportedFilePath == null ?
- getContext().getString(R.string.export_summary) :
- getContext().getString(R.string.export_success, exportedFilePath);
+ getContext().getString(R.string.zip_export_summary) :
+ getContext().getString(R.string.zip_export_success, exportedFilePath);
}
@Override
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 91e3fb0a..192d444c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -39,9 +39,6 @@
<string name="error_down">Error bringing down tunnel: %s</string>
<string name="error_root">Please obtain root access and try again</string>
<string name="error_up">Error bringing up tunnel: %s</string>
- <string name="export_error">Unable to export tunnels: %s</string>
- <string name="export_success">Saved to %s</string>
- <string name="export_summary">Zip file will be saved to downloads folder</string>
<string name="generate">Generate</string>
<string name="hint_automatic">(auto)</string>
<string name="hint_generated">(generated)</string>
@@ -51,6 +48,10 @@
<string name="import_success">Imported “%s”</string>
<string name="interface_title">Interface</string>
<string name="listen_port">Listen port</string>
+ <string name="log_exporter_title">Export log file</string>
+ <string name="log_export_error">Unable to export log: %s</string>
+ <string name="log_export_success">Saved to %s</string>
+ <string name="log_export_summary">Log file will be saved to downloads folder</string>
<string name="mtu">MTU</string>
<string name="name">Name</string>
<string name="peer">Peer</string>
@@ -79,4 +80,7 @@
<string name="version_kernel_unknown_summary">Using unknown kernel module implementation</string>
<string name="version_userspace_summary">Using Go userspace implementation v%s</string>
<string name="zip_exporter_title">Export tunnels to zip file</string>
+ <string name="zip_export_error">Unable to export tunnels: %s</string>
+ <string name="zip_export_success">Saved to %s</string>
+ <string name="zip_export_summary">Zip file will be saved to downloads folder</string>
</resources>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 78483f6b..680e8137 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -6,7 +6,8 @@
android:summary="@string/restore_on_boot_summary"
android:title="@string/restore_on_boot_title" />
<com.wireguard.android.preference.ToolsInstallerPreference android:key="tools_installer" />
- <com.wireguard.android.preference.ZipExporterPreference android:key="zip_exporter" />
+ <com.wireguard.android.preference.ZipExporterPreference />
+ <com.wireguard.android.preference.LogExporterPreference />
<CheckBoxPreference
android:defaultValue="false"
android:key="dark_theme"