mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-01-11 23:23:37 +00:00
parent
6b42db943d
commit
41a644afb9
@ -3,12 +3,13 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
val canary = !Config["appVersion"].orEmpty().contains(".")
|
val canary = !Config.appVersion.contains(".")
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.topjohnwu.magisk"
|
applicationId = "com.topjohnwu.magisk"
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = Config.appVersion
|
versionName = Config.appVersion
|
||||||
|
buildConfigField("int", "STUB_VERSION", "15")
|
||||||
buildConfigField("String", "DEV_CHANNEL", Config["DEV_CHANNEL"] ?: "null")
|
buildConfigField("String", "DEV_CHANNEL", Config["DEV_CHANNEL"] ?: "null")
|
||||||
buildConfigField("boolean", "CANARY", if (canary) "true" else "false")
|
buildConfigField("boolean", "CANARY", if (canary) "true" else "false")
|
||||||
}
|
}
|
||||||
|
@ -5,25 +5,82 @@
|
|||||||
package="com.topjohnwu.magisk">
|
package="com.topjohnwu.magisk">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="29" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:appComponentFactory=".DelegateComponentFactory"
|
||||||
tools:ignore="AllowBackup">
|
android:name="a.e"
|
||||||
|
android:allowBackup="false"
|
||||||
|
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning" >
|
||||||
|
|
||||||
<activity android:name=".MainActivity">
|
<!-- Splash -->
|
||||||
|
<activity
|
||||||
|
android:name="a.c">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!-- Main -->
|
||||||
|
<activity android:name="a.b" />
|
||||||
|
|
||||||
|
<!-- Superuser -->
|
||||||
|
<activity
|
||||||
|
android:name="a.m"
|
||||||
|
android:directBootAware="true"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="false"
|
||||||
|
tools:ignore="AppLinkUrlError">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<!-- Receiver -->
|
||||||
|
<receiver
|
||||||
|
android:name="a.h"
|
||||||
|
android:directBootAware="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.REBOOT" />
|
||||||
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
|
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||||
|
|
||||||
|
<data android:scheme="package" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<!-- DownloadService -->
|
||||||
|
<service android:name="a.j" />
|
||||||
|
|
||||||
|
<!-- FileProvider -->
|
||||||
<provider
|
<provider
|
||||||
android:name=".FileProvider"
|
android:name="a.p"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
|
android:directBootAware="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<!-- Hardcode GMS version -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.version"
|
||||||
|
android:value="12451000" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
6
stub/src/main/java/a/c.java
Normal file
6
stub/src/main/java/a/c.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.DownloadActivity;
|
||||||
|
|
||||||
|
public class c extends DownloadActivity {
|
||||||
|
}
|
6
stub/src/main/java/a/e.java
Normal file
6
stub/src/main/java/a/e.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.DelegateApplication;
|
||||||
|
|
||||||
|
public class e extends DelegateApplication {
|
||||||
|
}
|
6
stub/src/main/java/a/h.java
Normal file
6
stub/src/main/java/a/h.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyReceiver;
|
||||||
|
|
||||||
|
public class h extends DummyReceiver {
|
||||||
|
}
|
6
stub/src/main/java/a/p.java
Normal file
6
stub/src/main/java/a/p.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.FileProvider;
|
||||||
|
|
||||||
|
public class p extends FileProvider {
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class DelegateApplication extends Application {
|
||||||
|
|
||||||
|
private Application delegate;
|
||||||
|
static boolean dynLoad = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void attachBaseContext(Context base) {
|
||||||
|
super.attachBaseContext(base);
|
||||||
|
|
||||||
|
// Only dynamic load full APK if hidden and possible
|
||||||
|
dynLoad = Build.VERSION.SDK_INT >= 28 &&
|
||||||
|
!base.getPackageName().equals(BuildConfig.APPLICATION_ID);
|
||||||
|
if (!dynLoad)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delegate = InjectAPK.setup(this);
|
||||||
|
if (delegate != 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(delegate, this);
|
||||||
|
} catch (Exception ignored) { /* Impossible */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
if (delegate != null)
|
||||||
|
delegate.onConfigurationChanged(newConfig);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AppComponentFactory;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyProvider;
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyReceiver;
|
||||||
|
import com.topjohnwu.magisk.dummy.DummyService;
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public class DelegateComponentFactory extends AppComponentFactory {
|
||||||
|
|
||||||
|
ClassLoader loader;
|
||||||
|
AppComponentFactory delegate;
|
||||||
|
|
||||||
|
interface DummyFactory<T> {
|
||||||
|
T create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DelegateComponentFactory() {
|
||||||
|
InjectAPK.factory = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Application instantiateApplication(ClassLoader cl, String className) {
|
||||||
|
if (loader == null) loader = cl;
|
||||||
|
return new DelegateApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Activity instantiateActivity(ClassLoader cl, String className, Intent intent)
|
||||||
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.instantiateActivity(loader, className, intent);
|
||||||
|
return create(className, DownloadActivity::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent)
|
||||||
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.instantiateReceiver(loader, className, intent);
|
||||||
|
return create(className, DummyReceiver::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Service instantiateService(ClassLoader cl, String className, Intent intent)
|
||||||
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.instantiateService(loader, className, intent);
|
||||||
|
return create(className, DummyService::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentProvider instantiateProvider(ClassLoader cl, String className)
|
||||||
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
|
if (loader == null) loader = cl;
|
||||||
|
if (delegate != null)
|
||||||
|
return delegate.instantiateProvider(loader, className);
|
||||||
|
return create(className, DummyProvider::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the class or dummy implementation if creation failed
|
||||||
|
*/
|
||||||
|
private <T> T create(String name, DummyFactory<T> factory) {
|
||||||
|
try {
|
||||||
|
return (T) loader.loadClass(name).newInstance();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return factory.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,11 @@ import android.app.Activity;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.net.Networking;
|
import com.topjohnwu.magisk.net.Networking;
|
||||||
import com.topjohnwu.magisk.net.Request;
|
import com.topjohnwu.magisk.net.Request;
|
||||||
@ -20,11 +22,13 @@ import java.io.File;
|
|||||||
import static android.R.string.no;
|
import static android.R.string.no;
|
||||||
import static android.R.string.ok;
|
import static android.R.string.ok;
|
||||||
import static android.R.string.yes;
|
import static android.R.string.yes;
|
||||||
|
import static com.topjohnwu.magisk.DelegateApplication.dynLoad;
|
||||||
import static com.topjohnwu.magisk.R.string.dling;
|
import static com.topjohnwu.magisk.R.string.dling;
|
||||||
import static com.topjohnwu.magisk.R.string.no_internet_msg;
|
import static com.topjohnwu.magisk.R.string.no_internet_msg;
|
||||||
|
import static com.topjohnwu.magisk.R.string.relaunch_app;
|
||||||
import static com.topjohnwu.magisk.R.string.upgrade_msg;
|
import static com.topjohnwu.magisk.R.string.upgrade_msg;
|
||||||
|
|
||||||
public class MainActivity extends Activity {
|
public class DownloadActivity extends Activity {
|
||||||
|
|
||||||
private static final String APP_NAME = "Magisk Manager";
|
private static final String APP_NAME = "Magisk Manager";
|
||||||
private static final String CDN_URL = "https://cdn.jsdelivr.net/gh/topjohnwu/magisk_files@%s/%s";
|
private static final String CDN_URL = "https://cdn.jsdelivr.net/gh/topjohnwu/magisk_files@%s/%s";
|
||||||
@ -107,11 +111,23 @@ public class MainActivity extends Activity {
|
|||||||
private void dlAPK() {
|
private void dlAPK() {
|
||||||
dialog = ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true);
|
dialog = ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true);
|
||||||
// Download and upgrade the app
|
// Download and upgrade the app
|
||||||
File apk = new File(getCacheDir(), "manager.apk");
|
File apk = dynLoad ? DynAPK.current(this) : new File(getCacheDir(), "manager.apk");
|
||||||
request(apkLink).getAsFile(apk, file -> {
|
request(apkLink).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR).getAsFile(apk, file -> {
|
||||||
dialog.dismiss();
|
if (dynLoad) {
|
||||||
APKInstall.install(this, file);
|
InjectAPK.setup(this);
|
||||||
finish();
|
runOnUiThread(() -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
Toast.makeText(themed, relaunch_app, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
APKInstall.install(this, file);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
78
stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java
Normal file
78
stub/src/main/java/com/topjohnwu/magisk/InjectAPK.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.app.AppComponentFactory;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.utils.DynamicClassLoader;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class InjectAPK {
|
||||||
|
|
||||||
|
static DelegateComponentFactory factory;
|
||||||
|
|
||||||
|
static Application setup(Context context) {
|
||||||
|
File apk = DynAPK.current(context);
|
||||||
|
File update = DynAPK.update(context);
|
||||||
|
if (update.exists())
|
||||||
|
update.renameTo(apk);
|
||||||
|
Application delegate = 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 (OutputStream out = new FileOutputStream(apk)) {
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
for (int read; (read = is.read(buf)) >= 0;) {
|
||||||
|
out.write(buf, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (apk.exists()) {
|
||||||
|
ClassLoader cl = new DynamicClassLoader(apk, factory.loader);
|
||||||
|
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)
|
||||||
|
.newInstance(DynAPK.pack(dynData()));
|
||||||
|
|
||||||
|
// If everything went well, set our loader and delegate
|
||||||
|
factory.delegate = df;
|
||||||
|
factory.loader = cl;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(InjectAPK.class.getSimpleName(), "", e);
|
||||||
|
apk.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DynAPK.Data dynData() {
|
||||||
|
DynAPK.Data data = new DynAPK.Data();
|
||||||
|
data.version = BuildConfig.STUB_VERSION;
|
||||||
|
// Public source code does not do component name obfuscation
|
||||||
|
data.classToComponent = new HashMap<>();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.topjohnwu.magisk.dummy;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class DummyActivity extends Activity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.topjohnwu.magisk.dummy;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
public class DummyProvider extends ContentProvider {
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(Uri uri) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.topjohnwu.magisk.dummy;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
public class DummyReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.topjohnwu.magisk.dummy;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public class DummyService extends Service {
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
stopSelf();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user