diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/StubApk.java b/app/shared/src/main/java/com/topjohnwu/magisk/StubApk.java index 70543637b..45a2e05d3 100644 --- a/app/shared/src/main/java/com/topjohnwu/magisk/StubApk.java +++ b/app/shared/src/main/java/com/topjohnwu/magisk/StubApk.java @@ -1,14 +1,20 @@ package com.topjohnwu.magisk; import static android.os.Build.VERSION.SDK_INT; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.res.AssetManager; +import android.content.res.Resources; +import android.content.res.loader.ResourcesLoader; +import android.content.res.loader.ResourcesProvider; +import android.os.ParcelFileDescriptor; import java.io.File; +import java.io.IOException; import java.lang.reflect.Method; import java.util.Map; @@ -50,12 +56,21 @@ public class StubApk { return new File(getDynDir(info), "update.apk"); } - public static void addAssetPath(AssetManager asset, String path) { - try { - if (addAssetPath == null) - addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class); - addAssetPath.invoke(asset, path); - } catch (Exception ignored) {} + public static void addAssetPath(Resources res, String path) { + if (SDK_INT >= 30) { + try (var fd = ParcelFileDescriptor.open(new File(path), MODE_READ_ONLY)) { + var loader = new ResourcesLoader(); + loader.addProvider(ResourcesProvider.loadFromApk(fd)); + res.addLoaders(loader); + } catch (IOException ignored) {} + } else { + AssetManager asset = res.getAssets(); + try { + if (addAssetPath == null) + addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class); + addAssetPath.invoke(asset, path); + } catch (Exception ignored) {} + } } public static void restartProcess(Activity activity) { diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt index b4506bd52..b5ad7f2a4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt @@ -18,7 +18,7 @@ import com.topjohnwu.magisk.di.AppContext lateinit var AppApkPath: String -fun AssetManager.addAssetPath(path: String) = StubApk.addAssetPath(this, path) +fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path) fun Context.wrap(): Context = if (this is PatchedContext) this else PatchedContext(this) @@ -32,17 +32,18 @@ private class PatchedContext(base: Context) : ContextWrapper(base) { fun Resources.patch(): Resources { syncLocale() if (isRunningAsStub) - assets.addAssetPath(AppApkPath) + addAssetPath(AppApkPath) return this } fun createNewResources(): Resources { val asset = AssetManager::class.java.newInstance() - asset.addAssetPath(AppApkPath) val config = Configuration(AppContext.resources.configuration) val metrics = DisplayMetrics() metrics.setTo(AppContext.resources.displayMetrics) - return Resources(asset, metrics, config) + val res = Resources(asset, metrics, config) + res.addAssetPath(AppApkPath) + return res } fun Class<*>.cmp(pkg: String) = diff --git a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java index 450988b89..d74cbff9d 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java +++ b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java @@ -12,8 +12,14 @@ import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; +import android.content.res.loader.ResourcesLoader; +import android.content.res.loader.ResourcesProvider; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.system.Os; +import android.system.OsConstants; import android.util.Log; import android.view.ContextThemeWrapper; @@ -27,6 +33,7 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -66,7 +73,9 @@ public class DownloadActivity extends Activity { dynLoad = !getPackageName().equals(BuildConfig.APPLICATION_ID); // Inject resources - loadResources(); + try { + loadResources(); + } catch (Exception ignored) {} ProviderInstaller.install(this); @@ -140,21 +149,35 @@ public class DownloadActivity extends Activity { } } - private void loadResources() { - File apk = new File(getCacheDir(), "res.apk"); - try { - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - SecretKey key = new SecretKeySpec(Bytes.key(), "AES"); - IvParameterSpec iv = new IvParameterSpec(Bytes.iv()); - cipher.init(Cipher.DECRYPT_MODE, key, iv); - var is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher); - var out = new FileOutputStream(apk); - try (is; out) { - APKInstall.transfer(is, out); - } - StubApk.addAssetPath(getResources().getAssets(), apk.getPath()); - } catch (Exception ignored) { + private void decryptResources(OutputStream out) throws Exception { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + SecretKey key = new SecretKeySpec(Bytes.key(), "AES"); + IvParameterSpec iv = new IvParameterSpec(Bytes.iv()); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + var is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher); + try (is; out) { + APKInstall.transfer(is, out); } } + private void loadResources() throws Exception { + if (Build.VERSION.SDK_INT >= 30) { + var fd = Os.memfd_create("res.apk", 0); + try { + decryptResources(new FileOutputStream(fd)); + Os.lseek(fd, 0, OsConstants.SEEK_SET); + try (var pfd = ParcelFileDescriptor.dup(fd)) { + var loader = new ResourcesLoader(); + loader.addProvider(ResourcesProvider.loadFromApk(pfd)); + getResources().addLoaders(loader); + } + } finally { + Os.close(fd); + } + } else { + File apk = new File(getCacheDir(), "res.apk"); + decryptResources(new FileOutputStream(apk)); + StubApk.addAssetPath(getResources(), apk.getPath()); + } + } }