diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt index 8359d4c..aefacbe 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt @@ -102,6 +102,20 @@ object ConfigManager { saveConfig() } + var altAppDataIsolation: Boolean + get() = config.altAppDataIsolation + set(value) { + config.altAppDataIsolation = value + saveConfig() + } + + var altVoldAppDataIsolation: Boolean + get() = config.altVoldAppDataIsolation + set(value) { + config.altVoldAppDataIsolation = value + saveConfig() + } + var skipSystemAppDataIsolation: Boolean get() = config.skipSystemAppDataIsolation set(value) { diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt index fe52ee6..c403859 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt @@ -230,6 +230,9 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) { R.string.app_force_stop_warning, Toast.LENGTH_LONG).show() true } + findPreference("excludeVoldIsolation")?.let { + it.isEnabled = ConfigManager.altVoldAppDataIsolation + } findPreference("invertActivityLaunchProtection")?.let { it.summary = getString(R.string.app_invert_activity_launch_protection_desc) + "\n\n" + getString(R.string.app_global_activity_launch_protection_state, diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt index b73617a..24378a8 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt @@ -109,6 +109,8 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen "detailLog" -> ConfigManager.detailLog "hideIcon" -> PrefManager.hideIcon "bypassRiskyPackageWarning" -> PrefManager.bypassRiskyPackageWarning + "appDataIsolation" -> ConfigManager.altAppDataIsolation + "voldAppDataIsolation" -> ConfigManager.altVoldAppDataIsolation "skipSystemAppDataIsolation" -> ConfigManager.skipSystemAppDataIsolation "disableActivityLaunchProtection" -> ConfigManager.disableActivityLaunchProtection "forceMountData" -> ConfigManager.forceMountData @@ -144,6 +146,9 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen "hideIcon" -> PrefManager.hideIcon = value "bypassRiskyPackageWarning" -> PrefManager.bypassRiskyPackageWarning = value "disableActivityLaunchProtection" -> ConfigManager.disableActivityLaunchProtection = value + "appDataIsolation" -> ConfigManager.altAppDataIsolation = value + "voldAppDataIsolation" -> ConfigManager.altVoldAppDataIsolation = value + "skipSystemAppDataIsolation" -> ConfigManager.skipSystemAppDataIsolation = value "packageQueryWorkaround" -> ConfigManager.packageQueryWorkaround = value else -> throw IllegalArgumentException("Invalid key: $key") } @@ -167,9 +172,64 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen } } + class DataIsolationPreferenceFragment : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + preferenceManager.preferenceDataStore = SettingsPreferenceDataStore() + setPreferencesFromResource(R.xml.settings_data_isolation, rootKey) + findPreference("appDataIsolation")?.let { + it.summary = getString(R.string.settings_need_reboot) + "\n\n" + + getString( + R.string.settings_default_value, + PropertyUtils.isAppDataIsolationEnabled.enabledString(resources) + ) + } + + findPreference("voldAppDataIsolation")?.let { + it.summary = getString(R.string.settings_need_reboot) + "\n\n" + + getString( + R.string.settings_default_value, + PropertyUtils.isVoldAppDataIsolationEnabled.enabledString(resources) + ) + + it.setOnPreferenceChangeListener { _, newValue -> + val enabled = newValue as Boolean + if (enabled) { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.settings_warning) + .setMessage(R.string.settings_vold_warning) + .setPositiveButton(android.R.string.ok) { _, _ -> + it.isChecked = true + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + it.isChecked = false + } + .setCancelable(false) + .show() + } + !enabled + } + } + } + } class SettingsPreferenceFragment : PreferenceFragmentCompat() { + private fun configureDataIsolation() { + findPreference("dataIsolation")?.let { + it.isEnabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R + it.summary = when { + it.isEnabled -> getString( + R.string.settings_data_isolation_summary, + if (ConfigManager.altAppDataIsolation) getString(R.string.settings_overwritten) + else PropertyUtils.isAppDataIsolationEnabled.enabledString(resources), + if (ConfigManager.altVoldAppDataIsolation) getString(R.string.settings_overwritten) + else PropertyUtils.isVoldAppDataIsolationEnabled.enabledString(resources), + ConfigManager.forceMountData.enabledString(resources) + ) + else -> getString(R.string.settings_data_isolation_unsupported) + } + } + } @Suppress("deprecation") override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -305,6 +365,8 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen } } + configureDataIsolation() + findPreference("stopSystemService")?.setOnPreferenceClickListener { if (ServiceClient.serviceVersion != 0) { MaterialAlertDialogBuilder(requireContext()) @@ -341,6 +403,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen override fun onResume() { super.onResume() + configureDataIsolation() } } } diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index 7c42e44..cafe2a3 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -81,7 +81,7 @@ %d ứng dụng không ẩn %d Ứng dụng bị ẩn - Bật vô hình + Bật ẩn Mục tùy chỉnh Chọn mục Chọn thiết lập trước @@ -95,13 +95,13 @@ %d Ứng dụng bổ sung được ẩn Gói rủi ro Gói này được đánh dấu là rủi ro và bạn có thể gây ra lỗi cho các ứng dụng được ẩn. Bạn có muốn tiếp tục không? - Bịp - Bịp nguồn cài - Tất cả các ứng dụng của người dùng sẽ bị bịp là đã cài đặt trên Google Play cho ứng dụng này - Bịp luôn cho các ứng dụng hệ thống - Tất cả các ứng dụng hệ thống sẽ được bịp là đã cài đặt từ Google Play cho ứng dụng này + Giả lập + Giả lập các nguồn cài đặt + Tất cả các ứng dụng của người dùng sẽ bị giả lập là đã cài đặt trên Play Store cho ứng dụng này + Giả lập luôn cho các ứng dụng hệ thống + Tất cả các ứng dụng hệ thống sẽ được giả lập là đã cài đặt từ Play Store cho ứng dụng này Loại trừ ứng dụng mục tiêu(đang thiết lập) - Bạn có thể bật nếu ứng dụng này không có sẵn trên Google Play + Bạn có thể bật nó nếu ứng dụng này không có sẵn trên Play Store Đừng quên buộc dừng ứng dụng mục tiêu sau khi lưu cấu hình Đảo ngược lựa chọn Tắt bảo vệ khởi chạy hoạt động Tùy chọn này sẽ đảo ngược lựa chọn \"Tắt bảo vệ khởi chạy hoạt động\" trong Cài đặt. @@ -114,12 +114,12 @@ Cài đặt mẫu Hiển thị các cài đặt trước Các mô-đun LSPosed/Xposed - App Root - App ROM Custom - App linh tinh - App phát hiện / check Root - App Shizuku/Dhizuku - App WebView + Ứng dụng quản lý Root/Ứng dụng được cấp Root + Ứng dụng ROM tùy biến + Ứng dụng đáng ngờ + ỨD phát hiện/KT Root + Shizuku/Dhizuku ứng dụng + Các ứng dụng WebView Trợ năng @@ -155,9 +155,9 @@ Quyền bị từ chối Thực thi cách ly dữ liệu cho tất cả các ứng dụng Tính năng cô lập dữ liệu bị vô hiệu hóa đối với các ứng dụng mục tiêu có API thấp hơn 30 theo mặc định - Log debug chi tiết + Nhật ký chi tiết gỡ lỗi Kích cỡ bộ đệm nhật ký - Ẩn mô đun trong launcher + Ẩn mô đun trong Trình khởi chạy Bạn vẫn có thể mở ứng dụng trong Xposed Bỏ qua cảnh báo gói rủi ro Lưu ý rằng nhà phát triển không chịu trách nhiệm về các vấn đề liên quan đến gói rủi ro trước hoặc sau khi bạn bật tính năng này @@ -169,7 +169,7 @@ Thao tác không thể hoàn tác. Tìm gói cài đặt thay thế Đừng bật lựa chọn này nếu bạn thấy danh sách tên gói ứng dụng trong ứng dụng này đúng cách. - Biểu tượng app + Biểu tượng khởi động Ngôn ngữ Giúp chúng tôi dịch Giúp chúng tôi dịch %s sang ngôn ngữ của bạn diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 02c1276..5c81daa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -216,10 +216,9 @@ The original HMA project has become closed-source and their latest code didn\'t contain some of the latest changes, so I created this fork to continue it as open-source again. - How to use HMAL-OSS / MBCP usages + How to use this module - For MBCP/MBBank app : \nGo to Manage apps -> find for MBCP app, which is [MB Bank] -> [Template config] -> [Using %s presets] -> [Root managers/Rooted apps] -> OK. \nThen try to open MB/MBCP app again.\n - For others: \nYou can create templates in \"Manage templates\".\nThen apply the templates to apps in \"Manage apps\". (You can also select extra apps for targets.)\nYou should and ONLY should check \"System Framework\" in Xposed module scope / whitelist. + You can create templates in \"Manage templates\".\nThen apply the templates to apps in \"Manage apps\". (You can also select extra apps for targets.)\nYou should and ONLY should check \"System Framework\" in Xposed module scope / whitelist. Config modification effective in real time. diff --git a/app/src/main/res/xml/app_settings.xml b/app/src/main/res/xml/app_settings.xml index 7790ef1..2bdd8a9 100644 --- a/app/src/main/res/xml/app_settings.xml +++ b/app/src/main/res/xml/app_settings.xml @@ -80,4 +80,12 @@ android:title="@string/app_exclude_target_installation_source" /> + + + diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 821c111..63d06b6 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -68,6 +68,11 @@ android:key="disableActivityLaunchProtection" android:title="@string/settings_disable_activity_launch_protection" android:summary="@string/settings_disable_activity_launch_protection_summary" /> + + + + + + + + + + + + + + + + + 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 da6f48c..d180c98 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/AppPresets.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/AppPresets.kt @@ -9,6 +9,7 @@ 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 +import icu.nullptr.hidemyapplist.common.app_presets.DetectorAppsPreset import icu.nullptr.hidemyapplist.common.app_presets.RootAppsPreset import icu.nullptr.hidemyapplist.common.app_presets.SDhizukuAppsPreset import icu.nullptr.hidemyapplist.common.app_presets.SuspiciousAppsPreset @@ -138,6 +139,7 @@ class AppPresets private constructor() { presetList.add(XposedModulesPreset()) presetList.add(SuspiciousAppsPreset()) presetList.add(SDhizukuAppsPreset()) + presetList.add(DetectorAppsPreset()) } } diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/JsonConfig.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/JsonConfig.kt index e9bb753..47065da 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/JsonConfig.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/JsonConfig.kt @@ -13,6 +13,7 @@ data class JsonConfig( var forceMountData: Boolean = true, var disableActivityLaunchProtection: Boolean = false, var altAppDataIsolation: Boolean = false, + var altVoldAppDataIsolation: Boolean = false, var skipSystemAppDataIsolation: Boolean = true, var packageQueryWorkaround: Boolean = false, val templates: MutableMap = mutableMapOf(), diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/DetectorAppsPreset.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/DetectorAppsPreset.kt new file mode 100644 index 0000000..2695fcb --- /dev/null +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/DetectorAppsPreset.kt @@ -0,0 +1,63 @@ +package icu.nullptr.hidemyapplist.common.app_presets + +import android.content.pm.ApplicationInfo + +class DetectorAppsPreset : BasePreset(NAME) { + companion object { + const val NAME = "detector_apps" + } + + override val exactPackageNames = setOf( + // Detector apps + "com.reveny.nativecheck", + "icu.nullptr.nativetest", + "io.github.rabehx.securify", + "com.zhenxi.hunter", + "io.github.vvb2060.mahoshojo", + "io.github.huskydg.memorydetector", + "org.akanework.checker", + "icu.nullptr.applistdetector", + "com.byxiaorun.detector", + "com.kimchangyoun.rootbeerFresh.sample", + "com.androidfung.drminfo", + "com.kikyps.crackme", + "org.matrix.demo", + "com.rem01gaming.disclosure", + "luna.safe.luna", + "com.AndroLua", + "com.detect.mt", + "io.liankong.riskdetector", + "com.suisho.rc", + "com.ahmed.security_tester", + "id.my.pjm.qbcd_okr_dvii", + "wu.Zygisk.Detector", + "com.atominvention.rootchecker", + "com.joeykrim.rootcheck", + + // Play Integrity checkers + "krypton.tbsafetychecker", + "gr.nikolasspyr.integritycheck", + "com.henrikherzig.playintegritychecker", + "com.thend.integritychecker", + "com.flinkapps.safteynet", + + // Other checkers + "com.bryancandi.knoxcheck", + ) + + override fun canBeAddedIntoPreset(appInfo: ApplicationInfo): Boolean { + val packageName = appInfo.packageName + + // All Garfield packages + if (packageName.startsWith("me.garfieldhan.")) { + return true + } + + // Key attestation apps + if (packageName.endsWith(".keyattestation")) { + return true + } + + return false + } +} \ No newline at end of file diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/RootAppsPreset.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/RootAppsPreset.kt index c98ab40..eafeded 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/RootAppsPreset.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/RootAppsPreset.kt @@ -11,7 +11,6 @@ class RootAppsPreset : BasePreset(NAME) { override val exactPackageNames = setOf( // rooted apps - "io.github.huskydg.magisk", "io.github.a13e300.ksuwebui", "com.fox2code.mmm", "id.kuato.diskhealth", @@ -73,7 +72,12 @@ class RootAppsPreset : BasePreset(NAME) { ) override fun canBeAddedIntoPreset(appInfo: ApplicationInfo): Boolean { - val packageName = appInfo.packageName + val packageName = appInfo.packageName + + // All libxzr apps (konabess, hkf, ...) + if (Utils.startsWithMultiple(packageName, "xzr.", "moe.xzr.")) { + return true + } // LSPosed and LSPatch if (packageName.startsWith("org.lsposed")) { @@ -85,11 +89,6 @@ class RootAppsPreset : BasePreset(NAME) { return true } - // Kitsune - if (packageName.startsWith("io.github.huskydg.magisk")) { - return true - } - // MMRL if (packageName.startsWith("com.dergoogler.mmrl")) { return true @@ -109,7 +108,17 @@ class RootAppsPreset : BasePreset(NAME) { if (packageName.startsWith("com.xayah.databackup")) { return true } - + + // SmartPack Kernel Manager + Busybox Installer + if (packageName.startsWith("com.smartpack.")) { + return true + } + + // F-Droid Privileged + if (packageName.startsWith("org.fdroid.fdroid.privileged")) { + return true + } + ZipFile(appInfo.sourceDir).use { zipFile -> if (findAppsFromLibs(zipFile, libNames) || findAppsFromAssets(zipFile, assetNames)) { return true 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 d795b2e..19ded4e 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt @@ -15,10 +15,12 @@ import icu.nullptr.hidemyapplist.common.JsonConfig import icu.nullptr.hidemyapplist.common.RiskyPackageUtils.appHasGMSConnection import icu.nullptr.hidemyapplist.common.SettingsPresets import icu.nullptr.hidemyapplist.common.Utils +import icu.nullptr.hidemyapplist.common.app_presets.DetectorAppsPreset import icu.nullptr.hidemyapplist.common.app_presets.WebViewPreset import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem import icu.nullptr.hidemyapplist.xposed.hook.AccessibilityHook import icu.nullptr.hidemyapplist.xposed.hook.ActivityHook +import icu.nullptr.hidemyapplist.xposed.hook.AppDataIsolationHook import icu.nullptr.hidemyapplist.xposed.hook.ContentProviderHook import icu.nullptr.hidemyapplist.xposed.hook.IFrameworkHook import icu.nullptr.hidemyapplist.xposed.hook.ImmHook @@ -163,6 +165,7 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { frameworkHooks.add(PlatformCompatHook(this)) + frameworkHooks.add(AppDataIsolationHook(this)) } frameworkHooks.add(ActivityHook(this)) @@ -177,6 +180,12 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { fun isHookEnabled(packageName: String?) = config.scope.containsKey(packageName) + fun isAppDataIsolationExcluded(packageName: String?): Boolean { + if (packageName.isNullOrBlank()) return false + + return config.scope[packageName]?.excludeVoldIsolation ?: false + } + fun getSpoofedSetting(caller: String?, name: String?, database: String): ReplacementItem? { if (caller == null || name == null) return null @@ -233,8 +242,18 @@ class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { for (presetName in appConfig.applyPresets) { val preset = AppPresets.instance.getPresetByName(presetName) ?: continue + if (preset.containsPackage(query)) { + // Do not hide detector apps from Play Store if they are connected to GMS + val overriddenCaller = if (presetName == DetectorAppsPreset.NAME && caller == Constants.VENDING_PACKAGE_NAME) { + Constants.GMS_PACKAGE_NAME + } else { + caller + } + + return !isAppInGMSIgnoredPackages(overriddenCaller, query) } } + } return appConfig.useWhitelist } diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/AppDataIsolationHook.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/AppDataIsolationHook.kt new file mode 100644 index 0000000..2286493 --- /dev/null +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/AppDataIsolationHook.kt @@ -0,0 +1,222 @@ +package icu.nullptr.hidemyapplist.xposed.hook + +import android.os.Build +import android.os.SystemProperties +import androidx.annotation.RequiresApi +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.Utils +import icu.nullptr.hidemyapplist.xposed.HMAService +import icu.nullptr.hidemyapplist.xposed.logD +import icu.nullptr.hidemyapplist.xposed.logE +import icu.nullptr.hidemyapplist.xposed.logI +import org.frknkrc44.hma_oss.common.BuildConfig + +@RequiresApi(Build.VERSION_CODES.R) +class AppDataIsolationHook(private val service: HMAService): IFrameworkHook { + + companion object { + private const val TAG = "AppDataIsolationHook" + private const val APPDATA_ISOLATION_ENABLED = "mAppDataIsolationEnabled" + private const val VOLD_APPDATA_ISOLATION_ENABLED = "mVoldAppDataIsolationEnabled" + private const val FUSE_PROP = "persist.sys.fuse" + } + + private val hooks = mutableListOf() + private var voldHookSkipped = false + + override fun load() { + if (!(service.config.altAppDataIsolation || service.config.altVoldAppDataIsolation)) return + logI(TAG, "Load hook") + + findMethodOrNull( + "com.android.server.am.ProcessList" + ) { + name == "startProcess" + }?.hookBefore { param -> + if (service.config.altAppDataIsolation) { + val isEnabled = XposedHelpers.getBooleanField( + param.thisObject, + APPDATA_ISOLATION_ENABLED + ) + + if (!isEnabled) { + XposedHelpers.setBooleanField( + param.thisObject, + APPDATA_ISOLATION_ENABLED, + true + ) + + logI(TAG, "ProcessList - App data isolation is forced") + } + } + + if (service.config.altVoldAppDataIsolation && !voldHookSkipped) { + val fuseEnabled = SystemProperties.getBoolean(FUSE_PROP, false) + + if (!fuseEnabled) { + voldHookSkipped = true + logE(TAG, "ProcessList - FUSE storage is not enabled, skip vold hook") + } else { + val isolationEnabled = XposedHelpers.getBooleanField( + param.thisObject, + VOLD_APPDATA_ISOLATION_ENABLED + ) + + if (!isolationEnabled) { + XposedHelpers.setBooleanField( + param.thisObject, + VOLD_APPDATA_ISOLATION_ENABLED, + true + ) + + logI(TAG, "ProcessList - Vold app data isolation is forced") + } + } + } + }?.let { + hooks += it + } + + findMethodOrNull( + "com.android.server.am.ProcessList" + ) { + name == "needsStorageDataIsolation" + }?.hookAfter { param -> + if (service.config.altVoldAppDataIsolation) { + val app = param.args.find { it.javaClass.simpleName == "ProcessRecord" } + val uid = XposedHelpers.getIntField(app, "uid") + val processName = runCatching { + XposedHelpers.getObjectField(app, "processName") + }.getOrDefault("") + val mountNode = runCatching { + XposedHelpers.getIntField(app, "mMountMode") + }.getOrDefault(0) + val isolated = runCatching { + XposedHelpers.getBooleanField(app, "isolated") + }.getOrDefault(false) + val appZygote = runCatching { + XposedHelpers.getBooleanField(app, "appZygote") + }.getOrDefault(false) + + val apps = Utils.binderLocalScope { + service.pms.getPackagesForUid(uid) + } ?: return@hookAfter + + logD( + TAG, + "@needsStorageDataIsolation $uid and ${apps.contentToString()} - $processName value without override: ${param.result}, mount node: $mountNode, isolated: $isolated, appZygote: $appZygote" + ) + + // Do not isolate this module for safety + if (apps.contains(BuildConfig.APP_PACKAGE_NAME)) { + param.result = false + return@hookAfter + } + + if (apps.any { service.isAppDataIsolationExcluded(it) }) { + param.result = false + } + + if (service.config.skipSystemAppDataIsolation) { + val isSystemApp = service.systemApps.any { apps.contains(it) } + logD( + TAG, + "@needsStorageDataIsolation $uid and ${apps.contentToString()} - isSystemApp: $isSystemApp" + ) + + if (isSystemApp) { + param.result = false + return@hookAfter + } + } + } + }?.let { + hooks += it + } + + findMethodOrNull( + "com.android.server.StorageManagerService" + ) { + name == "onVolumeStateChangedLocked" + }?.hookBefore { param -> + runCatching { + if (service.config.altVoldAppDataIsolation) { + val fuseEnabled = SystemProperties.getBoolean(FUSE_PROP, false) + + if (!fuseEnabled) { + logE(TAG, "StorageManagerService - FUSE storage is not enabled, disable hooks") + unload() + return@hookBefore + } + + val isolationEnabled = XposedHelpers.getBooleanField( + param.thisObject, + VOLD_APPDATA_ISOLATION_ENABLED + ) + + if (!isolationEnabled) { + XposedHelpers.setBooleanField( + param.thisObject, + VOLD_APPDATA_ISOLATION_ENABLED, + true + ) + + logI(TAG, "StorageManagerService - Vold app data isolation is forced") + } + } + }.onFailure { + logE(TAG, "Fatal error occurred, disable hooks", it) + unload() + } + }?.let { + hooks += it + } + + findMethodOrNull( + "com.android.server.StorageManagerService" + ) { + name == "remountAppStorageDirs" + }?.hookBefore { param -> + if (service.config.altVoldAppDataIsolation && service.config.skipSystemAppDataIsolation) { + val pidPkgMap = param.args[0] as java.util.Map<*, *> + val userId = param.args[1] as Int + + val keysToRemove = mutableSetOf() + + for (entry in pidPkgMap.entrySet()) { + val pid = entry.key + val packageName = entry.value as String + + val apps = Utils.binderLocalScope { + val uid = Utils.getPackageUidCompat(service.pms, packageName, 0L, userId) + service.pms.getPackagesForUid(uid) + } ?: continue + + for (app in apps) { + if (app in service.systemApps || app == BuildConfig.APP_PACKAGE_NAME) { + logD( + TAG, + "@remountAppStorageDirs SYSTEM $pid - $packageName is marked to remove" + ) + keysToRemove += pid + break + } + } + } + + keysToRemove.forEach { pidPkgMap.remove(it) } + } + }?.let { + hooks += it + } + } + + override fun unload() { + hooks.forEach(XC_MethodHook.Unhook::unhook) + hooks.clear() + } +}