mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-24 02:25:28 +00:00
Use AppComponentFactory to replace ClassLoader
This commit is contained in:
parent
7f65f7d3ca
commit
355341f0ab
@ -3,6 +3,7 @@ package com.topjohnwu.magisk;
|
|||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -16,24 +17,35 @@ public class DynAPK {
|
|||||||
private static File dynDir;
|
private static File dynDir;
|
||||||
private static Method addAssetPath;
|
private static Method addAssetPath;
|
||||||
|
|
||||||
private static File getDynDir(Context c) {
|
private static File getDynDir(ApplicationInfo info) {
|
||||||
if (dynDir == null) {
|
if (dynDir == null) {
|
||||||
|
final String dataDir;
|
||||||
if (SDK_INT >= 24) {
|
if (SDK_INT >= 24) {
|
||||||
// Use protected context to allow directBootAware
|
// Use device protected path to allow directBootAware
|
||||||
c = c.createDeviceProtectedStorageContext();
|
dataDir = info.deviceProtectedDataDir;
|
||||||
|
} else {
|
||||||
|
dataDir = info.dataDir;
|
||||||
}
|
}
|
||||||
dynDir = new File(c.getFilesDir().getParent(), "dyn");
|
dynDir = new File(dataDir, "dyn");
|
||||||
dynDir.mkdir();
|
dynDir.mkdirs();
|
||||||
}
|
}
|
||||||
return dynDir;
|
return dynDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File current(Context c) {
|
public static File current(Context c) {
|
||||||
return new File(getDynDir(c), "current.apk");
|
return new File(getDynDir(c.getApplicationInfo()), "current.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File current(ApplicationInfo info) {
|
||||||
|
return new File(getDynDir(info), "current.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File update(Context c) {
|
public static File update(Context c) {
|
||||||
return new File(getDynDir(c), "update.apk");
|
return new File(getDynDir(c.getApplicationInfo()), "update.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File update(ApplicationInfo info) {
|
||||||
|
return new File(getDynDir(info), "update.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addAssetPath(AssetManager asset, String path) {
|
public static void addAssetPath(AssetManager asset, String path) {
|
||||||
|
@ -30,3 +30,15 @@ class RedirectClassLoader extends ClassLoader {
|
|||||||
return clz == null ? super.loadClass(name, resolve) : clz;
|
return clz == null ? super.loadClass(name, resolve) : clz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DelegateClassLoader extends ClassLoader {
|
||||||
|
|
||||||
|
DelegateClassLoader() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
return DynLoad.loader.loadClass(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,11 +2,8 @@ package com.topjohnwu.magisk;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.ContextWrapper;
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import io.michaelrocks.paranoid.Obfuscate;
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
@Obfuscate
|
@Obfuscate
|
||||||
@ -17,14 +14,7 @@ public class DelegateApplication extends Application {
|
|||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
super.attachBaseContext(base);
|
super.attachBaseContext(base);
|
||||||
|
receiver = DynLoad.createAndSetupApp(this);
|
||||||
receiver = DynLoad.setup(this);
|
|
||||||
if (receiver != null) try {
|
|
||||||
// Call attachBaseContext without ContextImpl to show it is being wrapped
|
|
||||||
Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
|
|
||||||
m.setAccessible(true);
|
|
||||||
m.invoke(receiver, this);
|
|
||||||
} catch (Exception ignored) { /* Impossible */ }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,20 +8,25 @@ import android.app.Service;
|
|||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
public class DelegateComponentFactory extends AppComponentFactory {
|
public class DelegateComponentFactory extends AppComponentFactory {
|
||||||
|
|
||||||
ClassLoader loader;
|
|
||||||
AppComponentFactory receiver;
|
AppComponentFactory receiver;
|
||||||
|
|
||||||
public DelegateComponentFactory() {
|
public DelegateComponentFactory() {
|
||||||
DynLoad.componentFactory = this;
|
DynLoad.componentFactory = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader instantiateClassLoader(ClassLoader cl, ApplicationInfo info) {
|
||||||
|
DynLoad.loadAPK(info);
|
||||||
|
return new DelegateClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Application instantiateApplication(ClassLoader cl, String className) {
|
public Application instantiateApplication(ClassLoader cl, String className) {
|
||||||
if (loader == null) loader = cl;
|
|
||||||
return new DelegateApplication();
|
return new DelegateApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +34,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
public Activity instantiateActivity(ClassLoader cl, String className, Intent intent)
|
public Activity instantiateActivity(ClassLoader cl, String className, Intent intent)
|
||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateActivity(loader, className, intent);
|
return receiver.instantiateActivity(DynLoad.loader, className, intent);
|
||||||
return create(className);
|
return create(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +42,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent)
|
public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent)
|
||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateReceiver(loader, className, intent);
|
return receiver.instantiateReceiver(DynLoad.loader, className, intent);
|
||||||
return create(className);
|
return create(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +50,7 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
public Service instantiateService(ClassLoader cl, String className, Intent intent)
|
public Service instantiateService(ClassLoader cl, String className, Intent intent)
|
||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateService(loader, className, intent);
|
return receiver.instantiateService(DynLoad.loader, className, intent);
|
||||||
return create(className);
|
return create(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,13 +58,13 @@ public class DelegateComponentFactory extends AppComponentFactory {
|
|||||||
public ContentProvider instantiateProvider(ClassLoader cl, String className)
|
public ContentProvider instantiateProvider(ClassLoader cl, String className)
|
||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
if (receiver != null)
|
if (receiver != null)
|
||||||
return receiver.instantiateProvider(loader, className);
|
return receiver.instantiateProvider(DynLoad.loader, className);
|
||||||
return create(className);
|
return create(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T create(String name)
|
private <T> T create(String name)
|
||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException{
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException{
|
||||||
return (T) loader.loadClass(name).newInstance();
|
return (T) DynLoad.loader.loadClass(name).newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import android.content.ContextWrapper;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import io.michaelrocks.paranoid.Obfuscate;
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ public class DelegateRootService extends ContextWrapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
if (DynLoad.inject(base) == null)
|
if (DynLoad.createApp(base) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Create the actual RootService and call its attachBaseContext
|
// Create the actual RootService and call its attachBaseContext
|
||||||
@ -26,9 +25,7 @@ public class DelegateRootService extends ContextWrapper {
|
|||||||
Constructor<?> ctor = DynLoad.apkData.getRootService().getConstructor(Object.class);
|
Constructor<?> ctor = DynLoad.apkData.getRootService().getConstructor(Object.class);
|
||||||
ctor.setAccessible(true);
|
ctor.setAccessible(true);
|
||||||
Object service = ctor.newInstance(this);
|
Object service = ctor.newInstance(this);
|
||||||
Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
|
DynLoad.attachContext(service, base);
|
||||||
m.setAccessible(true);
|
|
||||||
m.invoke(service, base);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(DelegateRootService.class.getSimpleName(), "", e);
|
Log.e(DelegateRootService.class.getSimpleName(), "", e);
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,7 @@ public class DownloadActivity extends Activity {
|
|||||||
File apk = dynLoad ? DynAPK.current(this) : new File(getCacheDir(), "manager.apk");
|
File apk = dynLoad ? DynAPK.current(this) : new File(getCacheDir(), "manager.apk");
|
||||||
request(apkLink).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR).getAsFile(apk, file -> {
|
request(apkLink).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR).getAsFile(apk, file -> {
|
||||||
if (dynLoad) {
|
if (dynLoad) {
|
||||||
DynLoad.setup(this);
|
// TODO
|
||||||
runOnUiThread(onSuccess);
|
|
||||||
} else {
|
} else {
|
||||||
var receiver = APKInstall.register(this, BuildConfig.APPLICATION_ID, onSuccess);
|
var receiver = APKInstall.register(this, BuildConfig.APPLICATION_ID, onSuccess);
|
||||||
APKInstall.installapk(this, file);
|
APKInstall.installapk(this, file);
|
||||||
|
@ -2,7 +2,6 @@ package com.topjohnwu.magisk;
|
|||||||
|
|
||||||
import android.app.AppComponentFactory;
|
import android.app.AppComponentFactory;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
@ -10,6 +9,7 @@ import android.content.pm.PackageInfo;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.utils.APKInstall;
|
import com.topjohnwu.magisk.utils.APKInstall;
|
||||||
@ -20,6 +20,7 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import io.michaelrocks.paranoid.Obfuscate;
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
@ -27,23 +28,52 @@ import io.michaelrocks.paranoid.Obfuscate;
|
|||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public class DynLoad {
|
public class DynLoad {
|
||||||
|
|
||||||
|
// The current active classloader
|
||||||
|
static ClassLoader loader = new RedirectClassLoader();
|
||||||
static Object componentFactory;
|
static Object componentFactory;
|
||||||
static final DynAPK.Data apkData = createApkData();
|
static final DynAPK.Data apkData = createApkData();
|
||||||
|
|
||||||
// Dynamically load APK, inject ClassLoader into ContextImpl, then
|
private static boolean loadedApk = false;
|
||||||
// create the actual Application instance from the loaded APK
|
|
||||||
static Application inject(Context context) {
|
static void attachContext(Object o, Context context) {
|
||||||
File apk = DynAPK.current(context);
|
if (!(o instanceof ContextWrapper))
|
||||||
File update = DynAPK.update(context);
|
return;
|
||||||
|
try {
|
||||||
|
Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
|
||||||
|
m.setAccessible(true);
|
||||||
|
m.invoke(o, context);
|
||||||
|
} catch (Exception ignored) { /* Impossible */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically load APK from internal or external storage
|
||||||
|
static void loadAPK(ApplicationInfo info) {
|
||||||
|
if (loadedApk)
|
||||||
|
return;
|
||||||
|
loadedApk = true;
|
||||||
|
|
||||||
|
File apk = DynAPK.current(info);
|
||||||
|
File update = DynAPK.update(info);
|
||||||
|
|
||||||
if (update.exists()) {
|
if (update.exists()) {
|
||||||
// Rename from update
|
// Rename from update
|
||||||
update.renameTo(apk);
|
update.renameTo(apk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
// Copy from external for easier development
|
// Copy from external for easier development
|
||||||
File external = new File(context.getExternalFilesDir(null), "magisk.apk");
|
if (BuildConfig.DEBUG) copy_from_ext: {
|
||||||
|
final File dir;
|
||||||
|
try {
|
||||||
|
var dirs = (File[]) Environment.class
|
||||||
|
.getMethod("buildExternalStorageAppFilesDirs", String.class)
|
||||||
|
.invoke(null, info.packageName);
|
||||||
|
if (dirs == null)
|
||||||
|
break copy_from_ext;
|
||||||
|
dir = dirs[0];
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||||
|
break copy_from_ext;
|
||||||
|
}
|
||||||
|
File external = new File(dir, "magisk.apk");
|
||||||
if (external.exists()) {
|
if (external.exists()) {
|
||||||
try {
|
try {
|
||||||
var in = new FileInputStream(external);
|
var in = new FileInputStream(external);
|
||||||
@ -60,19 +90,33 @@ public class DynLoad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!apk.exists() && !context.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
if (apk.exists()) {
|
||||||
// Copy from previous app
|
loader = new InjectedClassLoader(apk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically load APK, inject ClassLoader into ContextImpl, then
|
||||||
|
// create the non-stub Application instance from the loaded APK
|
||||||
|
static Application createApp(Context context) {
|
||||||
|
File apk = DynAPK.current(context);
|
||||||
|
loadAPK(context.getApplicationInfo());
|
||||||
|
|
||||||
|
// Trigger folder creation
|
||||||
|
context.getExternalFilesDir(null);
|
||||||
|
|
||||||
|
// If no APK to load, attempt to copy from previous app
|
||||||
|
if (!isDynLoader() && !context.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
||||||
Uri uri = new Uri.Builder().scheme("content")
|
Uri uri = new Uri.Builder().scheme("content")
|
||||||
.authority("com.topjohnwu.magisk.provider")
|
.authority("com.topjohnwu.magisk.provider")
|
||||||
.encodedPath("apk_file").build();
|
.encodedPath("apk_file").build();
|
||||||
ContentResolver resolver = context.getContentResolver();
|
|
||||||
try {
|
try {
|
||||||
InputStream src = resolver.openInputStream(uri);
|
InputStream src = context.getContentResolver().openInputStream(uri);
|
||||||
if (src != null) {
|
if (src != null) {
|
||||||
var out = new FileOutputStream(apk);
|
var out = new FileOutputStream(apk);
|
||||||
try (src; out) {
|
try (src; out) {
|
||||||
APKInstall.transfer(src, out);
|
APKInstall.transfer(src, out);
|
||||||
}
|
}
|
||||||
|
loader = new InjectedClassLoader(apk);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||||
@ -80,12 +124,11 @@ public class DynLoad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (apk.exists()) {
|
if (isDynLoader()) {
|
||||||
ClassLoader cl = new InjectedClassLoader(apk);
|
|
||||||
PackageManager pm = context.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), 0);
|
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), 0);
|
||||||
try {
|
try {
|
||||||
return createApp(context, cl, pkgInfo.applicationInfo);
|
return newApp(pkgInfo.applicationInfo);
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||||
apk.delete();
|
apk.delete();
|
||||||
@ -95,65 +138,63 @@ public class DynLoad {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject and create Application, or setup redirections for the current app
|
// Stub app setup entry
|
||||||
static Application setup(Context context) {
|
static Application createAndSetupApp(Application context) {
|
||||||
Application app = inject(context);
|
// On API >= 29, AppComponentFactory will replace the ClassLoader
|
||||||
|
if (Build.VERSION.SDK_INT < 29)
|
||||||
|
replaceClassLoader(context);
|
||||||
|
|
||||||
|
Application app = createApp(context);
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
|
// Send real application to attachBaseContext
|
||||||
|
attachContext(app, context);
|
||||||
|
}
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassLoader cl = new RedirectClassLoader();
|
private static boolean isDynLoader() {
|
||||||
try {
|
return loader instanceof InjectedClassLoader;
|
||||||
setClassLoader(context, cl);
|
|
||||||
if (Build.VERSION.SDK_INT >= 28) {
|
|
||||||
((DelegateComponentFactory) componentFactory).loader = cl;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
private static Application newApp(ApplicationInfo info) throws ReflectiveOperationException {
|
||||||
}
|
|
||||||
|
|
||||||
private static Application createApp(Context context, ClassLoader cl, ApplicationInfo info)
|
|
||||||
throws ReflectiveOperationException {
|
|
||||||
// Create the receiver Application
|
// Create the receiver Application
|
||||||
Object app = cl.loadClass(info.className)
|
var app = (Application) loader.loadClass(info.className)
|
||||||
.getConstructor(Object.class)
|
.getConstructor(Object.class)
|
||||||
.newInstance(apkData.getObject());
|
.newInstance(apkData.getObject());
|
||||||
|
|
||||||
// Create the receiver component factory
|
// Create the receiver component factory
|
||||||
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
|
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
|
||||||
Object factory = cl.loadClass(info.appComponentFactory).newInstance();
|
Object factory = loader.loadClass(info.appComponentFactory).newInstance();
|
||||||
DelegateComponentFactory delegate = (DelegateComponentFactory) componentFactory;
|
var delegate = (DelegateComponentFactory) componentFactory;
|
||||||
delegate.loader = cl;
|
|
||||||
delegate.receiver = (AppComponentFactory) factory;
|
delegate.receiver = (AppComponentFactory) factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
setClassLoader(context, cl);
|
return app;
|
||||||
|
|
||||||
return (Application) app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace LoadedApk mClassLoader
|
// Replace LoadedApk mClassLoader
|
||||||
private static void setClassLoader(Context context, ClassLoader cl)
|
private static void replaceClassLoader(Context context) {
|
||||||
throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
// Get ContextImpl
|
// Get ContextImpl
|
||||||
while (context instanceof ContextWrapper) {
|
while (context instanceof ContextWrapper) {
|
||||||
context = ((ContextWrapper) context).getBaseContext();
|
context = ((ContextWrapper) context).getBaseContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
Field mInfo = context.getClass().getDeclaredField("mPackageInfo");
|
Field mInfo = context.getClass().getDeclaredField("mPackageInfo");
|
||||||
mInfo.setAccessible(true);
|
mInfo.setAccessible(true);
|
||||||
Object loadedApk = mInfo.get(context);
|
Object loadedApk = mInfo.get(context);
|
||||||
assert loadedApk != null;
|
|
||||||
Field mcl = loadedApk.getClass().getDeclaredField("mClassLoader");
|
Field mcl = loadedApk.getClass().getDeclaredField("mClassLoader");
|
||||||
mcl.setAccessible(true);
|
mcl.setAccessible(true);
|
||||||
mcl.set(loadedApk, cl);
|
mcl.set(loadedApk, new DelegateClassLoader());
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Actually impossible as this method is only called on API < 29,
|
||||||
|
// and API 21 - 28 do not restrict access to these methods/fields.
|
||||||
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DynAPK.Data createApkData() {
|
private static DynAPK.Data createApkData() {
|
||||||
DynAPK.Data data = new DynAPK.Data();
|
var data = new DynAPK.Data();
|
||||||
data.setVersion(BuildConfig.STUB_VERSION);
|
data.setVersion(BuildConfig.STUB_VERSION);
|
||||||
data.setClassToComponent(Mapping.inverseMap);
|
data.setClassToComponent(Mapping.inverseMap);
|
||||||
data.setRootService(DelegateRootService.class);
|
data.setRootService(DelegateRootService.class);
|
||||||
|
Loading…
Reference in New Issue
Block a user