Proper app relaunch for stub

This commit is contained in:
topjohnwu 2022-02-02 04:44:22 -08:00
parent f4cf5dc0cd
commit c1fc3f373c
5 changed files with 68 additions and 26 deletions

View File

@ -0,0 +1,37 @@
package com.topjohnwu.magisk;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Process;
import io.michaelrocks.paranoid.Obfuscate;
// Inspired by https://github.com/JakeWharton/ProcessPhoenix
@Obfuscate
public class PhoenixActivity extends Activity {
private static final String PID_KEY = "pid";
public static void rebirth(Context context, String clzName) {
var intent = new Intent();
intent.setComponent(new ComponentName(context, clzName));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(PID_KEY, Process.myPid());
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Process.killProcess(getIntent().getIntExtra(PID_KEY, -1));
var intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
Runtime.getRuntime().exit(0);
}
}

View File

@ -1,13 +1,9 @@
package com.topjohnwu.magisk.core.download package com.topjohnwu.magisk.core.download
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Intent
import androidx.core.content.getSystemService
import androidx.core.net.toFile import androidx.core.net.toFile
import com.topjohnwu.magisk.PhoenixActivity
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.StubApk import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.ActivityTracker
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.tasks.HideAPK import com.topjohnwu.magisk.core.tasks.HideAPK
@ -59,17 +55,8 @@ suspend fun DownloadService.handleAPK(subject: Subject.Manager, stream: InputStr
apk.delete() apk.delete()
patched.renameTo(apk) patched.renameTo(apk)
} else { } else {
val intent = packageManager.getLaunchIntentForPackage(packageName) val clz = Info.stub!!.classToComponent["PHOENIX"]!!
intent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) PhoenixActivity.rebirth(this, clz)
//noinspection InlinedApi
val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
val pending = PendingIntent.getActivity(this, id, intent, flag)
if (ActivityTracker.hasForeground) {
val alarm = getSystemService<AlarmManager>()
alarm!!.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pending)
}
stopSelf()
Runtime.getRuntime().exit(0)
} }
} else { } else {
write(subject.file.outputStream()) write(subject.file.outputStream())

View File

@ -77,7 +77,8 @@ fun genStubManifest(srcDir: File, outDir: File): String {
class Component( class Component(
val real: String, val real: String,
val stub: String, val stub: String,
val xml: String val xml: String,
val genClass: Boolean = false
) )
outDir.deleteRecursively() outDir.deleteRecursively()
@ -92,13 +93,26 @@ fun genStubManifest(srcDir: File, outDir: File): String {
cmpList.add(Component( cmpList.add(Component(
"androidx.core.app.CoreComponentFactory", "androidx.core.app.CoreComponentFactory",
"DelegateComponentFactory", "DelegateComponentFactory",
"" "",
true
)) ))
cmpList.add(Component( cmpList.add(Component(
"com.topjohnwu.magisk.core.App", "com.topjohnwu.magisk.core.App",
"DelegateApplication", "DelegateApplication",
"" "",
true
))
cmpList.add(Component(
"PHOENIX",
"PhoenixActivity",
"""
|<activity
| android:name="%s"
| android:process=":%s"
| android:exported="false" />""".ind(2),
true
)) ))
cmpList.add(Component( cmpList.add(Component(
@ -236,12 +250,12 @@ fun genStubManifest(srcDir: File, outDir: File): String {
maps.append("|internalMap.put(\"$name\", com.topjohnwu.magisk.${gen.stub}.class);".ind(2)) maps.append("|internalMap.put(\"$name\", com.topjohnwu.magisk.${gen.stub}.class);".ind(2))
maps.append('\n') maps.append('\n')
} }
if (gen.stub.startsWith("Delegate")) { if (gen.genClass) {
genClass(name, gen.stub) genClass(name, gen.stub)
} }
} }
if (gen.xml.isNotEmpty()) { if (gen.xml.isNotEmpty()) {
cmps.add(gen.xml.format(name)) cmps.add(gen.xml.format(name, names.random(kRANDOM)))
} }
} }

View File

@ -8,13 +8,19 @@ import java.io.File;
// mapping when loading from platform (via LoadedApk.mClassLoader) // mapping when loading from platform (via LoadedApk.mClassLoader)
class InjectedClassLoader extends ClassLoader { class InjectedClassLoader extends ClassLoader {
private static final String PKEY = "PHOENIX";
static final String PHOENIX = Mapping.inverseMap.get(PKEY);
InjectedClassLoader(File apk) { InjectedClassLoader(File apk) {
super(new DynamicClassLoader(apk)); super(new DynamicClassLoader(apk));
} }
@Override @Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
return super.loadClass(Mapping.get(name), resolve); String n = Mapping.get(name);
if (n.equals(PKEY))
return PhoenixActivity.class;
return super.loadClass(n, resolve);
} }
} }

View File

@ -5,7 +5,6 @@ import static android.R.string.ok;
import static android.R.string.yes; import static android.R.string.yes;
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;
import android.app.Activity; import android.app.Activity;
@ -17,7 +16,6 @@ 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;
@ -111,14 +109,14 @@ public class DownloadActivity extends Activity {
dialog = ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true); dialog = ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true);
Runnable onSuccess = () -> { Runnable onSuccess = () -> {
dialog.dismiss(); dialog.dismiss();
Toast.makeText(themed, relaunch_app, Toast.LENGTH_LONG).show(); PhoenixActivity.rebirth(this, InjectedClassLoader.PHOENIX);
finish(); finish();
}; };
// Download and upgrade the app // Download and upgrade the app
File apk = dynLoad ? StubApk.current(this) : new File(getCacheDir(), "manager.apk"); File apk = dynLoad ? StubApk.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) {
// 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);