From cc6155b496bd75570e435c042ee595b64cfae859 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sat, 6 Mar 2021 18:58:45 +0800 Subject: [PATCH] [core] Auto install manager when invalid --- build.gradle.kts | 1 + core/build.gradle.kts | 2 + .../lsposed/lspd/service/ILSPosedService.aidl | 3 +- .../lsposed/lspd/service/BootReceiver.java | 45 ++++++++ .../lsposed/lspd/service/BridgeService.java | 10 ++ .../lsposed/lspd/service/ConfigManager.java | 18 +++- .../lsposed/lspd/service/LSPosedService.java | 6 ++ .../lsposed/lspd/service/PackageService.java | 102 ++++++++++++++++++ core/template_override/customize.sh | 5 - .../src/main/java/hidden/HiddenApiBridge.java | 9 ++ .../java/android/content/IIntentSender.java | 34 ++++++ .../java/android/content/IntentSender.java | 4 + .../android/content/pm/IPackageInstaller.java | 18 ++++ .../android/content/pm/IPackageManager.java | 2 + .../android/content/pm/PackageInstaller.java | 7 ++ .../android/content/pm/VersionedPackage.java | 4 + 16 files changed, 259 insertions(+), 11 deletions(-) create mode 100644 core/src/main/java/io/github/lsposed/lspd/service/BootReceiver.java create mode 100644 hiddenapi-stubs/src/main/java/android/content/IIntentSender.java create mode 100644 hiddenapi-stubs/src/main/java/android/content/IntentSender.java create mode 100644 hiddenapi-stubs/src/main/java/android/content/pm/IPackageInstaller.java create mode 100644 hiddenapi-stubs/src/main/java/android/content/pm/PackageInstaller.java create mode 100644 hiddenapi-stubs/src/main/java/android/content/pm/VersionedPackage.java diff --git a/build.gradle.kts b/build.gradle.kts index e64f41c5..e8ebbe57 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,7 @@ val repo = FileRepository(rootProject.file(".git")) val refId = repo.refDatabase.exactRef("refs/remotes/origin/master").objectId!! val commitCount = Git(repo).log().add(refId).call().count() +val defaultManagerPackageName by extra("io.github.lsposed.manager") val verCode by extra(commitCount + 4200) val verName by extra("v1.2.0") val androidTargetSdkVersion by extra(30) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6b308c9f..53a395e5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -47,6 +47,7 @@ val moduleMinRiruApiVersion = 10 val moduleMinRiruVersionName = "v23.0" val moduleMaxRiruApiVersion = 10 +val defaultManagerPackageName: String by rootProject.extra val apiCode: Int by rootProject.extra val androidTargetSdkVersion: Int by rootProject.extra @@ -102,6 +103,7 @@ android { buildConfigField("int", "API_CODE", "$apiCode") buildConfigField("String", "VERSION_NAME", "\"$verName\"") buildConfigField("Integer", "VERSION_CODE", verCode.toString()) + buildConfigField("String", "DEFAULT_MANAGER_PACKAGE_NAME", "\"$defaultManagerPackageName\"") } lint { diff --git a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl index fef85f66..918be3fe 100644 --- a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl +++ b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl @@ -6,4 +6,5 @@ interface ILSPosedService { ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) = 1; oneway void dispatchPackageChanged(in Intent intent) = 2; -} \ No newline at end of file + oneway void dispatchBootCompleted(in Intent intent) = 3; +} diff --git a/core/src/main/java/io/github/lsposed/lspd/service/BootReceiver.java b/core/src/main/java/io/github/lsposed/lspd/service/BootReceiver.java new file mode 100644 index 00000000..eb02b1e6 --- /dev/null +++ b/core/src/main/java/io/github/lsposed/lspd/service/BootReceiver.java @@ -0,0 +1,45 @@ +package io.github.lsposed.lspd.service; + +import android.annotation.SuppressLint; +import android.app.ActivityThread; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.HandlerThread; +import java.lang.reflect.Method; + +import io.github.lsposed.lspd.util.Utils; + +public class BootReceiver { + public static void register(BroadcastReceiver receiver) { + ActivityThread activityThread = ActivityThread.currentActivityThread(); + if (activityThread == null) { + Utils.logW("ActivityThread is null"); + return; + } + Context context = activityThread.getSystemContext(); + if (context == null) { + Utils.logW("context is null"); + return; + } + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); + + HandlerThread thread = new HandlerThread("lspd-BootReceiver"); + thread.start(); + Handler handler = new Handler(thread.getLooper()); + + try { + @SuppressLint("DiscouragedPrivateApi") + Method method = Context.class.getDeclaredMethod("registerReceiver", BroadcastReceiver.class, IntentFilter.class, String.class, Handler.class); + method.invoke(context, receiver, intentFilter, null, handler); + Utils.logI("registered package receiver"); + } catch (Throwable e) { + Utils.logW("registerReceiver failed", e); + } + + } +} diff --git a/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java b/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java index 7fc28e28..23536fb9 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java @@ -206,6 +206,16 @@ public class BridgeService { } } }); + BootReceiver.register(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + try { + service.dispatchBootCompleted(intent); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(e)); + } + } + }); } else { serviceBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0); } diff --git a/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java b/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java index 7ec80b99..1f91f34c 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java @@ -58,6 +58,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import io.github.lsposed.lspd.Application; +import io.github.lsposed.lspd.BuildConfig; import static io.github.lsposed.lspd.service.ServiceManager.TAG; @@ -81,8 +82,6 @@ public class ConfigManager { private static final File verboseLogSwitch = new File(configPath, "verbose_log"); private boolean verboseLog = false; - private static final String DEFAULT_MANAGER_PACKAGE_NAME = "io.github.lsposed.manager"; - private static final File managerPath = new File(configPath, "manager"); private String manager = null; private int managerUid = -1; @@ -226,7 +225,7 @@ public class ConfigManager { public synchronized void updateManager() { if (!packageStarted) return; try { - PackageInfo info = PackageService.getPackageInfo(readText(managerPath, DEFAULT_MANAGER_PACKAGE_NAME), 0, 0); + PackageInfo info = PackageService.getPackageInfo(readText(managerPath, BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME), 0, 0); if (info != null) { managerUid = info.applicationInfo.uid; manager = info.packageName; @@ -237,6 +236,15 @@ public class ConfigManager { } } + public void ensureManager() { + if (!packageStarted) return; + new Thread(() -> { + if (PackageService.installManagerIfAbsent(manager, new File("/data/adb/lspd/base.apk"))) { + updateManager(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME); + } + }).start(); + } + public synchronized void updateManager(@NonNull String packageName) { writeText(managerPath, packageName); manager = packageName; @@ -610,7 +618,7 @@ public class ConfigManager { } public boolean isManager(String packageName) { - return packageName.equals(manager) || packageName.equals(DEFAULT_MANAGER_PACKAGE_NAME); + return packageName.equals(manager) || packageName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME); } public boolean isManager(int uid) { @@ -627,7 +635,7 @@ public class ConfigManager { public static void grantManagerPermission() { try { - PackageService.grantRuntimePermission(readText(managerPath, DEFAULT_MANAGER_PACKAGE_NAME), "android.permission.INTERACT_ACROSS_USERS", 0); + PackageService.grantRuntimePermission(readText(managerPath, BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME), "android.permission.INTERACT_ACROSS_USERS", 0); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(e)); } diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java index af42633f..41d55746 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java @@ -31,6 +31,7 @@ import android.util.Log; import java.util.Arrays; import io.github.lsposed.lspd.Application; +import pxb.android.arsc.Config; import static io.github.lsposed.lspd.service.ServiceManager.TAG; @@ -122,4 +123,9 @@ public class LSPosedService extends ILSPosedService.Stub { } } + + @Override + public void dispatchBootCompleted(Intent intent) throws RemoteException { + ConfigManager.getInstance().ensureManager(); + } } diff --git a/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java b/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java index 9fb2f976..1d5c8122 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/PackageService.java @@ -19,12 +19,20 @@ package io.github.lsposed.lspd.service; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.Intent; +import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; +import android.content.pm.IPackageInstaller; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.content.pm.VersionedPackage; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -32,14 +40,23 @@ import android.util.Log; import android.util.Pair; import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; +import hidden.HiddenApiBridge; import io.github.lsposed.lspd.Application; +import io.github.lsposed.lspd.BuildConfig; +import io.github.lsposed.lspd.util.InstallerVerifier; import io.github.lsposed.lspd.utils.ParceledListSlice; import static android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS; @@ -145,6 +162,8 @@ public class PackageService { } private static PackageInfo getPackageInfoWithComponents(String packageName, int flags, int userId) throws RemoteException { + IPackageManager pm = getPackageManager(); + if (pm == null) return null; PackageInfo pkgInfo; try { pkgInfo = pm.getPackageInfo(packageName, flags | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS, userId); @@ -176,4 +195,87 @@ public class PackageService { return null; return pkgInfo; } + + static abstract class IntentSenderAdaptor extends IIntentSender.Stub { + public abstract void send(Intent intent); + + @Override + public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { + send(intent); + return 0; + } + + @Override + public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { + send(intent); + } + + public IntentSender getIntentSender() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException { + @SuppressWarnings("JavaReflectionMemberAccess") + Constructor intentSenderConstructor = IntentSender.class.getConstructor(IIntentSender.class); + intentSenderConstructor.setAccessible(true); + return intentSenderConstructor.newInstance(this); + } + } + + private static void uninstallPackage(VersionedPackage versionedPackage) throws RemoteException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + CountDownLatch latch = new CountDownLatch(1); + pm.getPackageInstaller().uninstallExistingPackage(versionedPackage, "com.android.shell", new IntentSenderAdaptor() { + @Override + public void send(Intent intent) { + latch.countDown(); + } + }.getIntentSender(), 0); + latch.await(); + } + + public static synchronized boolean installManagerIfAbsent(String packageName, File apkFile) { + IPackageManager pm = getPackageManager(); + if (pm == null) return false; + + try { + PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0, 0); + if (pkgInfo != null && pkgInfo.versionName != null && pkgInfo.versionName.equals(BuildConfig.VERSION_NAME) && pkgInfo.applicationInfo != null && InstallerVerifier.verifyInstallerSignature(pkgInfo.applicationInfo)) + return false; + // manager is not installed or version not matched, install stub + if (pkgInfo != null) { + uninstallPackage(new VersionedPackage(pkgInfo.packageName, pkgInfo.versionCode)); + } + IPackageInstaller installerService = pm.getPackageInstaller(); + String installerPackageName = "com.android.shell"; + @SuppressWarnings("JavaReflectionMemberAccess") + Constructor installerConstructor = PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, int.class); + installerConstructor.setAccessible(true); + PackageInstaller installer = installerConstructor.newInstance(installerService, installerPackageName, 0); + PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL); + int installFlags = HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params); + installFlags |= 0x00000004/*PackageManager.INSTALL_ALLOW_TEST*/ | 0x00000002/*PackageManager.INSTALL_REPLACE_EXISTING*/; + HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params, installFlags); + + int sessionId = installer.createSession(params); + try (PackageInstaller.Session session = installer.openSession(sessionId)) { + try (InputStream is = new FileInputStream(apkFile); OutputStream os = session.openWrite(apkFile.getName(), 0, -1)) { + byte[] buf = new byte[8192]; + int len; + while ((len = is.read(buf)) > 0) { + os.write(buf, 0, len); + os.flush(); + session.fsync(os); + } + } + session.commit(new IntentSenderAdaptor() { + @Override + public void send(Intent result) { + int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); + String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); + Log.d(TAG, status + " " + message); + } + }.getIntentSender()); + } + return true; + } catch (Throwable e) { + Log.e(TAG, e.getMessage(), e); + return false; + } + } } diff --git a/core/template_override/customize.sh b/core/template_override/customize.sh index cced89e3..dfc86ac6 100644 --- a/core/template_override/customize.sh +++ b/core/template_override/customize.sh @@ -192,11 +192,6 @@ else ui_print " - ${LANG_CUST_INST_CONF_NEW} ${MISC_RAND}" mkdir -p /data/adb/lspd || abortC "! ${LANG_CUST_ERR_CONF_CREATE}" echo "$MISC_PATH" > /data/adb/lspd/misc_path || abortC "! ${LANG_CUST_ERR_CONF_STORE}" - if [[ -d /data/user_de/0/io.github.lsposed.manager/conf/ ]]; then - mkdir -p /data/misc/$MISC_PATH/0/conf - cp -r /data/user_de/0/io.github.lsposed.manager/conf/* /data/misc/$MISC_PATH/0/conf/ - set_perm_recursive /data/misc/$MISC_PATH 0 0 0771 0660 "u:object_r:magisk_file:s0" || abortC "! ${LANG_CUST_ERR_PERM}" - fi fi touch /data/adb/lspd/new_install || abortC "! ${LANG_CUST_ERR_CONF_FIRST}" ui_print "- ${LANG_CUST_INST_COPY_LIB}" diff --git a/hiddenapi-bridge/src/main/java/hidden/HiddenApiBridge.java b/hiddenapi-bridge/src/main/java/hidden/HiddenApiBridge.java index 50e2c656..406a094c 100644 --- a/hiddenapi-bridge/src/main/java/hidden/HiddenApiBridge.java +++ b/hiddenapi-bridge/src/main/java/hidden/HiddenApiBridge.java @@ -19,6 +19,7 @@ package hidden; +import android.content.pm.PackageInstaller; import android.content.res.AssetManager; import android.content.res.Resources; import android.content.res.ResourcesImpl; @@ -37,4 +38,12 @@ public class HiddenApiBridge { public static void Resources_setImpl(Resources resources, ResourcesImpl impl) { resources.setImpl(impl); } + + public static int PackageInstaller_SessionParams_installFlags(PackageInstaller.SessionParams params) { + return params.installFlags; + } + + public static void PackageInstaller_SessionParams_installFlags(PackageInstaller.SessionParams params, int flags) { + params.installFlags = flags; + } } diff --git a/hiddenapi-stubs/src/main/java/android/content/IIntentSender.java b/hiddenapi-stubs/src/main/java/android/content/IIntentSender.java new file mode 100644 index 00000000..842d8a74 --- /dev/null +++ b/hiddenapi-stubs/src/main/java/android/content/IIntentSender.java @@ -0,0 +1,34 @@ +package android.content; + +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; + +import androidx.annotation.RequiresApi; + +public interface IIntentSender extends IInterface { + + int send(int code, Intent intent, String resolvedType, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options); + + @RequiresApi(26) + void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options); + + abstract class Stub extends Binder implements IIntentSender { + + public Stub() { + throw new UnsupportedOperationException(); + } + + @Override + public android.os.IBinder asBinder() { + throw new UnsupportedOperationException(); + } + + public static IIntentSender asInterface(IBinder binder) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/hiddenapi-stubs/src/main/java/android/content/IntentSender.java b/hiddenapi-stubs/src/main/java/android/content/IntentSender.java new file mode 100644 index 00000000..1279e06d --- /dev/null +++ b/hiddenapi-stubs/src/main/java/android/content/IntentSender.java @@ -0,0 +1,4 @@ +package android.content; + +public class IntentSender { +} diff --git a/hiddenapi-stubs/src/main/java/android/content/pm/IPackageInstaller.java b/hiddenapi-stubs/src/main/java/android/content/pm/IPackageInstaller.java new file mode 100644 index 00000000..afe240e1 --- /dev/null +++ b/hiddenapi-stubs/src/main/java/android/content/pm/IPackageInstaller.java @@ -0,0 +1,18 @@ +package android.content.pm; + +import android.content.IntentSender; +import android.os.Binder; +import android.os.IBinder; +import android.os.IInterface; + +public interface IPackageInstaller extends IInterface { + + void uninstallExistingPackage(VersionedPackage versionedPackage, String callerPackageName, + IntentSender statusReceiver, int userId); + + abstract class Stub extends Binder implements IPackageInstaller { + public static IPackageInstaller asInterface(IBinder obj) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/hiddenapi-stubs/src/main/java/android/content/pm/IPackageManager.java b/hiddenapi-stubs/src/main/java/android/content/pm/IPackageManager.java index 737d4338..a376f10c 100644 --- a/hiddenapi-stubs/src/main/java/android/content/pm/IPackageManager.java +++ b/hiddenapi-stubs/src/main/java/android/content/pm/IPackageManager.java @@ -46,6 +46,8 @@ public interface IPackageManager extends IInterface { int checkUidPermission(String permName, int uid) throws RemoteException; + IPackageInstaller getPackageInstaller() throws RemoteException; + abstract class Stub extends Binder implements IPackageManager { public static IPackageManager asInterface(IBinder obj) { diff --git a/hiddenapi-stubs/src/main/java/android/content/pm/PackageInstaller.java b/hiddenapi-stubs/src/main/java/android/content/pm/PackageInstaller.java new file mode 100644 index 00000000..ab6b0de4 --- /dev/null +++ b/hiddenapi-stubs/src/main/java/android/content/pm/PackageInstaller.java @@ -0,0 +1,7 @@ +package android.content.pm; + +public class PackageInstaller { + public static class SessionParams { + public int installFlags = 0; + } +} diff --git a/hiddenapi-stubs/src/main/java/android/content/pm/VersionedPackage.java b/hiddenapi-stubs/src/main/java/android/content/pm/VersionedPackage.java new file mode 100644 index 00000000..0d1bf99d --- /dev/null +++ b/hiddenapi-stubs/src/main/java/android/content/pm/VersionedPackage.java @@ -0,0 +1,4 @@ +package android.content.pm; + +public class VersionedPackage { +}