From de3747d65e3f67321876b50784bf7f878a82ebe7 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 12 Dec 2021 23:52:59 -0800 Subject: [PATCH] Copy APK from external storage in stub Much faster and easier development --- stub/build.gradle.kts | 2 +- .../topjohnwu/magisk/DelegateApplication.java | 5 -- .../topjohnwu/magisk/DownloadActivity.java | 5 +- .../java/com/topjohnwu/magisk/InjectAPK.java | 55 +++++++++++++++---- 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/stub/build.gradle.kts b/stub/build.gradle.kts index 5263213eb..94f9b567f 100644 --- a/stub/build.gradle.kts +++ b/stub/build.gradle.kts @@ -11,7 +11,7 @@ paranoid { android { val canary = !Config.version.contains(".") - val url = Config["DEV_CHANNEL"] ?: if (canary) null + val url = if (canary) null else "https://cdn.jsdelivr.net/gh/topjohnwu/magisk-files@${Config.version}/app-release.apk" defaultConfig { diff --git a/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java b/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java index 84d7b794f..3b4ad53d6 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java +++ b/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java @@ -12,17 +12,12 @@ import io.michaelrocks.paranoid.Obfuscate; @Obfuscate public class DelegateApplication extends Application { - static boolean dynLoad = false; - private Application receiver; @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); - // Only dynamic load full APK if hidden - dynLoad = !base.getPackageName().equals(BuildConfig.APPLICATION_ID); - receiver = InjectAPK.setup(this); if (receiver != null) try { // Call attachBaseContext without ContextImpl to show it is being wrapped diff --git a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java index 444edb03f..1d9dcba2b 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java +++ b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java @@ -3,7 +3,6 @@ package com.topjohnwu.magisk; import static android.R.string.no; import static android.R.string.ok; import static android.R.string.yes; -import static com.topjohnwu.magisk.DelegateApplication.dynLoad; import static com.topjohnwu.magisk.R2.string.dling; import static com.topjohnwu.magisk.R2.string.no_internet_msg; import static com.topjohnwu.magisk.R2.string.relaunch_app; @@ -49,12 +48,16 @@ public class DownloadActivity extends Activity { private String apkLink = BuildConfig.APK_URL; private Context themed; private ProgressDialog dialog; + private boolean dynLoad; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); themed = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault); + // Only download and dynamic load full APK if hidden + dynLoad = getPackageName().equals(BuildConfig.APPLICATION_ID); + // Inject resources loadResources(); diff --git a/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java b/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java index 0523e34d4..53c653e16 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java +++ b/stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java @@ -13,7 +13,9 @@ import android.os.Build; import android.util.Log; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -29,6 +31,17 @@ public class InjectAPK { return (DelegateComponentFactory) componentFactory; } + private static void copy(InputStream src, OutputStream dest) throws IOException { + try (InputStream s = src) { + try (OutputStream o = dest) { + byte[] buf = new byte[8192]; + for (int read; (read = s.read(buf)) >= 0;) { + o.write(buf, 0, read); + } + } + } + } + @SuppressWarnings("ResultOfMethodCallIgnored") static Application setup(Context context) { // Get ContextImpl @@ -38,25 +51,44 @@ public class InjectAPK { File apk = DynAPK.current(context); File update = DynAPK.update(context); - if (update.exists()) + + if (update.exists()) { + // Rename from update update.renameTo(apk); - if (!apk.exists()) { - // Try copying APK + } + + if (BuildConfig.DEBUG) { + // Copy from external for easier development + File external = new File(context.getExternalFilesDir(null), "magisk.apk"); + if (external.exists()) { + try { + copy(new FileInputStream(external), new FileOutputStream(apk)); + } catch (IOException e) { + Log.e(InjectAPK.class.getSimpleName(), "", e); + apk.delete(); + } finally { + external.delete(); + } + } + } + + if (!apk.exists() && !context.getPackageName().equals(BuildConfig.APPLICATION_ID)) { + // Copy from previous app Uri uri = new Uri.Builder().scheme("content") .authority("com.topjohnwu.magisk.provider") .encodedPath("apk_file").build(); ContentResolver resolver = context.getContentResolver(); - try (InputStream src = resolver.openInputStream(uri)) { + try { + InputStream src = resolver.openInputStream(uri); if (src != null) { - try (OutputStream out = new FileOutputStream(apk)) { - byte[] buf = new byte[4096]; - for (int read; (read = src.read(buf)) >= 0;) { - out.write(buf, 0, read); - } - } + copy(src, new FileOutputStream(apk)); } - } catch (Exception ignored) {} + } catch (IOException e) { + Log.e(InjectAPK.class.getSimpleName(), "", e); + apk.delete(); + } } + if (apk.exists()) { ClassLoader cl = new InjectedClassLoader(apk); PackageManager pm = context.getPackageManager(); @@ -124,5 +156,4 @@ public class InjectAPK { data.classToComponent = Mapping.inverseMap; return data; } - }