You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Support stub APK loading down to Android 5.0
This commit is contained in:
@@ -4,65 +4,97 @@ import android.app.AppComponentFactory;
|
||||
import android.app.Application;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.topjohnwu.magisk.utils.DynamicClassLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class InjectAPK {
|
||||
|
||||
static DelegateComponentFactory factory;
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
static Application setup(Context context) {
|
||||
// Get ContextImpl
|
||||
while (context instanceof ContextWrapper) {
|
||||
context = ((ContextWrapper) context).getBaseContext();
|
||||
}
|
||||
|
||||
File apk = DynAPK.current(context);
|
||||
File update = DynAPK.update(context);
|
||||
if (update.exists())
|
||||
update.renameTo(apk);
|
||||
Application delegate = null;
|
||||
Application result = null;
|
||||
if (!apk.exists()) {
|
||||
// Try copying APK
|
||||
Uri uri = new Uri.Builder().scheme("content")
|
||||
.authority("com.topjohnwu.magisk.provider")
|
||||
.encodedPath("apk_file").build();
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
try (InputStream is = resolver.openInputStream(uri)) {
|
||||
if (is != null) {
|
||||
try (InputStream src = resolver.openInputStream(uri)) {
|
||||
if (src != null) {
|
||||
try (OutputStream out = new FileOutputStream(apk)) {
|
||||
byte[] buf = new byte[4096];
|
||||
for (int read; (read = is.read(buf)) >= 0;) {
|
||||
for (int read; (read = src.read(buf)) >= 0;) {
|
||||
out.write(buf, 0, read);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
if (apk.exists()) {
|
||||
ClassLoader cl = new DynamicClassLoader(apk, factory.loader);
|
||||
ClassLoader cl = new InjectedClassLoader(apk);
|
||||
try {
|
||||
// Create the delegate AppComponentFactory
|
||||
AppComponentFactory df = (AppComponentFactory)
|
||||
cl.loadClass("androidx.core.app.CoreComponentFactory").newInstance();
|
||||
|
||||
// Create the delegate Application
|
||||
delegate = (Application) cl.loadClass("a.e").getConstructor(Object.class)
|
||||
// Create the receiver Application
|
||||
Object app = cl.loadClass(Mapping.get("APP")).getConstructor(Object.class)
|
||||
.newInstance(DynAPK.pack(dynData()));
|
||||
|
||||
// If everything went well, set our loader and delegate
|
||||
factory.delegate = df;
|
||||
factory.loader = cl;
|
||||
// Create the receiver component factory
|
||||
Object factory = null;
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
factory = cl.loadClass(Mapping.get("ACF")).newInstance();
|
||||
}
|
||||
|
||||
setClassLoader(context, cl);
|
||||
|
||||
// Finally, set variables
|
||||
result = (Application) app;
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
DelegateComponentFactory.INSTANCE.loader = cl;
|
||||
DelegateComponentFactory.INSTANCE.receiver = (AppComponentFactory) factory;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||
apk.delete();
|
||||
}
|
||||
} else {
|
||||
ClassLoader cl = new RedirectClassLoader();
|
||||
try {
|
||||
setClassLoader(context, cl);
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
DelegateComponentFactory.INSTANCE.loader = cl;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Should never happen
|
||||
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||
}
|
||||
}
|
||||
return delegate;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Replace LoadedApk mClassLoader
|
||||
private static void setClassLoader(Context impl, ClassLoader cl)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
Field mInfo = impl.getClass().getDeclaredField("mPackageInfo");
|
||||
mInfo.setAccessible(true);
|
||||
Object loadedApk = mInfo.get(impl);
|
||||
Field mcl = loadedApk.getClass().getDeclaredField("mClassLoader");
|
||||
mcl.setAccessible(true);
|
||||
mcl.set(loadedApk, cl);
|
||||
}
|
||||
|
||||
private static DynAPK.Data dynData() {
|
||||
|
||||
Reference in New Issue
Block a user