2020-12-29 01:44:02 -08:00
|
|
|
package com.topjohnwu.magisk;
|
|
|
|
|
|
|
|
import android.app.AppComponentFactory;
|
|
|
|
import android.app.Application;
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
import android.content.Context;
|
2021-01-26 03:40:25 -08:00
|
|
|
import android.content.ContextWrapper;
|
2021-04-17 22:14:54 -07:00
|
|
|
import android.content.pm.ApplicationInfo;
|
2021-11-25 13:13:30 +08:00
|
|
|
import android.content.pm.PackageInfo;
|
2021-04-17 22:14:54 -07:00
|
|
|
import android.content.pm.PackageManager;
|
2020-12-29 01:44:02 -08:00
|
|
|
import android.net.Uri;
|
2021-01-26 03:40:25 -08:00
|
|
|
import android.os.Build;
|
2020-12-29 01:44:02 -08:00
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import java.io.File;
|
2021-12-12 23:52:59 -08:00
|
|
|
import java.io.FileInputStream;
|
2020-12-29 01:44:02 -08:00
|
|
|
import java.io.FileOutputStream;
|
2021-12-12 23:52:59 -08:00
|
|
|
import java.io.IOException;
|
2020-12-29 01:44:02 -08:00
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
2021-01-26 03:40:25 -08:00
|
|
|
import java.lang.reflect.Field;
|
2020-12-29 01:44:02 -08:00
|
|
|
|
2021-09-02 21:31:33 -07:00
|
|
|
import io.michaelrocks.paranoid.Obfuscate;
|
|
|
|
|
|
|
|
@Obfuscate
|
2021-12-13 03:57:27 -08:00
|
|
|
public class DynLoad {
|
2020-12-29 01:44:02 -08:00
|
|
|
|
2021-04-17 22:14:54 -07:00
|
|
|
static Object componentFactory;
|
2021-12-13 03:57:27 -08:00
|
|
|
static final DynAPK.Data apkData = createApkData();
|
2021-04-17 22:14:54 -07:00
|
|
|
|
2021-12-12 23:52:59 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 03:57:27 -08:00
|
|
|
// Dynamically load APK, inject ClassLoader into ContextImpl, then
|
|
|
|
// create the actual Application instance from the loaded APK
|
|
|
|
static Application inject(Context context) {
|
2020-12-29 01:44:02 -08:00
|
|
|
File apk = DynAPK.current(context);
|
|
|
|
File update = DynAPK.update(context);
|
2021-12-12 23:52:59 -08:00
|
|
|
|
|
|
|
if (update.exists()) {
|
|
|
|
// Rename from update
|
2020-12-29 01:44:02 -08:00
|
|
|
update.renameTo(apk);
|
2021-12-12 23:52:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2021-12-13 03:57:27 -08:00
|
|
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
2021-12-12 23:52:59 -08:00
|
|
|
apk.delete();
|
|
|
|
} finally {
|
|
|
|
external.delete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!apk.exists() && !context.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
|
|
|
// Copy from previous app
|
2020-12-29 01:44:02 -08:00
|
|
|
Uri uri = new Uri.Builder().scheme("content")
|
|
|
|
.authority("com.topjohnwu.magisk.provider")
|
|
|
|
.encodedPath("apk_file").build();
|
|
|
|
ContentResolver resolver = context.getContentResolver();
|
2021-12-12 23:52:59 -08:00
|
|
|
try {
|
|
|
|
InputStream src = resolver.openInputStream(uri);
|
2021-01-26 03:40:25 -08:00
|
|
|
if (src != null) {
|
2021-12-12 23:52:59 -08:00
|
|
|
copy(src, new FileOutputStream(apk));
|
2020-12-29 01:44:02 -08:00
|
|
|
}
|
2021-12-12 23:52:59 -08:00
|
|
|
} catch (IOException e) {
|
2021-12-13 03:57:27 -08:00
|
|
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
2021-12-12 23:52:59 -08:00
|
|
|
apk.delete();
|
|
|
|
}
|
2020-12-29 01:44:02 -08:00
|
|
|
}
|
2021-12-12 23:52:59 -08:00
|
|
|
|
2020-12-29 01:44:02 -08:00
|
|
|
if (apk.exists()) {
|
2021-01-26 03:40:25 -08:00
|
|
|
ClassLoader cl = new InjectedClassLoader(apk);
|
2021-04-17 22:14:54 -07:00
|
|
|
PackageManager pm = context.getPackageManager();
|
2021-11-25 13:13:30 +08:00
|
|
|
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), 0);
|
2020-12-29 01:44:02 -08:00
|
|
|
try {
|
2021-11-25 13:13:30 +08:00
|
|
|
return createApp(context, cl, pkgInfo.applicationInfo);
|
2021-12-13 03:57:27 -08:00
|
|
|
} catch (ReflectiveOperationException e) {
|
|
|
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
2020-12-29 01:44:02 -08:00
|
|
|
apk.delete();
|
|
|
|
}
|
2021-12-13 03:57:27 -08:00
|
|
|
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inject and create Application, or setup redirections for the current app
|
|
|
|
static Application setup(Context context) {
|
|
|
|
Application app = inject(context);
|
|
|
|
if (app != null) {
|
|
|
|
return app;
|
2021-04-17 22:14:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ClassLoader cl = new RedirectClassLoader();
|
|
|
|
try {
|
|
|
|
setClassLoader(context, cl);
|
|
|
|
if (Build.VERSION.SDK_INT >= 28) {
|
2021-12-13 03:57:27 -08:00
|
|
|
((DelegateComponentFactory) componentFactory).loader = cl;
|
2021-01-26 03:40:25 -08:00
|
|
|
}
|
2021-04-17 22:14:54 -07:00
|
|
|
} catch (Exception e) {
|
2021-12-13 03:57:27 -08:00
|
|
|
Log.e(DynLoad.class.getSimpleName(), "", e);
|
2020-12-29 01:44:02 -08:00
|
|
|
}
|
2021-04-17 22:14:54 -07:00
|
|
|
|
|
|
|
return null;
|
2021-01-26 03:40:25 -08:00
|
|
|
}
|
|
|
|
|
2021-11-25 13:13:30 +08:00
|
|
|
private static Application createApp(Context context, ClassLoader cl, ApplicationInfo info)
|
|
|
|
throws ReflectiveOperationException {
|
|
|
|
// Create the receiver Application
|
|
|
|
Object app = cl.loadClass(info.className)
|
|
|
|
.getConstructor(Object.class)
|
2021-12-13 03:57:27 -08:00
|
|
|
.newInstance(apkData.getObject());
|
2021-11-25 13:13:30 +08:00
|
|
|
|
|
|
|
// Create the receiver component factory
|
2021-12-13 03:57:27 -08:00
|
|
|
if (Build.VERSION.SDK_INT >= 28 && componentFactory != null) {
|
|
|
|
Object factory = cl.loadClass(info.appComponentFactory).newInstance();
|
|
|
|
DelegateComponentFactory delegate = (DelegateComponentFactory) componentFactory;
|
|
|
|
delegate.loader = cl;
|
|
|
|
delegate.receiver = (AppComponentFactory) factory;
|
2021-11-25 13:13:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
setClassLoader(context, cl);
|
|
|
|
|
|
|
|
return (Application) app;
|
|
|
|
}
|
|
|
|
|
2021-01-26 03:40:25 -08:00
|
|
|
// Replace LoadedApk mClassLoader
|
2021-12-13 03:57:27 -08:00
|
|
|
private static void setClassLoader(Context context, ClassLoader cl)
|
2021-01-26 03:40:25 -08:00
|
|
|
throws NoSuchFieldException, IllegalAccessException {
|
2021-12-13 03:57:27 -08:00
|
|
|
// Get ContextImpl
|
|
|
|
while (context instanceof ContextWrapper) {
|
|
|
|
context = ((ContextWrapper) context).getBaseContext();
|
|
|
|
}
|
|
|
|
|
|
|
|
Field mInfo = context.getClass().getDeclaredField("mPackageInfo");
|
2021-01-26 03:40:25 -08:00
|
|
|
mInfo.setAccessible(true);
|
2021-12-13 03:57:27 -08:00
|
|
|
Object loadedApk = mInfo.get(context);
|
2021-01-26 03:40:25 -08:00
|
|
|
Field mcl = loadedApk.getClass().getDeclaredField("mClassLoader");
|
|
|
|
mcl.setAccessible(true);
|
|
|
|
mcl.set(loadedApk, cl);
|
2020-12-29 01:44:02 -08:00
|
|
|
}
|
|
|
|
|
2021-12-13 03:57:27 -08:00
|
|
|
private static DynAPK.Data createApkData() {
|
2020-12-29 01:44:02 -08:00
|
|
|
DynAPK.Data data = new DynAPK.Data();
|
2021-12-13 03:57:27 -08:00
|
|
|
data.setVersion(BuildConfig.STUB_VERSION);
|
|
|
|
data.setClassToComponent(Mapping.inverseMap);
|
|
|
|
data.setRootService(DelegateRootService.class);
|
2020-12-29 01:44:02 -08:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|