### Implement PMS Hooks for App Visibility and Installer Spoofing (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt This Kotlin code implements Xposed hooks for the Package Manager Service (PMS) to filter app visibility and spoof the installer package name. It targets different Android API levels and utilizes helper classes for Android framework interactions. The hooks intercept methods like `getPackageStates` and `getInstallerPackageName`. ```kotlin import android.content.pm.PackageManager import android.os.Binder import android.os.Build import android.os.UserHandle import android.util.ArrayMap import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.XposedHelpers.findMethod // Assuming HMAService, Constants, Utils4Xposed, IFrameworkHook, VENDING_PACKAGE_NAME, COMPUTER_ENGINE_CLASS are defined elsewhere abstract class PmsHookTargetBase(protected val service: HMAService) : IFrameworkHook { protected val hooks = mutableListOf() override fun load() { // Hook getPackageStates (Android 13+) to filter package lists if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { hooks += findMethod(COMPUTER_ENGINE_CLASS) { name == "getPackageStates" }.hookAfter { val callingUid = Binder.getCallingUid() if (callingUid == Constants.UID_SYSTEM) return@hookAfter val callingApps = Utils4Xposed.getCallingApps(service, callingUid) for (caller in callingApps) { if (service.isHookEnabled(caller)) { val result = it.result as ArrayMap<*, *> val markedToRemove = mutableListOf() for (pair in result.entries) { val packageName = XposedHelpers.callMethod( pair.value, "getPackageName") as String if (service.shouldHide(caller, packageName)) { markedToRemove.add(pair.key) } } if (markedToRemove.isNotEmpty()) { val copyResult = ArrayMap(result) copyResult.removeAll(markedToRemove) it.result = copyResult service.filterCount++ return@hookAfter } } } } } // Hook getInstallerPackageName to spoof installation source hooks += findMethod(service.pms::class.java, findSuper = true) { name == "getInstallerPackageName" }.hookBefore { val query = it.args[0] as String? val callingUid = Binder.getCallingUid() val callingApps = Utils4Xposed.getCallingApps(service, callingUid) val callingHandle = UserHandle.getUserHandleForUid(callingUid) for (caller in callingApps) { when (service.shouldHideInstallationSource(caller, query, callingHandle)) { Constants.FAKE_INSTALLATION_SOURCE_USER -> it.result = VENDING_PACKAGE_NAME // "com.android.vending" Constants.FAKE_INSTALLATION_SOURCE_SYSTEM -> it.result = null // System app indication } } } } override fun unload() { hooks.forEach(XC_MethodHook.Unhook::unhook) hooks.clear() } } ``` -------------------------------- ### Automatic App Preset Detection and Management (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt This Kotlin code defines the `AppPresets` class responsible for automatically detecting and managing app presets. It initializes various preset types at startup and provides methods to retrieve preset names, get specific presets, and handle package additions by updating relevant presets. Dependencies include Android's `IPackageManager` and `ApplicationInfo`. ```kotlin class AppPresets private constructor() { companion object { val instance by lazy { AppPresets() } } // Available presets initialized at startup init { presetList.add(CustomROMPreset()) // LineageOS, EvolutionX system apps presetList.add(DetectorAppsPreset()) // Root detection apps presetList.add(RootAppsPreset(this)) // Magisk, KernelSU, etc. presetList.add(XposedModulesPreset()) // LSPosed modules presetList.add(SuspiciousAppsPreset()) // File managers, etc. presetList.add(SDhizukuAppsPreset(this)) // Shizuku/Dhizuku apps } // Get all preset names fun getAllPresetNames() = presetList.map { it.name }.toTypedArray() // Get specific preset fun getPresetByName(name: String) = presetList.firstOrNull { it.name == name } // Handle package installation - add to relevant presets fun handlePackageAdded(pms: IPackageManager, packageName: String, holder: PresetCacheHolder): Boolean { var appInfo: ApplicationInfo? = null var addedInAList = false presetList.forEach { if (!it.containsPackage(packageName)) { if (appInfo == null) appInfo = getPackageInfoCompat(pms, packageName, 0, 0)?.applicationInfo if (appInfo != null && it.addPackageInfoPreset(appInfo!!)) { holder.presetPackageNames[it.name]?.add(packageName) addedInAList = true } } } return addedInAList } } // Example preset: XposedModulesPreset.kt class XposedModulesPreset : BasePreset() { override val name = "xposed_modules" override fun addPackageInfoPreset(appInfo: ApplicationInfo): Boolean { // Check if app has Xposed module metadata val metaData = appInfo.metaData ?: return false if (metaData.containsKey("xposedmodule") || metaData.containsKey("xposedminversion")) { packageNames.add(appInfo.packageName) return true } return false } } ``` -------------------------------- ### HMAService Core Hiding Logic in Kotlin Source: https://context7.com/frknkrc44/hma-oss/llms.txt Implements the core app hiding logic. It determines if a query from one app about another should be filtered based on templates, presets, and per-app configurations. It also handles installation source spoofing. ```kotlin class HMAService(val pms: IPackageManager, val pmn: Any?) : IHMAService.Stub() { // Check if hiding is enabled for a caller app fun isHookEnabled(packageName: String?) = config.scope.containsKey(packageName) // Main hiding logic - determines if 'query' should be hidden from 'caller' fun shouldHide(caller: String?, query: String?): Boolean { if (caller == null || query == null) return false if (caller == BuildConfig.APP_PACKAGE_NAME) return false if (caller in Constants.packagesShouldNotHide) return false if (caller == query) return false val appConfig = config.scope[caller] ?: return false // Check extra app lists first if (query in appConfig.extraAppList) return !appConfig.useWhitelist if (query in appConfig.extraOppositeAppList) return appConfig.useWhitelist // Check templates for (tplName in appConfig.applyTemplates) { val tpl = config.templates[tplName]!! if (query in tpl.appList) { // Protect GMS-connected apps from being hidden from Play services if (isAppInGMSIgnoredPackages(caller, query)) return false return !appConfig.useWhitelist } } // Check presets (auto-detected app categories) for (presetName in appConfig.applyPresets) { val preset = AppPresets.instance.getPresetByName(presetName) ?: continue if (preset.containsPackage(query)) { return !isAppInGMSIgnoredPackages(caller, query) } } // Whitelist mode: hide everything except whitelisted if (appConfig.useWhitelist && appConfig.excludeSystemApps && query in systemApps) return false return appConfig.useWhitelist } // Installation source spoofing fun shouldHideInstallationSource(caller: String?, query: String?, callingHandle: UserHandle): Int { val appConfig = config.scope[caller] ?: return Constants.FAKE_INSTALLATION_SOURCE_DISABLED if (!appConfig.hideInstallationSource) return Constants.FAKE_INSTALLATION_SOURCE_DISABLED return if (query in systemApps) { if (appConfig.hideSystemInstallationSource) Constants.FAKE_INSTALLATION_SOURCE_SYSTEM // Return "preload" else Constants.FAKE_INSTALLATION_SOURCE_DISABLED } else { Constants.FAKE_INSTALLATION_SOURCE_USER // Return Play Store } } } ``` -------------------------------- ### Restrict App Permissions at Zygote Fork (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt The ZygoteHook class restricts specific GID permissions when apps are forked by the Zygote process. It hooks into the 'start' method of the Zygote process to filter out restricted GIDs based on the calling app's package name and predefined constants. This prevents apps from accessing sensitive system resources. ```kotlin class ZygoteHook(private val service: HMAService): IFrameworkHook { override fun load() { findMethodOrNull(ZYGOTE_PROCESS_CLASS) { name == "start" }?.hookBefore { val gIDsIndex = param.args.indexOfFirst { it is IntArray } if (gIDsIndex < 0) return@hookBefore val caller = param.args.lastOrNull { it is String } as String? ?: return@hookBefore var perms = service.getRestrictedZygotePermissions(caller) ?: return@hookBefore if (perms.isNotEmpty()) { val gIDs = param.args[gIDsIndex] as IntArray // Filter only valid GIDs from Constants.GID_PAIRS perms = perms.filter { Constants.GID_PAIRS.containsValue(it) } // Remove restricted GIDs param.args[gIDsIndex] = gIDs.filter { it !in perms }.toIntArray() service.filterCount++ } } } } ``` -------------------------------- ### Xposed Module Initialization (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt XposedEntry.kt serves as the main entry point for the Xposed module. It initializes the framework and hooks into the Android system's ServiceManager to capture the Package Manager Service (PMS). Once PMS is registered, it initializes the HMA service. ```kotlin class XposedEntry : IXposedHookZygoteInit, IXposedHookLoadPackage { var targetsLeft = mutableListOf("package", "package_native") var targetStorage = mutableMapOf() override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) { EzXHelperInit.initZygote(startupParam) } override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { if (lpparam.packageName == "android") { EzXHelperInit.initHandleLoadPackage(lpparam) // Hook ServiceManager.addService to capture PMS var serviceManagerHook: XC_MethodHook.Unhook? = null serviceManagerHook = findMethod("android.os.ServiceManager") { name == "addService" }.hookBefore { val name = param.args[0] as String when (name) { "package", "package_native" -> { targetStorage[name] = param.args[1] targetsLeft.remove(name) } } if (targetsLeft.isEmpty()) { serviceManagerHook?.unhook() val pms = targetStorage["package"] as IPackageManager val pmn = targetStorage["package_native"] thread { runCatching { UserService.register(pms, pmn) logI(TAG, "User service started") }.onFailure { logE(TAG, "System service crashed", it) } } } } } } } ``` -------------------------------- ### Available GIDs for Restriction (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt Constants.kt defines a map of GID names to their corresponding integer IDs, representing various system permissions that can be restricted at the Zygote level. These include access to SD card, media storage, package information, internet, and external storage. ```kotlin val GID_PAIRS = mapOf( "SDCARD_RW_GID" to 1015, // SD card write access "MEDIA_RW_GID" to 1023, // Media storage write "PACKAGE_INFO_GID" to 1032, // Package info access "EXTERNAL_STORAGE_GID" to 1077, // USB OTG access "EXT_DATA_RW_GID" to 1078, // External app data "EXT_OBB_RW_GID" to 1079, // External OBB access "INET_GID" to 3003, // Internet access (use carefully!) "SHARED_USER_GID" to 9997 // Shared user storage ) ``` -------------------------------- ### Manage HMA-OSS Templates and App Configurations with ConfigManager Source: https://context7.com/frknkrc44/hma-oss/llms.txt Implements the ConfigManager object in Kotlin, providing a high-level API for managing HMA-OSS configurations. It includes functions to retrieve template lists, find apps associated with templates, update templates, rename templates across configurations, and set per-app configurations, ensuring config persistence and synchronization. ```kotlin // ConfigManager.kt - Template management operations object ConfigManager { enum class PTType { APP, SETTINGS } data class TemplateInfo(val name: String?, val type: PTType, val isWhiteList: Boolean) // Get all templates as list fun getTemplateList(): MutableList { return config.templates.mapTo(mutableListOf()) { TemplateInfo(it.key, PTType.APP, it.value.isWhitelist) } } // Get apps that have a specific template applied fun getTemplateAppliedAppList(name: String): ArrayList { return config.scope.mapNotNullTo(ArrayList()) { if (it.value.applyTemplates.contains(name)) it.key else null } } // Update template with new app list fun updateTemplate(name: String, template: JsonConfig.Template) { config.templates[name] = template saveConfig() } // Rename template across all configurations fun renameTemplate(oldName: String, newName: String) { if (oldName == newName) return config.scope.forEach { (_, appInfo) -> if (appInfo.applyTemplates.contains(oldName)) { appInfo.applyTemplates.remove(oldName) appInfo.applyTemplates.add(newName) } } config.templates[newName] = config.templates[oldName]!! config.templates.remove(oldName) saveConfig() } // Set per-app configuration fun setAppConfig(packageName: String, appConfig: JsonConfig.AppConfig?) { if (appConfig == null) config.scope.remove(packageName) else config.scope[packageName] = appConfig saveConfig() } } ``` -------------------------------- ### Define Developer Options Settings Preset (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt This Kotlin class defines a preset for spoofing developer options settings. It extends a base preset class and overrides the `getSpoofedValue` method to return `ReplacementItem` objects that hide or modify specific developer settings like `adb_enabled` and `development_settings_enabled`. ```kotlin // Assuming BasePreset, ReplacementItem, Constants are defined elsewhere class DeveloperOptionsPreset : BasePreset() { override val name = "developer_options" override fun getSpoofedValue(settingName: String): ReplacementItem? { return when (settingName) { "adb_enabled" -> ReplacementItem("adb_enabled", "0", Constants.SETTINGS_GLOBAL) "development_settings_enabled" -> ReplacementItem("development_settings_enabled", "0", Constants.SETTINGS_GLOBAL) else -> null } } } ``` -------------------------------- ### Implement Settings Spoofing Logic (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt This Kotlin function retrieves spoofed setting values based on the caller application, setting name, and database. It first checks user-defined templates and then falls back to built-in presets to determine the appropriate replacement value or if the setting should be hidden. ```kotlin // Assuming HMAService, Constants, ReplacementItem, SettingsPresets, BasePreset are defined elsewhere fun HMAService.getSpoofedSetting(caller: String?, name: String?, database: String): ReplacementItem? { if (caller == null || name == null) return null // Check settings templates first (user-defined) val templates = getEnabledSettingsTemplates(caller) val replacement = config.settingsTemplates.firstNotNullOfOrNull { (key, value) -> if (key in templates) value.settingsList.firstOrNull { it.name == name } else null } if (replacement != null) return replacement // Check settings presets (built-in categories) val presets = getEnabledSettingsPresets(caller) for (presetName in presets) { val preset = SettingsPresets.instance.getPresetByName(presetName) val spoofedValue = preset?.getSpoofedValue(name) if (spoofedValue?.database == database) return spoofedValue } return null } ``` -------------------------------- ### Define Settings Replacement Item for Spoofing (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt This Kotlin data class defines a structure for representing a setting replacement item. It includes the setting's name, its replacement value (or null to hide), and the database where the setting resides (global, secure, or system). This is used within the settings spoofing system. ```kotlin import kotlinx.serialization.Serializable // Assuming Constants is defined elsewhere @Serializable data class ReplacementItem( val name: String, // Settings key (e.g., "adb_enabled") val value: String?, // Replacement value (null to hide) val database: String = Constants.SETTINGS_GLOBAL // global/secure/system ) ``` -------------------------------- ### Manage Service Connection (Kotlin) Source: https://context7.com/frknkrc44/hma-oss/llms.txt Manages the connection to the HMA system service from the Android app using Binder IPC. It implements the `IHMAService` interface and handles service death notifications. ```kotlin // Connecting to HMA service from the app object ServiceClient : IHMAService, IBinder.DeathRecipient { @Volatile private var service: IHMAService? = null // Link to service when binder is received from ContentProvider fun linkService(binder: IBinder) { service = Proxy.newProxyInstance( javaClass.classLoader, arrayOf(IHMAService::class.java), ServiceProxy(IHMAService.Stub.asInterface(binder)) ) as IHMAService binder.linkToDeath(this, 0) } // Service method delegation override fun getServiceVersion() = service?.serviceVersion ?: 0 override fun getFilterCount() = service?.filterCount ?: 0 override fun getLogs() = service?.logs override fun readConfig() = service?.readConfig() override fun writeConfig(json: String) { service?.writeConfig(json) } // Example: Get packages for a specific preset override fun getPackagesForPreset(presetName: String) = service?.getPackagesForPreset(presetName) // Handle service death override fun binderDied() { service = null Log.e(TAG, "Binder died") } } ``` -------------------------------- ### IHMAService AIDL Interface Source: https://context7.com/frknkrc44/hma-oss/llms.txt The core AIDL interface for communication between the HMA app and the Xposed module. It exposes methods for managing configurations, logs, package queries, and service control. ```APIDOC ## IHMAService AIDL Interface ### Description The core service interface that enables communication between the HMA app and the Xposed module running in the system process. This AIDL interface provides methods for configuration management, logging, package queries, and service control. ### Methods * **stopService**(boolean stopService) * Description: Stops the service and optionally cleans the environment. * Parameters: * stopService (boolean) - Required - Whether to clean the environment upon stopping. * **writeConfig**(String json) * Description: Writes the provided JSON configuration to the service. * Parameters: * json (String) - Required - The JSON configuration string. * **getServiceVersion**() * Description: Retrieves the current version of the service. * Returns: (int) The service version. * **getFilterCount**() * Description: Gets the total count of filtered queries. * Returns: (int) The number of filtered queries. * **getLogs**() * Description: Retrieves the runtime logs from the service. * Returns: (String) The log content. * **clearLogs**() * Description: Clears the log file maintained by the service. * **handlePackageEvent**(String eventType, String packageName, Bundle extras) * Description: Handles package-related events. * Parameters: * eventType (String) - Required - The type of package event. * packageName (String) - Required - The name of the package involved. * extras (Bundle) - Optional - Additional event details. * **getPackagesForPreset**(String presetName) * Description: Retrieves a list of package names associated with a given preset. * Parameters: * presetName (String) - Required - The name of the preset. * Returns: (String[]) An array of package names. * **readConfig**() * Description: Reads the current configuration from the service as a JSON string. * Returns: (String) The current configuration in JSON format. * **forceStop**(String packageName, int userId) * Description: Forcefully stops a specified application for a given user. * Parameters: * packageName (String) - Required - The name of the package to stop. * userId (int) - Required - The user ID. * **log**(int level, String tag, String message) * Description: Writes a log message with a specified level and tag. * Parameters: * level (int) - Required - The log level. * tag (String) - Required - The log tag. * message (String) - Required - The log message. * **getPackageNames**(int userId) * Description: Lists all installed packages for a given user. * Parameters: * userId (int) - Required - The user ID. * Returns: (String[]) An array of package names. * **getPackageInfo**(String packageName, int userId) * Description: Retrieves detailed information about a specific package for a given user. * Parameters: * packageName (String) - Required - The name of the package. * userId (int) - Required - The user ID. * Returns: (PackageInfo) The package information object. * **listAllSettings**(String databaseName) * Description: Lists all setting keys within a specified database. * Parameters: * databaseName (String) - Required - The name of the database. * Returns: (String[]) An array of setting keys. * **getLogFileLocation**() * Description: Retrieves the file path of the current log file. * Returns: (String) The log file path. * **reloadPresetsFromScratch**() * Description: Forces a reload of all presets from scratch. ``` -------------------------------- ### Define Core Service Interface (AIDL) Source: https://context7.com/frknkrc44/hma-oss/llms.txt Defines the AIDL interface for the core HMA service, enabling communication between the HMA app and the Xposed module. It includes methods for configuration, logging, package queries, and service control. ```aidl // IHMAService.aidl - Core service interface interface IHMAService { void stopService(boolean cleanEnv); // Stop service, optionally clean data void writeConfig(String json); // Write JSON configuration int getServiceVersion(); // Get current service version int getFilterCount(); // Get total filtered query count String getLogs(); // Retrieve runtime logs void clearLogs(); // Clear log file void handlePackageEvent(String eventType, String packageName, in Bundle extras); String[] getPackagesForPreset(String presetName); // Get packages in preset String readConfig(); // Read current config as JSON void forceStop(String packageName, int userId); // Force stop an app void log(int level, String tag, String message); // Write to log String[] getPackageNames(int userId); // List all packages PackageInfo getPackageInfo(String packageName, int userId); String[] listAllSettings(String databaseName); // List settings keys String getLogFileLocation(); // Get log file path void reloadPresetsFromScratch(); // Reload all presets } ``` -------------------------------- ### Define HMA-OSS JsonConfig Structure with Kotlin Serialization Source: https://context7.com/frknkrc44/hma-oss/llms.txt Defines the main configuration data class 'JsonConfig' using Kotlin serialization for managing HMA settings. It includes nested data classes for 'Template' and 'AppConfig', and a companion object for JSON parsing. This structure handles various settings like logging, isolation, and app-specific configurations. ```kotlin import kotlinx.serialization.Serializable // JsonConfig.kt - Main configuration data class @Serializable data class JsonConfig( var configVersion: Int = BuildConfig.CONFIG_VERSION, var detailLog: Boolean = false, var errorOnlyLog: Boolean = false, var maxLogSize: Int = 512, 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(), val settingsTemplates: MutableMap = mutableMapOf(), val scope: MutableMap = mutableMapOf() ) { // Template for app hiding lists @Serializable data class Template( val isWhitelist: Boolean, // true = only show these apps, false = hide these apps val appList: Set // Package names in this template ) // Per-app configuration @Serializable data class AppConfig( var useWhitelist: Boolean = false, var excludeSystemApps: Boolean = true, var hideInstallationSource: Boolean = false, var invertActivityLaunchProtection: Boolean = false, var applyTemplates: MutableSet = mutableSetOf(), var applyPresets: MutableSet = mutableSetOf(), var applySettingTemplates: MutableSet = mutableSetOf(), var applySettingsPresets: MutableSet = mutableSetOf(), var extraAppList: MutableSet = mutableSetOf(), var restrictedZygotePermissions: List = listOf() ) companion object { fun parse(json: String) = encoder.decodeFromString(json) } } // Example JSON configuration /* { "configVersion": 93, "detailLog": true, "templates": { "Root Apps": { "isWhitelist": false, "appList": ["com.topjohnwu.magisk", "me.weishu.kernelsu"] } }, "scope": { "com.banking.app": { "useWhitelist": false, "applyTemplates": ["Root Apps"], "applyPresets": ["detector_apps", "xposed_modules"] } } } */ ``` -------------------------------- ### ServiceClient Connection Source: https://context7.com/frknkrc44/hma-oss/llms.txt Details on how the ServiceClient object manages the connection to the HMA system service using Android's Binder IPC mechanism. ```APIDOC ## ServiceClient Connection ### Description The ServiceClient object manages the connection between the HMA app and the system service. It uses Android's Binder IPC mechanism to communicate with the Xposed module running in the system_server process. ### Methods * **linkService**(IBinder binder) * Description: Links the ServiceClient to the HMA service using the provided binder. * Parameters: * binder (IBinder) - Required - The binder received from the ContentProvider. * **getServiceVersion**() * Description: Gets the current service version. * Returns: (int) The service version, or 0 if not connected. * **getFilterCount**() * Description: Gets the total count of filtered queries. * Returns: (int) The filter count, or 0 if not connected. * **getLogs**() * Description: Retrieves the runtime logs. * Returns: (String?) The log content, or null if not connected. * **readConfig**() * Description: Reads the current configuration as JSON. * Returns: (String?) The configuration JSON, or null if not connected. * **writeConfig**(String json) * Description: Writes the provided JSON configuration to the service. * Parameters: * json (String) - Required - The JSON configuration string. * **getPackagesForPreset**(String presetName) * Description: Gets packages for a specific preset. * Parameters: * presetName (String) - Required - The name of the preset. * Returns: (String[]?) An array of package names, or null if not connected. ### Callbacks * **binderDied**() * Description: Callback method invoked when the service binder dies. Resets the service connection. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.