diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt b/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt index 0b14f0d1..61c7278f 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt @@ -6,6 +6,8 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable +import android.os.UserHandle +import android.os.UserManager import android.util.Log import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.toDrawable @@ -34,7 +36,8 @@ object PackageHelper { class PackageCache( val info: PackageInfo, val label: String, - val icon: Drawable + val icon: Drawable, + val userId: Int, ) object Comparators { @@ -90,29 +93,39 @@ object PackageHelper { isRefreshing.emit(true) val cache = withContext(Dispatchers.IO) { val pm = hmaApp.packageManager + val um = hmaApp.getSystemService(Context.USER_SERVICE) as UserManager + val profiles = um.userProfiles if (ConfigManager.packageQueryWorkaround) { - val packages = ServiceClient.getPackageNames(0) ?: arrayOf() - mutableMapOf().also { - for (packageName in packages) { - val packageInfo = ServiceClient.getPackageInfo(packageName, 0)!! - if (packageInfo.packageName in Constants.packagesShouldNotHide) continue - packageInfo.applicationInfo?.let { appInfo -> - val label = pm.getApplicationLabel(appInfo).toString() - val icon = loadAppIconFromAppInfo(appInfo) - it[packageInfo.packageName] = PackageCache(packageInfo, label, icon) + mutableMapOf().also { cacheMap -> + for (userProfile: UserHandle in profiles) { + val packages = ServiceClient.getPackageNames(userProfile.hashCode()) ?: arrayOf() + for (packageName in packages) { + val packageInfo = ServiceClient.getPackageInfo(packageName, userProfile.hashCode())!! + if (packageInfo.packageName in Constants.packagesShouldNotHide) continue + packageInfo.applicationInfo?.let { appInfo -> + val label = pm.getApplicationLabel(appInfo).toString() + val icon = loadAppIconFromAppInfo(appInfo) + if (!cacheMap.containsKey(packageInfo.packageName)) { + cacheMap[packageInfo.packageName] = PackageCache(packageInfo, label, icon, userProfile.hashCode()) + } + } } } } } else { - val packages = pm.getInstalledPackages(0) - mutableMapOf().also { - for (packageInfo in packages) { - if (packageInfo.packageName in Constants.packagesShouldNotHide) continue - packageInfo.applicationInfo?.let { appInfo -> - val label = pm.getApplicationLabel(appInfo).toString() - val icon = loadAppIconFromAppInfo(appInfo) - it[packageInfo.packageName] = PackageCache(packageInfo, label, icon) + mutableMapOf().also { cacheMap -> + for (userProfile: UserHandle in profiles) { + val packages = getInstalledPackagesAsUser(pm, userProfile.hashCode()) + for (packageInfo in packages) { + if (packageInfo.packageName in Constants.packagesShouldNotHide) continue + packageInfo.applicationInfo?.let { appInfo -> + val label = pm.getApplicationLabel(appInfo).toString() + val icon = loadAppIconFromAppInfo(appInfo) + if (!cacheMap.containsKey(packageInfo.packageName)) { + cacheMap[packageInfo.packageName] = PackageCache(packageInfo, label, icon, userProfile.hashCode()) + } + } } } } @@ -163,6 +176,10 @@ object PackageHelper { android.R.drawable.sym_def_app_icon.asDrawable(hmaApp) } + fun loadUserId(packageName: String): Int = runBlocking { + getCacheNoThrow()[packageName]?.userId ?: 0 + } + fun isSystem(packageName: String): Boolean = runBlocking { getCacheNoThrow()[packageName]?.info?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) != 0 } @@ -203,4 +220,13 @@ object PackageHelper { return pkgInfo.activities?.firstOrNull { it.targetActivity != null }?.asComponentName() } } + + fun getInstalledPackagesAsUser(pm: PackageManager, userId: Int): List { + return if (userId == 0) { + pm.getInstalledPackages(0) + } else { + val packages = ServiceClient.getPackageNames(userId) ?: arrayOf() + packages.mapNotNull { ServiceClient.getPackageInfo(it, userId) } + } + } } diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AppSettingsV2Fragment.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AppSettingsV2Fragment.kt index 082c7ad7..f0abee2f 100644 --- a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AppSettingsV2Fragment.kt +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AppSettingsV2Fragment.kt @@ -163,7 +163,12 @@ class AppSettingsV2Fragment : Fragment(R.layout.fragment_settings) { private val parent get() = requireParentFragment() as AppSettingsV2Fragment private val pack get() = parent.viewModel.pack - private fun launchMainActivity(packageName: String) { + private fun launchMainActivity(packageName: String, userId: Int) { + if (userId != 0) { + // TODO: Try to find a method to launch apps across user profiles + return + } + try { val pkgMgr = requireContext().packageManager val pkgInfo = pkgMgr.getPackageInfo(packageName, 0) @@ -171,6 +176,8 @@ class AppSettingsV2Fragment : Fragment(R.layout.fragment_settings) { val resolvedIntent = pkgMgr.getLaunchIntentForPackage(packageName) if (resolvedIntent != null) { startActivity(resolvedIntent) + } else { + throw RuntimeException("No main activity found to launch this app") } } else { throw RuntimeException("Package is disabled") @@ -204,14 +211,15 @@ class AppSettingsV2Fragment : Fragment(R.layout.fragment_settings) { R.array.app_action_texts, ) { _, which -> parent.saveConfig() + val userId = PackageHelper.loadUserId(pack.app) when (which) { 0 -> { - ServiceClient.forceStop(pack.app, 0) - launchMainActivity(pack.app) + ServiceClient.forceStop(pack.app, userId) + launchMainActivity(pack.app, userId) } 1 -> { - launchMainActivity(pack.app) + launchMainActivity(pack.app, userId) } } } diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/AppPresets.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/AppPresets.kt index 3e764fb2..16b87ada 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/AppPresets.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/AppPresets.kt @@ -5,7 +5,6 @@ import android.content.pm.IPackageManager import android.util.Log import icu.nullptr.hidemyapplist.common.RiskyPackageUtils.ignoredForRiskyPackagesList import icu.nullptr.hidemyapplist.common.RiskyPackageUtils.tryToAddIntoGMSConnectionList -import icu.nullptr.hidemyapplist.common.Utils.getInstalledApplicationsCompat import icu.nullptr.hidemyapplist.common.Utils.getPackageInfoCompat import icu.nullptr.hidemyapplist.common.app_presets.BasePreset import icu.nullptr.hidemyapplist.common.app_presets.CustomROMPreset @@ -54,10 +53,9 @@ class AppPresets private constructor() { } val presetNames by lazy { presetList.map { it.name }.toTypedArray() } - // fun filterPresetsByName(names: Array) = presetList.filter { names.contains(it.name) } fun getPresetByName(name: String) = presetList.firstOrNull { it.name == name } - fun reloadPresets(pms: IPackageManager, holder: PresetCacheHolder, clearPresets: Boolean): PresetCacheHolder { + fun reloadPresets(appsList: List, holder: PresetCacheHolder, clearPresets: Boolean): PresetCacheHolder { if (holder.cacheVersion == BuildConfig.APP_VERSION_CODE && !clearPresets) { ignoredForRiskyPackagesList.addAll(holder.gmsDependentApps) @@ -68,15 +66,13 @@ class AppPresets private constructor() { return holder } - return reloadPresetsFromScratch(pms) + return reloadPresetsFromScratch(appsList) } - private fun reloadPresetsFromScratch(pms: IPackageManager): PresetCacheHolder { + private fun reloadPresetsFromScratch(appsList: List): PresetCacheHolder { ignoredForRiskyPackagesList.clear() presetList.forEach { it.clearPackageList() } - val appsList = getInstalledApplicationsCompat(pms, 0, 0) - for (appInfo in appsList) { runCatching { tryToAddIntoGMSConnectionList(appInfo, appInfo.packageName) { 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 7d8cfcac..d60618e7 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt @@ -18,7 +18,12 @@ import icu.nullptr.hidemyapplist.common.JsonConfig import icu.nullptr.hidemyapplist.common.PresetCacheHolder import icu.nullptr.hidemyapplist.common.RiskyPackageUtils.appHasGMSConnection import icu.nullptr.hidemyapplist.common.SettingsPresets -import icu.nullptr.hidemyapplist.common.Utils +import icu.nullptr.hidemyapplist.common.Utils.binderLocalScope +import icu.nullptr.hidemyapplist.common.Utils.generateRandomString +import icu.nullptr.hidemyapplist.common.Utils.getInstalledApplicationsCompat +import icu.nullptr.hidemyapplist.common.Utils.getInstalledPackagesCompat +import icu.nullptr.hidemyapplist.common.Utils.getPackageInfoCompat +import icu.nullptr.hidemyapplist.common.Utils.getPackageUidCompat import icu.nullptr.hidemyapplist.common.app_presets.DetectorAppsPreset import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem import icu.nullptr.hidemyapplist.xposed.hook.AccessibilityHook @@ -37,6 +42,7 @@ import icu.nullptr.hidemyapplist.xposed.hook.PmsPackageEventsHook import icu.nullptr.hidemyapplist.xposed.hook.ZygoteHook import org.frknkrc44.hma_oss.common.BuildConfig import rikka.hidden.compat.ActivityManagerApis +import rikka.hidden.compat.UserManagerApis import java.io.File import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -117,7 +123,7 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { } } if (!this::dataDir.isInitialized) { - dataDir = "/data/misc/hide_my_applist_" + Utils.generateRandomString(16) + dataDir = "/data/misc/hide_my_applist_" + generateRandomString(16) } File("$dataDir/log").mkdirs() @@ -204,7 +210,7 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { } private fun installHooks() { - Utils.getInstalledApplicationsCompat(pms, 0, 0).mapNotNullTo(systemApps) { + getInstalledApplicationsCompat(pms, 0, 0).mapNotNullTo(systemApps) { if (it.flags and ApplicationInfo.FLAG_SYSTEM != 0) it.packageName else null } @@ -415,7 +421,7 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { if (caller == query && appConfig.excludeTargetInstallationSource) return Constants.FAKE_INSTALLATION_SOURCE_DISABLED try { - val uid = Utils.getPackageUidCompat(pms, query, 0L, callingHandle.hashCode()) + val uid = getPackageUidCompat(pms, query, 0L, callingHandle.hashCode()) logD(TAG, "@shouldHideInstallationSource UID for $caller, ${callingHandle.hashCode()}: $query, $uid") if (uid < 0) return Constants.FAKE_INSTALLATION_SOURCE_DISABLED // invalid package installation source request } catch (e: Throwable) { @@ -559,7 +565,7 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { override fun readConfig() = config.toString() override fun forceStop(packageName: String?, userId: Int) { - Utils.binderLocalScope { + binderLocalScope { runCatching { ActivityManagerApis.forceStopPackage(packageName, userId) }.onFailure { error -> @@ -572,15 +578,15 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { logWithLevel(level, tag, message) } - override fun getPackageNames(userId: Int) = Utils.binderLocalScope { - Utils.getInstalledPackagesCompat(pms, 0L, userId).map { it.packageName }.toTypedArray() + override fun getPackageNames(userId: Int) = binderLocalScope { + getInstalledPackagesCompat(pms, 0L, userId).map { it.packageName }.toTypedArray() } override fun getPackageInfo( packageName: String, userId: Int - ) = Utils.binderLocalScope { - Utils.getPackageInfoCompat(pms, packageName, 0L, userId) + ) = binderLocalScope { + getPackageInfoCompat(pms, packageName, 0L, userId) } override fun listAllSettings(databaseName: String): Array { @@ -601,17 +607,24 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { override fun getLogFileLocation(): String = logFile.absolutePath fun reloadPresets(clearPresets: Boolean) { - presetCache = AppPresets.instance.reloadPresets(pms, presetCache, clearPresets) + val apps = mutableListOf().apply { + binderLocalScope { + UserManagerApis.getUserIdsNoThrow().forEach { id -> + addAll(getInstalledApplicationsCompat(pms, 0L, id)) + } + } + } + + presetCache = AppPresets.instance.reloadPresets( + apps, + presetCache, + clearPresets, + ) writePresetCache() logI(TAG, "All presets are loaded") } - override fun reloadPresetsFromScratch() { - presetCache.presetPackageNames.clear() - presetCache.gmsDependentApps.clear() - - reloadPresets(true) - } + override fun reloadPresetsFromScratch() = reloadPresets(true) override fun getDetailedFilterStats() = filterHolder.toString()