Repo created
This commit is contained in:
parent
d327c31227
commit
0b2aca0925
638 changed files with 76461 additions and 0 deletions
1
app/shared/.gitignore
vendored
Normal file
1
app/shared/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
9
app/shared/build.gradle.kts
Normal file
9
app/shared/build.gradle.kts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
plugins {
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
setupCommon()
|
||||
|
||||
android {
|
||||
namespace = "com.topjohnwu.shared"
|
||||
}
|
||||
8
app/shared/src/debug/AndroidManifest.xml
Normal file
8
app/shared/src/debug/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
</manifest>
|
||||
34
app/shared/src/main/AndroidManifest.xml
Normal file
34
app/shared/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS" />
|
||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" />
|
||||
<uses-permission
|
||||
android:name="android.permission.FOREGROUND_SERVICE"
|
||||
android:maxSdkVersion="33" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:label="Magisk"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
||||
public class ProviderInstaller {
|
||||
|
||||
private static final String GMS_PACKAGE_NAME = "com.google.android.gms";
|
||||
|
||||
public static void install(Context context) {
|
||||
try {
|
||||
// Check if gms is a system app
|
||||
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try installing new SSL provider from Google Play Service
|
||||
Context gms = context.createPackageContext(GMS_PACKAGE_NAME,
|
||||
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
|
||||
gms.getClassLoader()
|
||||
.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
|
||||
.getMethod("insertProvider", Context.class)
|
||||
.invoke(null, gms);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
120
app/shared/src/main/java/com/topjohnwu/magisk/StubApk.java
Normal file
120
app/shared/src/main/java/com/topjohnwu/magisk/StubApk.java
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package com.topjohnwu.magisk;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
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.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
public class StubApk {
|
||||
private static File dynDir;
|
||||
private static Method addAssetPath;
|
||||
|
||||
private static File getDynDir(ApplicationInfo info) {
|
||||
if (dynDir == null) {
|
||||
final String dataDir;
|
||||
if (SDK_INT >= Build.VERSION_CODES.N) {
|
||||
// Use device protected path to allow directBootAware
|
||||
dataDir = info.deviceProtectedDataDir;
|
||||
} else {
|
||||
dataDir = info.dataDir;
|
||||
}
|
||||
dynDir = new File(dataDir, "dyn");
|
||||
dynDir.mkdirs();
|
||||
}
|
||||
return dynDir;
|
||||
}
|
||||
|
||||
public static File current(Context c) {
|
||||
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) {
|
||||
return new File(getDynDir(c.getApplicationInfo()), "update.apk");
|
||||
}
|
||||
|
||||
public static File update(ApplicationInfo info) {
|
||||
return new File(getDynDir(info), "update.apk");
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
private static ResourcesLoader getResourcesLoader(File path) throws IOException {
|
||||
var loader = new ResourcesLoader();
|
||||
ResourcesProvider provider;
|
||||
if (path.isDirectory()) {
|
||||
provider = ResourcesProvider.loadFromDirectory(path.getPath(), null);
|
||||
} else {
|
||||
var fd = ParcelFileDescriptor.open(path, MODE_READ_ONLY);
|
||||
provider = ResourcesProvider.loadFromApk(fd);
|
||||
}
|
||||
loader.addProvider(provider);
|
||||
return loader;
|
||||
}
|
||||
|
||||
public static void addAssetPath(Resources res, String path) {
|
||||
if (SDK_INT >= Build.VERSION_CODES.R) {
|
||||
try {
|
||||
res.addLoaders(getResourcesLoader(new File(path)));
|
||||
} 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) {
|
||||
Intent intent = activity.getPackageManager()
|
||||
.getLaunchIntentForPackage(activity.getPackageName());
|
||||
activity.finishAffinity();
|
||||
activity.startActivity(intent);
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
|
||||
public static class Data {
|
||||
// Indices of the object array
|
||||
private static final int STUB_VERSION = 0;
|
||||
private static final int CLASS_COMPONENT_MAP = 1;
|
||||
private static final int ROOT_SERVICE = 2;
|
||||
private static final int ARR_SIZE = 3;
|
||||
|
||||
private final Object[] arr;
|
||||
|
||||
public Data() { arr = new Object[ARR_SIZE]; }
|
||||
public Data(Object o) { arr = (Object[]) o; }
|
||||
public Object getObject() { return arr; }
|
||||
|
||||
public int getVersion() { return (int) arr[STUB_VERSION]; }
|
||||
public void setVersion(int version) { arr[STUB_VERSION] = version; }
|
||||
public Map<String, String> getClassToComponent() {
|
||||
// noinspection unchecked
|
||||
return (Map<String, String>) arr[CLASS_COMPONENT_MAP];
|
||||
}
|
||||
public void setClassToComponent(Map<String, String> map) {
|
||||
arr[CLASS_COMPONENT_MAP] = map;
|
||||
}
|
||||
public Class<?> getRootService() { return (Class<?>) arr[ROOT_SERVICE]; }
|
||||
public void setRootService(Class<?> service) { arr[ROOT_SERVICE] = service; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import static android.content.pm.PackageInstaller.EXTRA_SESSION_ID;
|
||||
import static android.content.pm.PackageInstaller.EXTRA_STATUS;
|
||||
import static android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID;
|
||||
import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
|
||||
import static android.content.pm.PackageInstaller.STATUS_SUCCESS;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class APKInstall {
|
||||
|
||||
public static void transfer(InputStream in, OutputStream out) throws IOException {
|
||||
int size = 8192;
|
||||
var buffer = new byte[size];
|
||||
int read;
|
||||
while ((read = in.read(buffer, 0, size)) >= 0) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerReceiver(
|
||||
Context context, BroadcastReceiver receiver, IntentFilter filter) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// noinspection InlinedApi
|
||||
context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED);
|
||||
} else {
|
||||
context.registerReceiver(receiver, filter);
|
||||
}
|
||||
}
|
||||
|
||||
public static Session startSession(Context context) {
|
||||
return startSession(context, null, null, null);
|
||||
}
|
||||
|
||||
public static Session startSession(Context context, String pkg,
|
||||
Runnable onFailure, Runnable onSuccess) {
|
||||
var receiver = new InstallReceiver(pkg, onSuccess, onFailure);
|
||||
context = context.getApplicationContext();
|
||||
if (pkg != null) {
|
||||
// If pkg is not null, look for package added event
|
||||
var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addDataScheme("package");
|
||||
registerReceiver(context, receiver, filter);
|
||||
}
|
||||
registerReceiver(context, receiver, new IntentFilter(receiver.sessionId));
|
||||
return receiver;
|
||||
}
|
||||
|
||||
public interface Session {
|
||||
// @WorkerThread
|
||||
OutputStream openStream(Context context) throws IOException;
|
||||
// @WorkerThread @Nullable
|
||||
Intent waitIntent();
|
||||
}
|
||||
|
||||
private static class InstallReceiver extends BroadcastReceiver implements Session {
|
||||
private final String packageName;
|
||||
private final Runnable onSuccess;
|
||||
private final Runnable onFailure;
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private Intent userAction = null;
|
||||
|
||||
final String sessionId = UUID.randomUUID().toString();
|
||||
|
||||
private InstallReceiver(String packageName, Runnable onSuccess, Runnable onFailure) {
|
||||
this.packageName = packageName;
|
||||
this.onSuccess = onSuccess;
|
||||
this.onFailure = onFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
|
||||
Uri data = intent.getData();
|
||||
if (data == null)
|
||||
return;
|
||||
String pkg = data.getSchemeSpecificPart();
|
||||
if (pkg.equals(packageName)) {
|
||||
onSuccess(context);
|
||||
}
|
||||
} else if (sessionId.equals(intent.getAction())) {
|
||||
int status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID);
|
||||
switch (status) {
|
||||
case STATUS_PENDING_USER_ACTION ->
|
||||
userAction = intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
case STATUS_SUCCESS -> {
|
||||
if (packageName == null) {
|
||||
onSuccess(context);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
int id = intent.getIntExtra(EXTRA_SESSION_ID, 0);
|
||||
var installer = context.getPackageManager().getPackageInstaller();
|
||||
try {
|
||||
installer.abandonSession(id);
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
if (onFailure != null) {
|
||||
onFailure.run();
|
||||
}
|
||||
try {
|
||||
context.getApplicationContext().unregisterReceiver(this);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccess(Context context) {
|
||||
if (onSuccess != null)
|
||||
onSuccess.run();
|
||||
try {
|
||||
context.getApplicationContext().unregisterReceiver(this);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent waitIntent() {
|
||||
try {
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
} catch (Exception ignored) {}
|
||||
return userAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openStream(Context context) throws IOException {
|
||||
// noinspection InlinedApi
|
||||
var flag = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
|
||||
var intent = new Intent(sessionId).setPackage(context.getPackageName());
|
||||
var pending = PendingIntent.getBroadcast(context, 0, intent, flag);
|
||||
|
||||
var installer = context.getPackageManager().getPackageInstaller();
|
||||
var params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
params.setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED);
|
||||
}
|
||||
var session = installer.openSession(installer.createSession(params));
|
||||
var out = session.openWrite(sessionId, 0, -1);
|
||||
return new FilterOutputStream(out) {
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
out.write(b, off, len);
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
session.commit(pending.getIntentSender());
|
||||
session.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class CompoundEnumeration<E> implements Enumeration<E> {
|
||||
private Enumeration<E>[] enums;
|
||||
private int index = 0;
|
||||
|
||||
@SafeVarargs
|
||||
public CompoundEnumeration(Enumeration<E> ...enums) {
|
||||
this.enums = enums;
|
||||
}
|
||||
|
||||
private boolean next() {
|
||||
while (index < enums.length) {
|
||||
if (enums[index] != null && enums[index].hasMoreElements()) {
|
||||
return true;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return next();
|
||||
}
|
||||
|
||||
public E nextElement() {
|
||||
if (!next()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return enums[index].nextElement();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.os.Process;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import dalvik.system.BaseDexClassLoader;
|
||||
|
||||
public class DynamicClassLoader extends BaseDexClassLoader {
|
||||
|
||||
public DynamicClassLoader(File apk) {
|
||||
this(apk, DynamicClassLoader.class.getClassLoader());
|
||||
}
|
||||
|
||||
public DynamicClassLoader(File apk, ClassLoader parent) {
|
||||
// Set optimizedDirectory to null for RootService to bypass DexFile's security checks
|
||||
super(apk.getPath(), Process.myUid() == 0 ? null : apk.getParentFile(), null, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
// First check if already loaded
|
||||
Class<?> cls = findLoadedClass(name);
|
||||
if (cls != null)
|
||||
return cls;
|
||||
|
||||
try {
|
||||
// Then check boot classpath
|
||||
return getSystemClassLoader().loadClass(name);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
try {
|
||||
// Next try current dex
|
||||
return findClass(name);
|
||||
} catch (ClassNotFoundException fromSuper) {
|
||||
try {
|
||||
// Finally try parent
|
||||
return getParent().loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw fromSuper;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
URL resource = getSystemClassLoader().getResource(name);
|
||||
if (resource != null)
|
||||
return resource;
|
||||
resource = findResource(name);
|
||||
if (resource != null)
|
||||
return resource;
|
||||
resource = getParent().getResource(name);
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
return new CompoundEnumeration<>(getSystemClassLoader().getResources(name),
|
||||
findResources(name), getParent().getResources(name));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue