aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/app/src/main/java/com/wireguard/android/util
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/com/wireguard/android/util')
-rw-r--r--app/src/main/java/com/wireguard/android/util/AsyncWorker.java63
-rw-r--r--app/src/main/java/com/wireguard/android/util/ClipboardUtils.java37
-rw-r--r--app/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java98
-rw-r--r--app/src/main/java/com/wireguard/android/util/ErrorMessages.java130
-rw-r--r--app/src/main/java/com/wireguard/android/util/ExceptionLoggers.java36
-rw-r--r--app/src/main/java/com/wireguard/android/util/FragmentUtils.java27
-rw-r--r--app/src/main/java/com/wireguard/android/util/ModuleLoader.java183
-rw-r--r--app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java109
-rw-r--r--app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java19
-rw-r--r--app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java198
-rw-r--r--app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java17
-rw-r--r--app/src/main/java/com/wireguard/android/util/RootShell.java199
-rw-r--r--app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java92
-rw-r--r--app/src/main/java/com/wireguard/android/util/ToolsInstaller.java180
14 files changed, 0 insertions, 1388 deletions
diff --git a/app/src/main/java/com/wireguard/android/util/AsyncWorker.java b/app/src/main/java/com/wireguard/android/util/AsyncWorker.java
deleted file mode 100644
index 1d041851..00000000
--- a/app/src/main/java/com/wireguard/android/util/AsyncWorker.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.os.Handler;
-
-import java.util.concurrent.Executor;
-
-import java9.util.concurrent.CompletableFuture;
-import java9.util.concurrent.CompletionStage;
-
-/**
- * Helper class for running asynchronous tasks and ensuring they are completed on the main thread.
- */
-
-public class AsyncWorker {
- private final Executor executor;
- private final Handler handler;
-
- public AsyncWorker(final Executor executor, final Handler handler) {
- this.executor = executor;
- this.handler = handler;
- }
-
- public CompletionStage<Void> runAsync(final AsyncRunnable<?> runnable) {
- final CompletableFuture<Void> future = new CompletableFuture<>();
- executor.execute(() -> {
- try {
- runnable.run();
- handler.post(() -> future.complete(null));
- } catch (final Throwable t) {
- handler.post(() -> future.completeExceptionally(t));
- }
- });
- return future;
- }
-
- public <T> CompletionStage<T> supplyAsync(final AsyncSupplier<T, ?> supplier) {
- final CompletableFuture<T> future = new CompletableFuture<>();
- executor.execute(() -> {
- try {
- final T result = supplier.get();
- handler.post(() -> future.complete(result));
- } catch (final Throwable t) {
- handler.post(() -> future.completeExceptionally(t));
- }
- });
- return future;
- }
-
- @FunctionalInterface
- public interface AsyncRunnable<E extends Throwable> {
- void run() throws E;
- }
-
- @FunctionalInterface
- public interface AsyncSupplier<T, E extends Throwable> {
- T get() throws E;
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ClipboardUtils.java b/app/src/main/java/com/wireguard/android/util/ClipboardUtils.java
deleted file mode 100644
index 0df5e96a..00000000
--- a/app/src/main/java/com/wireguard/android/util/ClipboardUtils.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import com.google.android.material.snackbar.Snackbar;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * Standalone utilities for interacting with the system clipboard.
- */
-
-public final class ClipboardUtils {
- private ClipboardUtils() {
- // Prevent instantiation
- }
-
- public static void copyTextView(final View view) {
- if (!(view instanceof TextView))
- return;
- final CharSequence text = ((TextView) view).getText();
- if (text == null || text.length() == 0)
- return;
- final Object service = view.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
- if (!(service instanceof ClipboardManager))
- return;
- final CharSequence description = view.getContentDescription();
- ((ClipboardManager) service).setPrimaryClip(ClipData.newPlainText(description, text));
- Snackbar.make(view, description + " copied to clipboard", Snackbar.LENGTH_LONG).show();
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java b/app/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java
deleted file mode 100644
index 7db46fa9..00000000
--- a/app/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright © 2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Environment;
-import android.provider.MediaStore;
-import android.provider.MediaStore.MediaColumns;
-
-import com.wireguard.android.R;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-public class DownloadsFileSaver {
-
- public static class DownloadsFile {
- private Context context;
- private OutputStream outputStream;
- private String fileName;
- private Uri uri;
-
- private DownloadsFile(final Context context, final OutputStream outputStream, final String fileName, final Uri uri) {
- this.context = context;
- this.outputStream = outputStream;
- this.fileName = fileName;
- this.uri = uri;
- }
-
- public OutputStream getOutputStream() { return outputStream; }
- public String getFileName() { return fileName; }
-
- public void delete() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- context.getContentResolver().delete(uri, null, null);
- else
- new File(fileName).delete();
- }
- }
-
- public static DownloadsFile save(final Context context, final String name, final String mimeType, final boolean overwriteExisting) throws Exception {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- final ContentResolver contentResolver = context.getContentResolver();
- if (overwriteExisting)
- contentResolver.delete(MediaStore.Downloads.EXTERNAL_CONTENT_URI, String.format("%s = ?", MediaColumns.DISPLAY_NAME), new String[]{name});
- final ContentValues contentValues = new ContentValues();
- contentValues.put(MediaColumns.DISPLAY_NAME, name);
- contentValues.put(MediaColumns.MIME_TYPE, mimeType);
- final Uri contentUri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
- if (contentUri == null)
- throw new IOException(context.getString(R.string.create_downloads_file_error));
- final OutputStream contentStream = contentResolver.openOutputStream(contentUri);
- if (contentStream == null)
- throw new IOException(context.getString(R.string.create_downloads_file_error));
- @SuppressWarnings("deprecation")
- Cursor cursor = contentResolver.query(contentUri, new String[]{MediaColumns.DATA}, null, null, null);
- String path = null;
- if (cursor != null) {
- try {
- if (cursor.moveToFirst())
- path = cursor.getString(0);
- } finally {
- cursor.close();
- }
- }
- if (path == null) {
- path = "Download/";
- cursor = contentResolver.query(contentUri, new String[]{MediaColumns.DISPLAY_NAME}, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst())
- path += cursor.getString(0);
- } finally {
- cursor.close();
- }
- }
- }
- return new DownloadsFile(context, contentStream, path, contentUri);
- } else {
- @SuppressWarnings("deprecation")
- final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
- final File file = new File(path, name);
- if (!path.isDirectory() && !path.mkdirs())
- throw new IOException(context.getString(R.string.create_output_dir_error));
- return new DownloadsFile(context, new FileOutputStream(file), file.getAbsolutePath(), null);
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ErrorMessages.java b/app/src/main/java/com/wireguard/android/util/ErrorMessages.java
deleted file mode 100644
index ee9cb12a..00000000
--- a/app/src/main/java/com/wireguard/android/util/ErrorMessages.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.res.Resources;
-import androidx.annotation.Nullable;
-
-import com.wireguard.android.Application;
-import com.wireguard.android.R;
-import com.wireguard.config.BadConfigException;
-import com.wireguard.config.BadConfigException.Location;
-import com.wireguard.config.BadConfigException.Reason;
-import com.wireguard.config.InetEndpoint;
-import com.wireguard.config.InetNetwork;
-import com.wireguard.config.ParseException;
-import com.wireguard.crypto.Key.Format;
-import com.wireguard.crypto.KeyFormatException;
-import com.wireguard.crypto.KeyFormatException.Type;
-
-import java.net.InetAddress;
-import java.util.EnumMap;
-import java.util.Map;
-
-import java9.util.Maps;
-
-public final class ErrorMessages {
- private static final Map<Reason, Integer> BCE_REASON_MAP = new EnumMap<>(Maps.of(
- Reason.INVALID_KEY, R.string.bad_config_reason_invalid_key,
- Reason.INVALID_NUMBER, R.string.bad_config_reason_invalid_number,
- Reason.INVALID_VALUE, R.string.bad_config_reason_invalid_value,
- Reason.MISSING_ATTRIBUTE, R.string.bad_config_reason_missing_attribute,
- Reason.MISSING_SECTION, R.string.bad_config_reason_missing_section,
- Reason.MISSING_VALUE, R.string.bad_config_reason_missing_value,
- Reason.SYNTAX_ERROR, R.string.bad_config_reason_syntax_error,
- Reason.UNKNOWN_ATTRIBUTE, R.string.bad_config_reason_unknown_attribute,
- Reason.UNKNOWN_SECTION, R.string.bad_config_reason_unknown_section
- ));
- private static final Map<Format, Integer> KFE_FORMAT_MAP = new EnumMap<>(Maps.of(
- Format.BASE64, R.string.key_length_explanation_base64,
- Format.BINARY, R.string.key_length_explanation_binary,
- Format.HEX, R.string.key_length_explanation_hex
- ));
- private static final Map<Type, Integer> KFE_TYPE_MAP = new EnumMap<>(Maps.of(
- Type.CONTENTS, R.string.key_contents_error,
- Type.LENGTH, R.string.key_length_error
- ));
- private static final Map<Class, Integer> PE_CLASS_MAP = Maps.of(
- InetAddress.class, R.string.parse_error_inet_address,
- InetEndpoint.class, R.string.parse_error_inet_endpoint,
- InetNetwork.class, R.string.parse_error_inet_network,
- Integer.class, R.string.parse_error_integer
- );
-
- private ErrorMessages() {
- // Prevent instantiation
- }
-
- public static String get(@Nullable final Throwable throwable) {
- final Resources resources = Application.get().getResources();
- if (throwable == null)
- return resources.getString(R.string.unknown_error);
- final Throwable rootCause = rootCause(throwable);
- final String message;
- if (rootCause instanceof BadConfigException) {
- final BadConfigException bce = (BadConfigException) rootCause;
- final String reason = getBadConfigExceptionReason(resources, bce);
- final String context = bce.getLocation() == Location.TOP_LEVEL ?
- resources.getString(R.string.bad_config_context_top_level,
- bce.getSection().getName()) :
- resources.getString(R.string.bad_config_context,
- bce.getSection().getName(),
- bce.getLocation().getName());
- final String explanation = getBadConfigExceptionExplanation(resources, bce);
- message = resources.getString(R.string.bad_config_error, reason, context) + explanation;
- } else if (rootCause.getMessage() != null) {
- message = rootCause.getMessage();
- } else {
- final String errorType = rootCause.getClass().getSimpleName();
- message = resources.getString(R.string.generic_error, errorType);
- }
- return message;
- }
-
- private static String getBadConfigExceptionExplanation(final Resources resources,
- final BadConfigException bce) {
- if (bce.getCause() instanceof KeyFormatException) {
- final KeyFormatException kfe = (KeyFormatException) bce.getCause();
- if (kfe.getType() == Type.LENGTH)
- return resources.getString(KFE_FORMAT_MAP.get(kfe.getFormat()));
- } else if (bce.getCause() instanceof ParseException) {
- final ParseException pe = (ParseException) bce.getCause();
- if (pe.getMessage() != null)
- return ": " + pe.getMessage();
- } else if (bce.getLocation() == Location.LISTEN_PORT) {
- return resources.getString(R.string.bad_config_explanation_udp_port);
- } else if (bce.getLocation() == Location.MTU) {
- return resources.getString(R.string.bad_config_explanation_positive_number);
- } else if (bce.getLocation() == Location.PERSISTENT_KEEPALIVE) {
- return resources.getString(R.string.bad_config_explanation_pka);
- }
- return "";
- }
-
- private static String getBadConfigExceptionReason(final Resources resources,
- final BadConfigException bce) {
- if (bce.getCause() instanceof KeyFormatException) {
- final KeyFormatException kfe = (KeyFormatException) bce.getCause();
- return resources.getString(KFE_TYPE_MAP.get(kfe.getType()));
- } else if (bce.getCause() instanceof ParseException) {
- final ParseException pe = (ParseException) bce.getCause();
- final String type = resources.getString(PE_CLASS_MAP.containsKey(pe.getParsingClass()) ?
- PE_CLASS_MAP.get(pe.getParsingClass()) : R.string.parse_error_generic);
- return resources.getString(R.string.parse_error_reason, type, pe.getText());
- }
- return resources.getString(BCE_REASON_MAP.get(bce.getReason()), bce.getText());
- }
-
- private static Throwable rootCause(final Throwable throwable) {
- Throwable cause = throwable;
- while (cause.getCause() != null) {
- if (cause instanceof BadConfigException)
- break;
- cause = cause.getCause();
- }
- return cause;
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ExceptionLoggers.java b/app/src/main/java/com/wireguard/android/util/ExceptionLoggers.java
deleted file mode 100644
index 5c7a38c0..00000000
--- a/app/src/main/java/com/wireguard/android/util/ExceptionLoggers.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import androidx.annotation.Nullable;
-import android.util.Log;
-
-import java9.util.function.BiConsumer;
-
-/**
- * Helpers for logging exceptions from asynchronous tasks. These can be passed to
- * {@code CompletionStage.whenComplete()} at the end of an asynchronous future chain.
- */
-
-public enum ExceptionLoggers implements BiConsumer<Object, Throwable> {
- D(Log.DEBUG),
- E(Log.ERROR);
-
- private static final String TAG = "WireGuard/" + ExceptionLoggers.class.getSimpleName();
- private final int priority;
-
- ExceptionLoggers(final int priority) {
- this.priority = priority;
- }
-
- @Override
- public void accept(final Object result, @Nullable final Throwable throwable) {
- if (throwable != null)
- Log.println(Log.ERROR, TAG, Log.getStackTraceString(throwable));
- else if (priority <= Log.DEBUG)
- Log.println(priority, TAG, "Future completed successfully");
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/FragmentUtils.java b/app/src/main/java/com/wireguard/android/util/FragmentUtils.java
deleted file mode 100644
index 5fb9a3bc..00000000
--- a/app/src/main/java/com/wireguard/android/util/FragmentUtils.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-package com.wireguard.android.util;
-
-import android.content.Context;
-import androidx.preference.Preference;
-import android.view.ContextThemeWrapper;
-
-import com.wireguard.android.activity.SettingsActivity;
-
-public final class FragmentUtils {
- private FragmentUtils() {
- // Prevent instantiation
- }
-
- public static SettingsActivity getPrefActivity(final Preference preference) {
- final Context context = preference.getContext();
- if (context instanceof ContextThemeWrapper) {
- if (context instanceof SettingsActivity) {
- return ((SettingsActivity) context);
- }
- }
- return null;
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ModuleLoader.java b/app/src/main/java/com/wireguard/android/util/ModuleLoader.java
deleted file mode 100644
index 524d10a6..00000000
--- a/app/src/main/java/com/wireguard/android/util/ModuleLoader.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright © 2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.Context;
-import android.system.OsConstants;
-import android.util.Base64;
-
-import com.wireguard.android.Application;
-import com.wireguard.android.util.RootShell.NoRootException;
-
-import net.i2p.crypto.eddsa.EdDSAEngine;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
-import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.security.InvalidParameterException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.Signature;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Nullable;
-
-public class ModuleLoader {
- private static final String MODULE_PUBLIC_KEY_BASE64 = "RWRmHuT9PSqtwfsLtEx+QS06BJtLgFYteL9WCNjH7yuyu5Y1DieSN7If";
- private static final String MODULE_LIST_URL = "https://download.wireguard.com/android-module/modules.txt.sig";
- private static final String MODULE_URL = "https://download.wireguard.com/android-module/%s";
- private static final String MODULE_NAME = "wireguard-%s.ko";
-
- private final File moduleDir;
- private final File tmpDir;
-
- public ModuleLoader(final Context context) {
- moduleDir = new File(context.getCacheDir(), "kmod");
- tmpDir = new File(context.getCacheDir(), "tmp");
- }
-
- public boolean moduleMightExist() {
- return moduleDir.exists() && moduleDir.isDirectory();
- }
-
- public void loadModule() throws IOException, NoRootException {
- Application.getRootShell().run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath()));
- }
-
- public boolean isModuleLoaded() {
- return new File("/sys/module/wireguard").exists();
- }
-
- private static final class Sha256Digest {
- private byte[] bytes;
- private Sha256Digest(final String hex) {
- if (hex.length() != 64)
- throw new InvalidParameterException("SHA256 hashes must be 32 bytes long");
- bytes = new byte[32];
- for (int i = 0; i < 32; ++i)
- bytes[i] = (byte)Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
- }
- }
-
- @Nullable
- private Map<String, Sha256Digest> verifySignedHashes(final String signifyDigest) {
- final byte[] publicKeyBytes = Base64.decode(MODULE_PUBLIC_KEY_BASE64, Base64.DEFAULT);
-
- if (publicKeyBytes == null || publicKeyBytes.length != 32 + 10 || publicKeyBytes[0] != 'E' || publicKeyBytes[1] != 'd')
- return null;
-
- final String[] lines = signifyDigest.split("\n", 3);
- if (lines.length != 3)
- return null;
- if (!lines[0].startsWith("untrusted comment: "))
- return null;
-
- final byte[] signatureBytes = Base64.decode(lines[1], Base64.DEFAULT);
- if (signatureBytes == null || signatureBytes.length != 64 + 10)
- return null;
- for (int i = 0; i < 10; ++i) {
- if (signatureBytes[i] != publicKeyBytes[i])
- return null;
- }
-
- try {
- EdDSAParameterSpec parameterSpec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
- Signature signature = new EdDSAEngine(MessageDigest.getInstance(parameterSpec.getHashAlgorithm()));
- byte[] rawPublicKeyBytes = new byte[32];
- System.arraycopy(publicKeyBytes, 10, rawPublicKeyBytes, 0, 32);
- signature.initVerify(new EdDSAPublicKey(new EdDSAPublicKeySpec(rawPublicKeyBytes, parameterSpec)));
- signature.update(lines[2].getBytes(StandardCharsets.UTF_8));
- if (!signature.verify(signatureBytes, 10, 64))
- return null;
- } catch (final Exception ignored) {
- return null;
- }
-
- Map<String, Sha256Digest> hashes = new HashMap<>();
- for (final String line : lines[2].split("\n")) {
- final String[] components = line.split(" ", 2);
- if (components.length != 2)
- return null;
- try {
- hashes.put(components[1], new Sha256Digest(components[0]));
- } catch (final Exception ignored) {
- return null;
- }
- }
- return hashes;
- }
-
- public Integer download() throws IOException, NoRootException, NoSuchAlgorithmException {
- final List<String> output = new ArrayList<>();
- Application.getRootShell().run(output, "sha256sum /proc/version|cut -d ' ' -f 1");
- if (output.size() != 1 || output.get(0).length() != 64)
- throw new InvalidParameterException("Invalid sha256 of /proc/version");
- final String moduleName = String.format(MODULE_NAME, output.get(0));
- HttpURLConnection connection = (HttpURLConnection)new URL(MODULE_LIST_URL).openConnection();
- connection.setRequestProperty("User-Agent", Application.USER_AGENT);
- connection.connect();
- if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
- throw new IOException("Hash list could not be found");
- byte[] input = new byte[1024 * 1024 * 3 /* 3MiB */];
- int len;
- try (final InputStream inputStream = connection.getInputStream()) {
- len = inputStream.read(input);
- }
- if (len <= 0)
- throw new IOException("Hash list was empty");
- final Map<String, Sha256Digest> modules = verifySignedHashes(new String(input, 0, len, StandardCharsets.UTF_8));
- if (modules == null)
- throw new InvalidParameterException("The signature did not verify or invalid hash list format");
- if (!modules.containsKey(moduleName))
- return OsConstants.ENOENT;
- connection = (HttpURLConnection)new URL(String.format(MODULE_URL, moduleName)).openConnection();
- connection.setRequestProperty("User-Agent", Application.USER_AGENT);
- connection.connect();
- if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
- throw new IOException("Module file could not be found, despite being on hash list");
-
- tmpDir.mkdirs();
- moduleDir.mkdir();
- File tempFile = null;
- try {
- tempFile = File.createTempFile("UNVERIFIED-", null, tmpDir);
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- try (final InputStream inputStream = connection.getInputStream();
- final OutputStream outputStream = new FileOutputStream(tempFile)) {
- int total = 0;
- while ((len = inputStream.read(input)) > 0) {
- total += len;
- if (total > 1024 * 1024 * 15 /* 15 MiB */)
- throw new IOException("File too big");
- outputStream.write(input, 0, len);
- digest.update(input, 0, len);
- }
- }
- if (!Arrays.equals(digest.digest(), modules.get(moduleName).bytes))
- throw new IOException("Incorrect file hash");
-
- if (!tempFile.renameTo(new File(moduleDir, moduleName)))
- throw new IOException("Unable to rename to final destination");
- } finally {
- if (tempFile != null)
- tempFile.delete();
- }
- return OsConstants.EXIT_SUCCESS;
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java b/app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java
deleted file mode 100644
index 0ba02184..00000000
--- a/app/src/main/java/com/wireguard/android/util/ObservableKeyedArrayList.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import androidx.databinding.ObservableArrayList;
-import androidx.annotation.Nullable;
-
-import com.wireguard.util.Keyed;
-
-import java.util.Collection;
-import java.util.ListIterator;
-import java.util.Objects;
-
-/**
- * ArrayList that allows looking up elements by some key property. As the key property must always
- * be retrievable, this list cannot hold {@code null} elements. Because this class places no
- * restrictions on the order or duplication of keys, lookup by key, as well as all list modification
- * operations, require O(n) time.
- */
-
-public class ObservableKeyedArrayList<K, E extends Keyed<? extends K>>
- extends ObservableArrayList<E> implements ObservableKeyedList<K, E> {
- @Override
- public boolean add(@Nullable final E e) {
- if (e == null)
- throw new NullPointerException("Trying to add a null element");
- return super.add(e);
- }
-
- @Override
- public void add(final int index, @Nullable final E e) {
- if (e == null)
- throw new NullPointerException("Trying to add a null element");
- super.add(index, e);
- }
-
- @Override
- public boolean addAll(final Collection<? extends E> c) {
- if (c.contains(null))
- throw new NullPointerException("Trying to add a collection with null element(s)");
- return super.addAll(c);
- }
-
- @Override
- public boolean addAll(final int index, final Collection<? extends E> c) {
- if (c.contains(null))
- throw new NullPointerException("Trying to add a collection with null element(s)");
- return super.addAll(index, c);
- }
-
- @Override
- public boolean containsAllKeys(final Collection<K> keys) {
- for (final K key : keys)
- if (!containsKey(key))
- return false;
- return true;
- }
-
- @Override
- public boolean containsKey(final K key) {
- return indexOfKey(key) >= 0;
- }
-
- @Nullable
- @Override
- public E get(final K key) {
- final int index = indexOfKey(key);
- return index >= 0 ? get(index) : null;
- }
-
- @Nullable
- @Override
- public E getLast(final K key) {
- final int index = lastIndexOfKey(key);
- return index >= 0 ? get(index) : null;
- }
-
- @Override
- public int indexOfKey(final K key) {
- final ListIterator<E> iterator = listIterator();
- while (iterator.hasNext()) {
- final int index = iterator.nextIndex();
- if (Objects.equals(iterator.next().getKey(), key))
- return index;
- }
- return -1;
- }
-
- @Override
- public int lastIndexOfKey(final K key) {
- final ListIterator<E> iterator = listIterator(size());
- while (iterator.hasPrevious()) {
- final int index = iterator.previousIndex();
- if (Objects.equals(iterator.previous().getKey(), key))
- return index;
- }
- return -1;
- }
-
- @Override
- public E set(final int index, @Nullable final E e) {
- if (e == null)
- throw new NullPointerException("Trying to set a null key");
- return super.set(index, e);
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java b/app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java
deleted file mode 100644
index be8ceb9b..00000000
--- a/app/src/main/java/com/wireguard/android/util/ObservableKeyedList.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import androidx.databinding.ObservableList;
-
-import com.wireguard.util.Keyed;
-import com.wireguard.util.KeyedList;
-
-/**
- * A list that is both keyed and observable.
- */
-
-public interface ObservableKeyedList<K, E extends Keyed<? extends K>>
- extends KeyedList<K, E>, ObservableList<E> {
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java
deleted file mode 100644
index 1d585856..00000000
--- a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import androidx.annotation.Nullable;
-
-import com.wireguard.util.Keyed;
-import com.wireguard.util.SortedKeyedList;
-
-import java.util.AbstractList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.Spliterator;
-
-/**
- * KeyedArrayList that enforces uniqueness and sorted order across the set of keys. This class uses
- * binary search to improve lookup and replacement times to O(log(n)). However, due to the
- * array-based nature of this class, insertion and removal of elements with anything but the largest
- * key still require O(n) time.
- */
-
-public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>>
- extends ObservableKeyedArrayList<K, E> implements ObservableSortedKeyedList<K, E> {
- @Nullable private final Comparator<? super K> comparator;
- private final transient KeyList<K, E> keyList = new KeyList<>(this);
-
- @SuppressWarnings("WeakerAccess")
- public ObservableSortedKeyedArrayList() {
- comparator = null;
- }
-
- public ObservableSortedKeyedArrayList(final Comparator<? super K> comparator) {
- this.comparator = comparator;
- }
-
- public ObservableSortedKeyedArrayList(final Collection<? extends E> c) {
- this();
- addAll(c);
- }
-
- public ObservableSortedKeyedArrayList(final SortedKeyedList<K, E> other) {
- this(other.comparator());
- addAll(other);
- }
-
- @Override
- public boolean add(final E e) {
- final int insertionPoint = getInsertionPoint(e);
- if (insertionPoint < 0) {
- // Skipping insertion is non-destructive if the new and existing objects are the same.
- if (e == get(-insertionPoint - 1))
- return false;
- throw new IllegalArgumentException("Element with same key already exists in list");
- }
- super.add(insertionPoint, e);
- return true;
- }
-
- @Override
- public void add(final int index, final E e) {
- final int insertionPoint = getInsertionPoint(e);
- if (insertionPoint < 0)
- throw new IllegalArgumentException("Element with same key already exists in list");
- if (insertionPoint != index)
- throw new IndexOutOfBoundsException("Wrong index given for element");
- super.add(index, e);
- }
-
- @Override
- public boolean addAll(final Collection<? extends E> c) {
- boolean didChange = false;
- for (final E e : c)
- if (add(e))
- didChange = true;
- return didChange;
- }
-
- @Override
- public boolean addAll(int index, final Collection<? extends E> c) {
- for (final E e : c)
- add(index++, e);
- return true;
- }
-
- @Nullable
- @Override
- public Comparator<? super K> comparator() {
- return comparator;
- }
-
- @Override
- public K firstKey() {
- if (isEmpty())
- // The parameter in the exception is only to shut
- // lint up, we never care for the exception message.
- throw new NoSuchElementException("Empty set");
- return get(0).getKey();
- }
-
- private int getInsertionPoint(final E e) {
- if (comparator != null) {
- return -Collections.binarySearch(keyList, e.getKey(), comparator) - 1;
- } else {
- @SuppressWarnings("unchecked") final List<Comparable<? super K>> list =
- (List<Comparable<? super K>>) keyList;
- return -Collections.binarySearch(list, e.getKey()) - 1;
- }
- }
-
- @Override
- public int indexOfKey(final K key) {
- final int index;
- if (comparator != null) {
- index = Collections.binarySearch(keyList, key, comparator);
- } else {
- @SuppressWarnings("unchecked") final List<Comparable<? super K>> list =
- (List<Comparable<? super K>>) keyList;
- index = Collections.binarySearch(list, key);
- }
- return index >= 0 ? index : -1;
- }
-
- @Override
- public Set<K> keySet() {
- return keyList;
- }
-
- @Override
- public int lastIndexOfKey(final K key) {
- // There can never be more than one element with the same key in the list.
- return indexOfKey(key);
- }
-
- @Override
- public K lastKey() {
- if (isEmpty())
- // The parameter in the exception is only to shut
- // lint up, we never care for the exception message.
- throw new NoSuchElementException("Empty set");
- return get(size() - 1).getKey();
- }
-
- @Override
- public E set(final int index, final E e) {
- final int order;
- if (comparator != null) {
- order = comparator.compare(e.getKey(), get(index).getKey());
- } else {
- @SuppressWarnings("unchecked") final Comparable<? super K> key =
- (Comparable<? super K>) e.getKey();
- order = key.compareTo(get(index).getKey());
- }
- if (order != 0) {
- // Allow replacement if the new key would be inserted adjacent to the replaced element.
- final int insertionPoint = getInsertionPoint(e);
- if (insertionPoint < index || insertionPoint > index + 1)
- throw new IndexOutOfBoundsException("Wrong index given for element");
- }
- return super.set(index, e);
- }
-
- @Override
- public Collection<E> values() {
- return this;
- }
-
- private static final class KeyList<K, E extends Keyed<? extends K>>
- extends AbstractList<K> implements Set<K> {
- private final ObservableSortedKeyedArrayList<K, E> list;
-
- private KeyList(final ObservableSortedKeyedArrayList<K, E> list) {
- this.list = list;
- }
-
- @Override
- public K get(final int index) {
- return list.get(index).getKey();
- }
-
- @Override
- public int size() {
- return list.size();
- }
-
- @Override
- @SuppressWarnings("EmptyMethod")
- public Spliterator<K> spliterator() {
- return super.spliterator();
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java
deleted file mode 100644
index d796704e..00000000
--- a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import com.wireguard.util.Keyed;
-import com.wireguard.util.SortedKeyedList;
-
-/**
- * A list that is both sorted/keyed and observable.
- */
-
-public interface ObservableSortedKeyedList<K, E extends Keyed<? extends K>>
- extends ObservableKeyedList<K, E>, SortedKeyedList<K, E> {
-}
diff --git a/app/src/main/java/com/wireguard/android/util/RootShell.java b/app/src/main/java/com/wireguard/android/util/RootShell.java
deleted file mode 100644
index 1fe5667a..00000000
--- a/app/src/main/java/com/wireguard/android/util/RootShell.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.Context;
-import androidx.annotation.Nullable;
-import android.util.Log;
-
-import com.wireguard.android.BuildConfig;
-import com.wireguard.android.R;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Collection;
-import java.util.UUID;
-
-/**
- * Helper class for running commands as root.
- */
-
-public class RootShell {
- private static final String SU = "su";
- private static final String TAG = "WireGuard/" + RootShell.class.getSimpleName();
-
- private final Context context;
- private final String deviceNotRootedMessage;
- private final File localBinaryDir;
- private final File localTemporaryDir;
- private final Object lock = new Object();
- private final String preamble;
- @Nullable private Process process;
- @Nullable private BufferedReader stderr;
- @Nullable private OutputStreamWriter stdin;
- @Nullable private BufferedReader stdout;
-
- public RootShell(final Context context) {
- deviceNotRootedMessage = context.getString(R.string.error_root);
- localBinaryDir = new File(context.getCodeCacheDir(), "bin");
- localTemporaryDir = new File(context.getCacheDir(), "tmp");
- preamble = String.format("export CALLING_PACKAGE=%s PATH=\"%s:$PATH\" TMPDIR='%s'; id -u\n",
- BuildConfig.APPLICATION_ID, localBinaryDir, localTemporaryDir);
- this.context = context;
- }
-
- private static boolean isExecutableInPath(final String name) {
- final String path = System.getenv("PATH");
- if (path == null)
- return false;
- for (final String dir : path.split(":"))
- if (new File(dir, name).canExecute())
- return true;
- return false;
- }
-
- private boolean isRunning() {
- synchronized (lock) {
- try {
- // Throws an exception if the process hasn't finished yet.
- if (process != null)
- process.exitValue();
- return false;
- } catch (final IllegalThreadStateException ignored) {
- // The existing process is still running.
- return true;
- }
- }
- }
-
- /**
- * Run a command in a root shell.
- *
- * @param output Lines read from stdout are appended to this list. Pass null if the
- * output from the shell is not important.
- * @param command Command to run as root.
- * @return The exit value of the command.
- */
- public int run(@Nullable final Collection<String> output, final String command)
- throws IOException, NoRootException {
- synchronized (lock) {
- /* Start inside synchronized block to prevent a concurrent call to stop(). */
- start();
- final String marker = UUID.randomUUID().toString();
- final String script = "echo " + marker + "; echo " + marker + " >&2; (" + command +
- "); ret=$?; echo " + marker + " $ret; echo " + marker + " $ret >&2\n";
- Log.v(TAG, "executing: " + command);
- stdin.write(script);
- stdin.flush();
- String line;
- int errnoStdout = Integer.MIN_VALUE;
- int errnoStderr = Integer.MAX_VALUE;
- int markersSeen = 0;
- while ((line = stdout.readLine()) != null) {
- if (line.startsWith(marker)) {
- ++markersSeen;
- if (line.length() > marker.length() + 1) {
- errnoStdout = Integer.valueOf(line.substring(marker.length() + 1));
- break;
- }
- } else if (markersSeen > 0) {
- if (output != null)
- output.add(line);
- Log.v(TAG, "stdout: " + line);
- }
- }
- while ((line = stderr.readLine()) != null) {
- if (line.startsWith(marker)) {
- ++markersSeen;
- if (line.length() > marker.length() + 1) {
- errnoStderr = Integer.valueOf(line.substring(marker.length() + 1));
- break;
- }
- } else if (markersSeen > 2) {
- Log.v(TAG, "stderr: " + line);
- }
- }
- if (markersSeen != 4)
- throw new IOException(context.getString(R.string.shell_marker_count_error, markersSeen));
- if (errnoStdout != errnoStderr)
- throw new IOException(context.getString(R.string.shell_exit_status_read_error));
- Log.v(TAG, "exit: " + errnoStdout);
- return errnoStdout;
- }
- }
-
- public void start() throws IOException, NoRootException {
- if (!isExecutableInPath(SU))
- throw new NoRootException(deviceNotRootedMessage);
- synchronized (lock) {
- if (isRunning())
- return;
- if (!localBinaryDir.isDirectory() && !localBinaryDir.mkdirs())
- throw new FileNotFoundException(context.getString(R.string.create_bin_dir_error));
- if (!localTemporaryDir.isDirectory() && !localTemporaryDir.mkdirs())
- throw new FileNotFoundException(context.getString(R.string.create_temp_dir_error));
- try {
- final ProcessBuilder builder = new ProcessBuilder().command(SU);
- builder.environment().put("LC_ALL", "C");
- try {
- process = builder.start();
- } catch (final IOException e) {
- // A failure at this stage means the device isn't rooted.
- throw new NoRootException(deviceNotRootedMessage, e);
- }
- stdin = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8);
- stdout = new BufferedReader(new InputStreamReader(process.getInputStream(),
- StandardCharsets.UTF_8));
- stderr = new BufferedReader(new InputStreamReader(process.getErrorStream(),
- StandardCharsets.UTF_8));
- stdin.write(preamble);
- stdin.flush();
- // Check that the shell started successfully.
- final String uid = stdout.readLine();
- if (!"0".equals(uid)) {
- Log.w(TAG, "Root check did not return correct UID: " + uid);
- throw new NoRootException(deviceNotRootedMessage);
- }
- if (!isRunning()) {
- String line;
- while ((line = stderr.readLine()) != null) {
- Log.w(TAG, "Root check returned an error: " + line);
- if (line.contains("Permission denied"))
- throw new NoRootException(deviceNotRootedMessage);
- }
- throw new IOException(context.getString(R.string.shell_start_error, process.exitValue()));
- }
- } catch (final IOException | NoRootException e) {
- stop();
- throw e;
- }
- }
- }
-
- public void stop() {
- synchronized (lock) {
- if (process != null) {
- process.destroy();
- process = null;
- }
- }
- }
-
- public static class NoRootException extends Exception {
- public NoRootException(final String message, final Throwable cause) {
- super(message, cause);
- }
-
- public NoRootException(final String message) {
- super(message);
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java b/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java
deleted file mode 100644
index e3923d19..00000000
--- a/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-public final class SharedLibraryLoader {
- private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName();
-
- private SharedLibraryLoader() {
- }
-
- public static boolean extractLibrary(final Context context, final String libName, final File destination) throws IOException {
- final Collection<String> apks = new HashSet<>();
- if (context.getApplicationInfo().sourceDir != null)
- apks.add(context.getApplicationInfo().sourceDir);
- if (context.getApplicationInfo().splitSourceDirs != null)
- apks.addAll(Arrays.asList(context.getApplicationInfo().splitSourceDirs));
-
- for (final String abi : Build.SUPPORTED_ABIS) {
- for (final String apk : apks) {
- final ZipFile zipFile;
- try {
- zipFile = new ZipFile(new File(apk), ZipFile.OPEN_READ);
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
-
- final String mappedLibName = System.mapLibraryName(libName);
- final byte[] buffer = new byte[1024 * 32];
- final String libZipPath = "lib" + File.separatorChar + abi + File.separatorChar + mappedLibName;
- final ZipEntry zipEntry = zipFile.getEntry(libZipPath);
- if (zipEntry == null)
- continue;
- Log.d(TAG, "Extracting apk:/" + libZipPath + " to " + destination.getAbsolutePath());
- try (final FileOutputStream out = new FileOutputStream(destination);
- final InputStream in = zipFile.getInputStream(zipEntry)) {
- int len;
- while ((len = in.read(buffer)) != -1) {
- out.write(buffer, 0, len);
- }
- }
- return true;
- }
- }
- return false;
- }
-
- public static void loadSharedLibrary(final Context context, final String libName) {
- Throwable noAbiException;
- try {
- System.loadLibrary(libName);
- return;
- } catch (final UnsatisfiedLinkError e) {
- Log.d(TAG, "Failed to load library normally, so attempting to extract from apk", e);
- noAbiException = e;
- }
- File f = null;
- try {
- f = File.createTempFile("lib", ".so", context.getCodeCacheDir());
- if (extractLibrary(context, libName, f)) {
- System.load(f.getAbsolutePath());
- return;
- }
- } catch (final Exception e) {
- Log.d(TAG, "Failed to load library apk:/" + libName, e);
- noAbiException = e;
- } finally {
- if (f != null)
- // noinspection ResultOfMethodCallIgnored
- f.delete();
- }
- if (noAbiException instanceof RuntimeException)
- throw (RuntimeException) noAbiException;
- throw new RuntimeException(noAbiException);
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java b/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java
deleted file mode 100644
index defdefd8..00000000
--- a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.Context;
-import androidx.annotation.Nullable;
-import android.system.OsConstants;
-import android.util.Log;
-
-import com.wireguard.android.Application;
-import com.wireguard.android.BuildConfig;
-import com.wireguard.android.R;
-import com.wireguard.android.util.RootShell.NoRootException;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Helper to install WireGuard tools to the system partition.
- */
-
-public final class ToolsInstaller {
- public static final int ERROR = 0x0;
- public static final int MAGISK = 0x4;
- public static final int NO = 0x2;
- public static final int SYSTEM = 0x8;
- public static final int YES = 0x1;
- private static final String[] EXECUTABLES = {"wg", "wg-quick"};
- private static final File[] INSTALL_DIRS = {
- new File("/system/xbin"),
- new File("/system/bin"),
- };
- @Nullable private static final File INSTALL_DIR = getInstallDir();
- private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName();
-
- private final Context context;
- private final File localBinaryDir;
- private final Object lock = new Object();
- @Nullable private Boolean areToolsAvailable;
- @Nullable private Boolean installAsMagiskModule;
-
- public ToolsInstaller(final Context context) {
- localBinaryDir = new File(context.getCodeCacheDir(), "bin");
- this.context = context;
- }
-
- @Nullable
- private static File getInstallDir() {
- final String path = System.getenv("PATH");
- if (path == null)
- return INSTALL_DIRS[0];
- final List<String> paths = Arrays.asList(path.split(":"));
- for (final File dir : INSTALL_DIRS) {
- if (paths.contains(dir.getPath()) && dir.isDirectory())
- return dir;
- }
- return null;
- }
-
- public int areInstalled() throws NoRootException {
- if (INSTALL_DIR == null)
- return ERROR;
- final StringBuilder script = new StringBuilder();
- for (final String name : EXECUTABLES) {
- script.append(String.format("cmp -s '%s' '%s' && ",
- new File(localBinaryDir, name).getAbsolutePath(),
- new File(INSTALL_DIR, name).getAbsolutePath()));
- }
- script.append("exit ").append(OsConstants.EALREADY).append(';');
- try {
- final int ret = Application.getRootShell().run(null, script.toString());
- if (ret == OsConstants.EALREADY)
- return willInstallAsMagiskModule() ? YES | MAGISK : YES | SYSTEM;
- else
- return willInstallAsMagiskModule() ? NO | MAGISK : NO | SYSTEM;
- } catch (final IOException ignored) {
- return ERROR;
- }
- }
-
- public void ensureToolsAvailable() throws FileNotFoundException {
- synchronized (lock) {
- if (areToolsAvailable == null) {
- try {
- Log.d(TAG, extract() ? "Tools are now extracted into our private binary dir" :
- "Tools were already extracted into our private binary dir");
- areToolsAvailable = true;
- } catch (final IOException e) {
- Log.e(TAG, "The wg and wg-quick tools are not available", e);
- areToolsAvailable = false;
- }
- }
- if (!areToolsAvailable)
- throw new FileNotFoundException(
- context.getString(R.string.tools_unavailable_error));
- }
- }
-
- public int install() throws NoRootException, IOException {
- return willInstallAsMagiskModule() ? installMagisk() : installSystem();
- }
-
- private int installMagisk() throws NoRootException, IOException {
- extract();
- final StringBuilder script = new StringBuilder("set -ex; ");
-
- script.append("trap 'rm -rf /sbin/.magisk/img/wireguard' INT TERM EXIT; ");
- script.append(String.format("rm -rf /sbin/.magisk/img/wireguard/; mkdir -p /sbin/.magisk/img/wireguard%s; ", INSTALL_DIR));
- script.append(String.format("printf 'name=WireGuard Command Line Tools\nversion=%s\nversionCode=%s\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /sbin/.magisk/img/wireguard/module.prop; ", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
- script.append("touch /sbin/.magisk/img/wireguard/auto_mount; ");
- for (final String name : EXECUTABLES) {
- final File destination = new File("/sbin/.magisk/img/wireguard" + INSTALL_DIR, name);
- script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; chcon 'u:object_r:system_file:s0' '%s' || true; ",
- new File(localBinaryDir, name), destination, destination, destination));
- }
- script.append("trap - INT TERM EXIT;");
-
- try {
- return Application.getRootShell().run(null, script.toString()) == 0 ? YES | MAGISK : ERROR;
- } catch (final IOException ignored) {
- return ERROR;
- }
- }
-
- private int installSystem() throws NoRootException, IOException {
- if (INSTALL_DIR == null)
- return OsConstants.ENOENT;
- extract();
- final StringBuilder script = new StringBuilder("set -ex; ");
- script.append("trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system; ");
- for (final String name : EXECUTABLES) {
- final File destination = new File(INSTALL_DIR, name);
- script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' || true; ",
- new File(localBinaryDir, name), destination, destination, destination));
- }
- try {
- return Application.getRootShell().run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR;
- } catch (final IOException ignored) {
- return ERROR;
- }
- }
-
- public boolean extract() throws IOException {
- localBinaryDir.mkdirs();
- final File files[] = new File[EXECUTABLES.length];
- boolean allExist = true;
- for (int i = 0; i < files.length; ++i) {
- files[i] = new File(localBinaryDir, EXECUTABLES[i]);
- allExist &= files[i].exists();
- }
- if (allExist)
- return false;
- for (int i = 0; i < files.length; ++i) {
- if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], files[i]))
- throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]);
- if (!files[i].setExecutable(true, false))
- throw new IOException("Unable to mark " + files[i].getAbsolutePath() + " as executable");
- }
- return true;
- }
-
- private boolean willInstallAsMagiskModule() {
- synchronized (lock) {
- if (installAsMagiskModule == null) {
- try {
- installAsMagiskModule = Application.getRootShell().run(null, "[ -d /sbin/.magisk/mirror -a -d /sbin/.magisk/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS;
- } catch (final Exception ignored) {
- installAsMagiskModule = false;
- }
- }
- return installAsMagiskModule;
- }
- }
-}