mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-25 12:07:30 +00:00
Use official APIs to load dynamic resources
This commit is contained in:
@@ -1,14 +1,20 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.AssetManager;
|
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.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -50,12 +56,21 @@ public class StubApk {
|
|||||||
return new File(getDynDir(info), "update.apk");
|
return new File(getDynDir(info), "update.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addAssetPath(AssetManager asset, String path) {
|
public static void addAssetPath(Resources res, String path) {
|
||||||
try {
|
if (SDK_INT >= 30) {
|
||||||
if (addAssetPath == null)
|
try (var fd = ParcelFileDescriptor.open(new File(path), MODE_READ_ONLY)) {
|
||||||
addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
|
var loader = new ResourcesLoader();
|
||||||
addAssetPath.invoke(asset, path);
|
loader.addProvider(ResourcesProvider.loadFromApk(fd));
|
||||||
} catch (Exception ignored) {}
|
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) {
|
public static void restartProcess(Activity activity) {
|
||||||
|
@@ -18,7 +18,7 @@ import com.topjohnwu.magisk.di.AppContext
|
|||||||
|
|
||||||
lateinit var AppApkPath: String
|
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)
|
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 {
|
fun Resources.patch(): Resources {
|
||||||
syncLocale()
|
syncLocale()
|
||||||
if (isRunningAsStub)
|
if (isRunningAsStub)
|
||||||
assets.addAssetPath(AppApkPath)
|
addAssetPath(AppApkPath)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNewResources(): Resources {
|
fun createNewResources(): Resources {
|
||||||
val asset = AssetManager::class.java.newInstance()
|
val asset = AssetManager::class.java.newInstance()
|
||||||
asset.addAssetPath(AppApkPath)
|
|
||||||
val config = Configuration(AppContext.resources.configuration)
|
val config = Configuration(AppContext.resources.configuration)
|
||||||
val metrics = DisplayMetrics()
|
val metrics = DisplayMetrics()
|
||||||
metrics.setTo(AppContext.resources.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) =
|
fun Class<*>.cmp(pkg: String) =
|
||||||
|
@@ -12,8 +12,14 @@ import android.app.AlertDialog;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.res.loader.ResourcesLoader;
|
||||||
|
import android.content.res.loader.ResourcesProvider;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.system.Os;
|
||||||
|
import android.system.OsConstants;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
|
|
||||||
@@ -27,6 +33,7 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.CipherInputStream;
|
import javax.crypto.CipherInputStream;
|
||||||
@@ -66,7 +73,9 @@ public class DownloadActivity extends Activity {
|
|||||||
dynLoad = !getPackageName().equals(BuildConfig.APPLICATION_ID);
|
dynLoad = !getPackageName().equals(BuildConfig.APPLICATION_ID);
|
||||||
|
|
||||||
// Inject resources
|
// Inject resources
|
||||||
loadResources();
|
try {
|
||||||
|
loadResources();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
ProviderInstaller.install(this);
|
ProviderInstaller.install(this);
|
||||||
|
|
||||||
@@ -140,21 +149,35 @@ public class DownloadActivity extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadResources() {
|
private void decryptResources(OutputStream out) throws Exception {
|
||||||
File apk = new File(getCacheDir(), "res.apk");
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
try {
|
SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
|
||||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
|
||||||
SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
|
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
||||||
IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
|
var is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher);
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
try (is; out) {
|
||||||
var is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher);
|
APKInstall.transfer(is, out);
|
||||||
var out = new FileOutputStream(apk);
|
|
||||||
try (is; out) {
|
|
||||||
APKInstall.transfer(is, out);
|
|
||||||
}
|
|
||||||
StubApk.addAssetPath(getResources().getAssets(), apk.getPath());
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user