From f13dff47c2583ba1bcfdaef9b1cc76a778b0c2cf Mon Sep 17 00:00:00 2001 From: frknkrc44 Date: Sat, 27 Sep 2025 19:53:20 +0300 Subject: [PATCH] Try to improve getPackagesHoldingPermissions performance, fix logging --- .../hidemyapplist/xposed/HMAService.kt | 9 +- .../hidemyapplist/xposed/Utils4Xposed.kt | 5 +- .../xposed/hook/PmsHookTarget30.kt | 53 ++++++ .../xposed/hook/PmsHookTarget31.kt | 152 ++++++++++++++++++ .../xposed/hook/PmsHookTargetBase.kt | 44 +++++ 5 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget31.kt diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt index 74cfc0f..5bfe5cc 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt @@ -19,6 +19,7 @@ import icu.nullptr.hidemyapplist.xposed.hook.IFrameworkHook import icu.nullptr.hidemyapplist.xposed.hook.PlatformCompatHook import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget28 import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget30 +import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget31 import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget33 import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget34 import icu.nullptr.hidemyapplist.xposed.hook.PmsPackageEventsHook @@ -145,6 +146,8 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() { frameworkHooks.add(PmsHookTarget34(this)) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { frameworkHooks.add(PmsHookTarget33(this)) + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + frameworkHooks.add(PmsHookTarget31(this)) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { frameworkHooks.add(PmsHookTarget30(this)) } else { @@ -222,15 +225,15 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() { if (caller == null || query == null) return 0 val appConfig = config.scope[caller] ?: return 0 if (!appConfig.hideInstallationSource) return 0 - logD(TAG, "@shouldHideInstallationSource $caller -> $query") + logD(TAG, "@shouldHideInstallationSource $caller: $query") if (caller == query && appConfig.excludeTargetInstallationSource) return 0 try { val uid = Utils.getPackageUidCompat(pms, query, 0L, user.hashCode()) - logD(TAG, "@shouldHideInstallationSource UID for $caller (${user.hashCode()}) -> $query: $uid") + logD(TAG, "@shouldHideInstallationSource UID for $caller, ${user.hashCode()}: $query, $uid") if (uid < 0) return 0 // invalid package installation source request } catch (e: Throwable) { - logD(TAG, "@shouldHideInstallationSource UID error for $caller (${user.hashCode()})", e) + logD(TAG, "@shouldHideInstallationSource UID error for $caller, ${user.hashCode()}", e) return 0 } diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Utils4Xposed.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Utils4Xposed.kt index bc5b388..b58fd95 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Utils4Xposed.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Utils4Xposed.kt @@ -17,7 +17,10 @@ class Utils4Xposed { } fun getCallingApps(service: HMAService): Array { - val callingUid = Binder.getCallingUid() + return getCallingApps(service, Binder.getCallingUid()) + } + + fun getCallingApps(service: HMAService, callingUid: Int): Array { if (callingUid == Constants.UID_SYSTEM) return arrayOf() return Utils.binderLocalScope { service.pms.getPackagesForUid(callingUid) diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget30.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget30.kt index 7de7691..caccaa7 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget30.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget30.kt @@ -1,9 +1,11 @@ package icu.nullptr.hidemyapplist.xposed.hook +import android.os.Binder import android.os.Build import androidx.annotation.RequiresApi import com.github.kyuubiran.ezxhelper.utils.findConstructor import com.github.kyuubiran.ezxhelper.utils.findMethod +import com.github.kyuubiran.ezxhelper.utils.findMethodOrNull import com.github.kyuubiran.ezxhelper.utils.hookBefore import com.github.kyuubiran.ezxhelper.utils.paramCount import icu.nullptr.hidemyapplist.common.Constants @@ -50,6 +52,57 @@ class PmsHookTarget30(service: HMAService) : PmsHookTargetBase(service) { override fun load() { logI(TAG, "Load hook") + + /* + findMethodOrNull("com.android.server.pm.PackageManagerService") { + name == "checkPermission" + }?.hookBefore { param -> + val targetApp = param.args[1] as String + val callingApps = Utils4Xposed.getCallingApps(service) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + logD(TAG, "@checkPermission - PkgMgr: insecure query from $caller to $targetApp") + param.result = PackageManager.PERMISSION_DENIED + service.filterCount++ + return@hookBefore + } + } + } + + findMethodOrNull("com.android.server.pm.permission.PermissionManagerService", findSuper = true) { + name == "checkPermission" + }?.hookBefore { param -> + val targetApp = param.args[1] as String + val callingApps = Utils4Xposed.getCallingApps(service) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + logD(TAG, "@checkPermission - PkgMgr: insecure query from $caller to $targetApp") + param.result = PackageManager.PERMISSION_DENIED + service.filterCount++ + return@hookBefore + } + } + } + */ + + findMethodOrNull("com.android.server.pm.PackageManagerService", findSuper = true) { + name == "getPackageSetting" + }?.hookBefore { param -> + val targetApp = param.args[0] as String + val callingUid = Binder.getCallingUid() + val callingApps = Utils4Xposed.getCallingApps(service, callingUid) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + logD(TAG, "@getPackageSetting - PkgMgr: insecure query from $caller to $targetApp") + param.result = null + service.filterCount++ + return@hookBefore + } + } + }?.let { + hooks += it + } + hooks += findMethod("com.android.server.pm.AppsFilter") { name == "shouldFilterApplication" }.hookBefore { param -> diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget31.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget31.kt new file mode 100644 index 0000000..6442771 --- /dev/null +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTarget31.kt @@ -0,0 +1,152 @@ +package icu.nullptr.hidemyapplist.xposed.hook + +import android.os.Binder +import android.os.Build +import androidx.annotation.RequiresApi +import com.github.kyuubiran.ezxhelper.utils.findConstructor +import com.github.kyuubiran.ezxhelper.utils.findMethod +import com.github.kyuubiran.ezxhelper.utils.findMethodOrNull +import com.github.kyuubiran.ezxhelper.utils.hookBefore +import com.github.kyuubiran.ezxhelper.utils.paramCount +import icu.nullptr.hidemyapplist.common.Constants +import icu.nullptr.hidemyapplist.common.Constants.VENDING_PACKAGE_NAME +import icu.nullptr.hidemyapplist.common.Utils +import icu.nullptr.hidemyapplist.xposed.HMAService +import icu.nullptr.hidemyapplist.xposed.Utils4Xposed +import icu.nullptr.hidemyapplist.xposed.logD +import icu.nullptr.hidemyapplist.xposed.logE +import icu.nullptr.hidemyapplist.xposed.logI + +@RequiresApi(Build.VERSION_CODES.S) +class PmsHookTarget31(service: HMAService) : PmsHookTargetBase(service) { + + companion object { + private const val TAG = "PmsHookTarget31" + } + + override val fakeSystemPackageInstallInfo: Any by lazy { + findConstructor( + "android.content.pm.InstallSourceInfo" + ) { + paramCount == 4 + }.newInstance( + null, + null, + null, + null, + ) + } + + override val fakeUserPackageInstallInfo: Any by lazy { + findConstructor( + "android.content.pm.InstallSourceInfo" + ) { + paramCount == 4 + }.newInstance( + VENDING_PACKAGE_NAME, + psSigningInfo, + VENDING_PACKAGE_NAME, + VENDING_PACKAGE_NAME, + ) + } + + override fun load() { + logI(TAG, "Load hook") + + /* + findMethodOrNull("com.android.server.pm.PackageManagerService") { + name == "checkPermission" + }?.hookBefore { param -> + val targetApp = param.args[1] as String + val callingApps = Utils4Xposed.getCallingApps(service) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + logD(TAG, "@checkPermission - PkgMgr: insecure query from $caller to $targetApp") + param.result = PackageManager.PERMISSION_DENIED + service.filterCount++ + return@hookBefore + } + } + } + + findMethodOrNull("com.android.server.pm.permission.PermissionManagerService", findSuper = true) { + name == "checkPermission" + }?.hookBefore { param -> + val targetApp = param.args[0] as String + val callingApps = Utils4Xposed.getCallingApps(service) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + logD(TAG, "@checkPermission - PermMgr: insecure query from $caller to $targetApp") + param.result = PackageManager.PERMISSION_DENIED + service.filterCount++ + return@hookBefore + } + } + } + */ + + findMethodOrNull("com.android.server.pm.PackageManagerService\$ComputerTracker") { + name == "getPackageSetting" + }?.hookBefore { param -> + val targetApp = param.args[0] as String + val callingUid = Binder.getCallingUid() + val callingApps = Utils4Xposed.getCallingApps(service, callingUid) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + logD(TAG, "@getPackageSetting - Computer: insecure query from $caller to $targetApp") + param.result = null + service.filterCount++ + return@hookBefore + } + } + }?.let { + hooks += it + } + + findMethodOrNull("com.android.server.pm.PackageManagerService\$ComputerTracker") { + name == "getPackageSettingInternal" + }?.hookBefore { param -> + val targetApp = param.args[0] as String + val callingUid = param.args[1] as Int + val callingApps = Utils4Xposed.getCallingApps(service, callingUid) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + logD(TAG, "@getPackageSettingInternal - Computer: insecure query from $caller to $targetApp") + param.result = null + service.filterCount++ + return@hookBefore + } + } + }?.let { + hooks += it + } + + hooks += findMethod("com.android.server.pm.AppsFilter") { + name == "shouldFilterApplication" + }.hookBefore { param -> + runCatching { + val callingUid = param.args[0] as Int + if (callingUid == Constants.UID_SYSTEM) return@hookBefore + val callingApps = Utils.binderLocalScope { + service.pms.getPackagesForUid(callingUid) + } ?: return@hookBefore + val targetApp = Utils4Xposed.getPackageNameFromPackageSettings(param.args[2]) + for (caller in callingApps) { + if (service.shouldHide(caller, targetApp)) { + param.result = true + service.filterCount++ + val last = lastFilteredApp.getAndSet(caller) + if (last != caller) logI(TAG, "@shouldFilterApplication: query from $caller") + logD(TAG, "@shouldFilterApplication caller: $callingUid $caller, target: $targetApp") + return@hookBefore + } + } + }.onFailure { + logE(TAG, "Fatal error occurred, disable hooks", it) + unload() + } + } + + super.load() + } +} diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTargetBase.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTargetBase.kt index b04c5fb..29373a6 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTargetBase.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsHookTargetBase.kt @@ -3,17 +3,24 @@ package icu.nullptr.hidemyapplist.xposed.hook import android.content.pm.PackageManager import android.os.Binder import android.os.Build +import android.util.ArrayMap import com.github.kyuubiran.ezxhelper.utils.findMethod import com.github.kyuubiran.ezxhelper.utils.findMethodOrNull +import com.github.kyuubiran.ezxhelper.utils.hookAfter import com.github.kyuubiran.ezxhelper.utils.hookBefore import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.XposedHelpers import icu.nullptr.hidemyapplist.common.Constants import icu.nullptr.hidemyapplist.common.Constants.VENDING_PACKAGE_NAME import icu.nullptr.hidemyapplist.common.Utils import icu.nullptr.hidemyapplist.xposed.HMAService +import icu.nullptr.hidemyapplist.xposed.Utils4Xposed +import icu.nullptr.hidemyapplist.xposed.logD import java.util.concurrent.atomic.AtomicReference abstract class PmsHookTargetBase(protected val service: HMAService) : IFrameworkHook { + private val TAG by lazy { this::class.java.simpleName } + protected val hooks = mutableListOf() protected var lastFilteredApp: AtomicReference = AtomicReference(null) @@ -30,6 +37,43 @@ abstract class PmsHookTargetBase(protected val service: HMAService) : IFramework abstract val fakeUserPackageInstallInfo: Any? override fun load() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + hooks += findMethod("com.android.server.pm.ComputerEngine") { + name == "getPackageStates" + }.hookAfter { param -> + val callingUid = Binder.getCallingUid() + if (callingUid == Constants.UID_SYSTEM) return@hookAfter + + val callingApps = Utils4Xposed.getCallingApps(service) + for (caller in callingApps) { + if (service.isHookEnabled(caller)) { + logD(TAG, "@getPackageStates: incoming query from $caller") + + val result = param.result as ArrayMap<*, *> + val markedToRemove = mutableListOf() + + for (pair in result.entries) { + val value = pair.value + val packageName = XposedHelpers.callMethod(value, "getPackageName") as String + if (service.shouldHide(caller, packageName)) { + markedToRemove.add(pair.key) + } + } + + if (markedToRemove.isNotEmpty()) { + val copyResult = ArrayMap(result) + copyResult.removeAll(markedToRemove) + logD(TAG, "@getPackageStates: removed ${markedToRemove.size} entries from $caller; ${result.size}, ${copyResult.size}") + param.result = copyResult + service.filterCount++ + + return@hookAfter + } + } + } + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { findMethodOrNull(service.pms::class.java, findSuper = true) { name == "getInstallSourceInfo"