diff --git a/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java b/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
index 5f30fa285..6e4729be5 100644
--- a/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
+++ b/app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java
@@ -10,11 +10,9 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageInstaller.Session;
import android.content.pm.PackageInstaller.SessionParams;
import android.net.Uri;
import android.os.Build;
-import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
@@ -31,51 +29,6 @@ import io.michaelrocks.paranoid.Obfuscate;
@Obfuscate
public final class APKInstall {
- private static final String ACTION_SESSION_UPDATE = "ACTION_SESSION_UPDATE";
-
- // @WorkerThread
- public static void install(Context context, File apk) {
- try (var src = new FileInputStream(apk);
- var out = openStream(context)) {
- if (out != null)
- transfer(src, out);
- } catch (IOException e) {
- Log.e(APKInstall.class.getSimpleName(), "", e);
- }
- }
-
- public static OutputStream openStream(Context context) {
- //noinspection InlinedApi
- var flag = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
- var intent = new Intent(ACTION_SESSION_UPDATE).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);
- }
- try {
- Session session = installer.openSession(installer.createSession(params));
- var out = session.openWrite(UUID.randomUUID().toString(), 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();
- }
- };
- } catch (IOException e) {
- Log.e(APKInstall.class.getSimpleName(), "", e);
- }
- return null;
- }
-
public static void transfer(InputStream in, OutputStream out) throws IOException {
int size = 8192;
var buffer = new byte[size];
@@ -85,21 +38,37 @@ public final class APKInstall {
}
}
- public static InstallReceiver register(Context context, String packageName, Runnable onSuccess) {
- var receiver = new InstallReceiver(packageName, onSuccess);
- var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addDataScheme("package");
- context.registerReceiver(receiver, filter);
- context.registerReceiver(receiver, new IntentFilter(ACTION_SESSION_UPDATE));
+ public static Session startSession(Context context) {
+ return startSession(context, null, null);
+ }
+
+ public static Session startSession(Context context, String pkg, Runnable onSuccess) {
+ var receiver = new InstallReceiver(pkg, onSuccess);
+ 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");
+ context.registerReceiver(receiver, filter);
+ }
+ context.registerReceiver(receiver, new IntentFilter(receiver.sessionId));
return receiver;
}
- public static class InstallReceiver extends BroadcastReceiver {
+ public interface Session {
+ OutputStream openStream(Context context) throws IOException;
+ void install(Context context, File apk) throws IOException;
+ Intent waitIntent();
+ }
+
+ private static class InstallReceiver extends BroadcastReceiver implements Session {
private final String packageName;
private final Runnable onSuccess;
private final CountDownLatch latch = new CountDownLatch(1);
private Intent userAction = null;
+ final String sessionId = UUID.randomUUID().toString();
+
private InstallReceiver(String packageName, Runnable onSuccess) {
this.packageName = packageName;
this.onSuccess = onSuccess;
@@ -113,27 +82,32 @@ public final class APKInstall {
return;
String pkg = data.getSchemeSpecificPart();
if (pkg.equals(packageName)) {
- if (onSuccess != null)
- onSuccess.run();
- context.unregisterReceiver(this);
+ onSuccess(context);
}
- return;
+ } 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);
+ break;
+ case STATUS_SUCCESS:
+ if (packageName == null) {
+ onSuccess(context);
+ }
+ break;
+ }
+ latch.countDown();
}
- int status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID);
- switch (status) {
- case STATUS_PENDING_USER_ACTION:
- userAction = intent.getParcelableExtra(Intent.EXTRA_INTENT);
- break;
- case STATUS_SUCCESS:
- if (onSuccess != null)
- onSuccess.run();
- default:
- context.unregisterReceiver(this);
- }
- latch.countDown();
+ }
+
+ private void onSuccess(Context context) {
+ if (onSuccess != null)
+ onSuccess.run();
+ context.getApplicationContext().unregisterReceiver(this);
}
// @WorkerThread @Nullable
+ @Override
public Intent waitIntent() {
try {
//noinspection ResultOfMethodCallIgnored
@@ -141,5 +115,41 @@ public final class APKInstall {
} 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();
+ }
+ };
+ }
+
+ @Override
+ public void install(Context context, File apk) throws IOException {
+ try (var src = new FileInputStream(apk);
+ var out = openStream(context)) {
+ transfer(src, out);
+ }
+ }
}
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
index 091023bbb..1e202bc41 100644
--- a/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
@@ -72,9 +72,9 @@ class DownloadService : NotificationService() {
}
}
- private fun openApkSession(): OutputStream {
+ private fun APKInstall.Session.open(): OutputStream {
Config.showUpdateDone = true
- return APKInstall.openStream(this)
+ return openStream(this@DownloadService)
}
private suspend fun handleApp(stream: InputStream, subject: Subject.App) {
@@ -110,9 +110,9 @@ class DownloadService : NotificationService() {
apk.delete()
// Install
- val receiver = APKInstall.register(this, null, null)
- patched.inputStream().copyAndClose(openApkSession())
- subject.intent = receiver.waitIntent()
+ val session = APKInstall.startSession(this)
+ patched.inputStream().copyAndClose(session.open())
+ subject.intent = session.waitIntent()
patched.delete()
} else {
@@ -131,9 +131,9 @@ class DownloadService : NotificationService() {
throw e
}
} else {
- val receiver = APKInstall.register(this, null, null)
- writeTee(openApkSession())
- subject.intent = receiver.waitIntent()
+ val session = APKInstall.startSession(this)
+ writeTee(session.open())
+ subject.intent = session.waitIntent()
}
}
diff --git a/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt b/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt
index d9cdb1d1e..931add738 100644
--- a/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/core/tasks/HideAPK.kt
@@ -1,6 +1,7 @@
package com.topjohnwu.magisk.core.tasks
import android.app.Activity
+import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
import android.widget.Toast
@@ -20,8 +21,7 @@ import com.topjohnwu.magisk.signing.SignApk
import com.topjohnwu.magisk.utils.APKInstall
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.*
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
@@ -108,7 +108,8 @@ object HideAPK {
Timber.e(e)
stub.createNewFile()
val cmd = "\$MAGISKBIN/magiskinit -x manager ${stub.path}"
- if (!Shell.su(cmd).exec().isSuccess) return false
+ if (!Shell.su(cmd).exec().isSuccess)
+ return false
}
// Generate a new random package name and signature
@@ -120,20 +121,22 @@ object HideAPK {
return false
// Install and auto launch app
- val receiver = APKInstall.register(activity, pkg) {
+ val session = APKInstall.startSession(activity, pkg) {
launchApp(activity, pkg)
}
- val cmd = "adb_pm_install $repack ${activity.applicationInfo.uid}"
- if (!Shell.su(cmd).exec().isSuccess) {
- APKInstall.install(activity, repack)
- receiver.waitIntent()?.let { activity.startActivity(it) }
+ try {
+ session.install(activity, repack)
+ } catch (e: IOException) {
+ Timber.e(e)
+ return false
}
+ session.waitIntent()?.let { activity.startActivity(it) }
return true
}
@Suppress("DEPRECATION")
suspend fun hide(activity: Activity, label: String) {
- val dialog = android.app.ProgressDialog(activity).apply {
+ val dialog = ProgressDialog(activity).apply {
setTitle(activity.getString(R.string.hide_app_title))
isIndeterminate = true
setCancelable(false)
@@ -148,24 +151,28 @@ object HideAPK {
}
}
+ @DelicateCoroutinesApi
@Suppress("DEPRECATION")
fun restore(activity: Activity) {
- val dialog = android.app.ProgressDialog(activity).apply {
+ val dialog = ProgressDialog(activity).apply {
setTitle(activity.getString(R.string.restore_img_msg))
isIndeterminate = true
setCancelable(false)
show()
}
val apk = StubApk.current(activity)
- val receiver = APKInstall.register(activity, APPLICATION_ID) {
+ val session = APKInstall.startSession(activity, APPLICATION_ID) {
launchApp(activity, APPLICATION_ID)
dialog.dismiss()
}
- val cmd = "adb_pm_install $apk ${activity.applicationInfo.uid}"
- Shell.su(cmd).submit(Shell.EXECUTOR) { ret ->
- if (ret.isSuccess) return@submit
- APKInstall.install(activity, apk)
- receiver.waitIntent()?.let { activity.startActivity(it) }
+ GlobalScope.launch(Dispatchers.IO) {
+ try {
+ session.install(activity, apk)
+ } catch (e: IOException) {
+ Timber.e(e)
+ return@launch
+ }
+ session.waitIntent()?.let { activity.startActivity(it) }
}
}
}
diff --git a/buildSrc/src/main/java/Codegen.kt b/buildSrc/src/main/java/Codegen.kt
index f2a8abd9c..1d375cff6 100644
--- a/buildSrc/src/main/java/Codegen.kt
+++ b/buildSrc/src/main/java/Codegen.kt
@@ -99,6 +99,7 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|
|
|
+ |
|
|
|
diff --git a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
index 840f253cf..efdcac50c 100644
--- a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
+++ b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java
@@ -26,6 +26,7 @@ import org.json.JSONException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.IOException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
@@ -126,10 +127,15 @@ public class DownloadActivity extends Activity {
if (dynLoad) {
runOnUiThread(onSuccess);
} else {
- var receiver = APKInstall.register(this, BuildConfig.APPLICATION_ID, onSuccess);
- APKInstall.install(this, file);
- Intent intent = receiver.waitIntent();
- if (intent != null) startActivity(intent);
+ var session = APKInstall.startSession(this);
+ try {
+ session.install(this, file);
+ Intent intent = session.waitIntent();
+ if (intent != null)
+ startActivity(intent);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
});
}