mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-04 09:45:19 +08:00
Moved YukiHookBridge some apps and host function into AppParasitics
This commit is contained in:
@@ -39,8 +39,8 @@ import android.widget.ImageView
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||
import java.io.BufferedReader
|
||||
@@ -124,7 +124,7 @@ fun Context.injectModuleAppResources() = resources?.injectModuleAppResources()
|
||||
*
|
||||
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
||||
*/
|
||||
fun Resources.injectModuleAppResources() = YukiHookBridge.injectModuleAppResources(hostResources = this)
|
||||
fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResources(hostResources = this)
|
||||
|
||||
/**
|
||||
* 仅判断模块是否在太极、无极中激活
|
||||
|
@@ -32,6 +32,7 @@ package com.highcapable.yukihookapi.hook.log
|
||||
import android.util.Log
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
|
||||
/**
|
||||
@@ -91,7 +92,7 @@ private fun baseLogger(format: String, type: LoggerType, tag: String, msg: Strin
|
||||
/** 打印到 [XposedBridge.log] */
|
||||
fun loggerInXposed() = runCatching {
|
||||
YukiHookBridge.hostProcessName.also {
|
||||
val appUserId = YukiHookBridge.findUserId(it)
|
||||
val appUserId = AppParasitics.findUserId(it)
|
||||
XposedBridge.log("[$tag][$format]${if (isShowProcessName) (if (appUserId != 0) "[$it][$appUserId]" else "[$it]") else ""}--> $msg")
|
||||
e?.also { e -> XposedBridge.log(e) }
|
||||
}
|
||||
|
@@ -48,11 +48,11 @@ import com.highcapable.yukihookapi.hook.factory.hasClass
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.utils.value
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同
|
||||
* @return [Int]
|
||||
*/
|
||||
val appUserId get() = YukiHookBridge.findUserId(packageName)
|
||||
val appUserId get() = AppParasitics.findUserId(packageName)
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的 [Application] 实例
|
||||
@@ -91,7 +91,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @return [Application]
|
||||
* @throws IllegalStateException 如果 [Application] 是空的
|
||||
*/
|
||||
val appContext get() = YukiHookBridge.hostApplication ?: YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext")
|
||||
val appContext get() = AppParasitics.hostApplication ?: YukiHookAppHelper.currentApplication() ?: error("PackageParam got null appContext")
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的 Resources
|
||||
@@ -107,7 +107,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @return [Context] ContextImpl 实例对象
|
||||
* @throws IllegalStateException 如果获取不到系统框架的 [Context]
|
||||
*/
|
||||
val systemContext get() = YukiHookBridge.systemContext
|
||||
val systemContext get() = AppParasitics.systemContext
|
||||
|
||||
/**
|
||||
* 获取当前 Hook APP 的进程名称
|
||||
@@ -143,7 +143,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* - ❗作为 Hook API 装载时无法使用 - 会获取到空字符串
|
||||
* @return [String]
|
||||
*/
|
||||
val moduleAppFilePath get() = YukiHookBridge.moduleAppFilePath
|
||||
val moduleAppFilePath get() = AppParasitics.moduleAppFilePath
|
||||
|
||||
/**
|
||||
* 获取当前 Xposed 模块自身 [Resources]
|
||||
@@ -153,8 +153,8 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @throws IllegalStateException 如果当前 Hook Framework 不支持此功能
|
||||
*/
|
||||
val moduleAppResources
|
||||
get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) YukiHookBridge.moduleAppResources
|
||||
else YukiHookBridge.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources")
|
||||
get() = (if (YukiHookAPI.Configs.isEnableModuleAppResourcesCache) AppParasitics.moduleAppResources
|
||||
else AppParasitics.dynamicModuleAppResources) ?: error("Current Hook Framework not support moduleAppResources")
|
||||
|
||||
/**
|
||||
* 获得当前使用的存取数据对象缓存实例
|
||||
@@ -202,7 +202,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
fun resources() = HookResources(wrapper?.appResources)
|
||||
|
||||
/** 刷新当前 Xposed 模块自身 [Resources] */
|
||||
fun refreshModuleAppResources() = YukiHookBridge.refreshModuleAppResources()
|
||||
fun refreshModuleAppResources() = AppParasitics.refreshModuleAppResources()
|
||||
|
||||
/**
|
||||
* 监听当前 Hook APP 生命周期装载事件
|
||||
@@ -244,13 +244,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* 装载并 Hook 系统框架
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
inline fun loadSystem(initiate: PackageParam.() -> Unit) = loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, initiate)
|
||||
inline fun loadSystem(initiate: PackageParam.() -> Unit) = loadApp(AppParasitics.SYSTEM_FRAMEWORK_NAME, initiate)
|
||||
|
||||
/**
|
||||
* 装载并 Hook 系统框架
|
||||
* @param hooker Hook 子类
|
||||
*/
|
||||
fun loadSystem(hooker: YukiBaseHooker) = loadApp(YukiHookBridge.SYSTEM_FRAMEWORK_NAME, hooker)
|
||||
fun loadSystem(hooker: YukiBaseHooker) = loadApp(AppParasitics.SYSTEM_FRAMEWORK_NAME, hooker)
|
||||
|
||||
/**
|
||||
* 装载 APP Zygote 事件
|
||||
@@ -356,7 +356,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* - ❗这是一个实验性功能 - 一般情况下不会用到此方法 - 不保证不会发生错误
|
||||
* @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve)
|
||||
*/
|
||||
fun ClassLoader.fetching(result: (clazz: Class<*>, resolve: Boolean) -> Unit) = YukiHookBridge.hookClassLoader(loader = this, result)
|
||||
fun ClassLoader.fetching(result: (clazz: Class<*>, resolve: Boolean) -> Unit) = AppParasitics.hookClassLoader(loader = this, result)
|
||||
|
||||
/**
|
||||
* Hook 方法、构造方法
|
||||
@@ -439,7 +439,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @param result 回调 - ([Context] baseContext,[Boolean] 是否已执行 super)
|
||||
*/
|
||||
fun attachBaseContext(result: (baseContext: Context, hasCalledSuper: Boolean) -> Unit) {
|
||||
YukiHookBridge.AppLifecycleCallback.attachBaseContextCallback = result
|
||||
AppParasitics.AppLifecycleCallback.attachBaseContextCallback = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,7 +447,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
fun onCreate(initiate: Application.() -> Unit) {
|
||||
YukiHookBridge.AppLifecycleCallback.onCreateCallback = initiate
|
||||
AppParasitics.AppLifecycleCallback.onCreateCallback = initiate
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,7 +455,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
fun onTerminate(initiate: Application.() -> Unit) {
|
||||
YukiHookBridge.AppLifecycleCallback.onTerminateCallback = initiate
|
||||
AppParasitics.AppLifecycleCallback.onTerminateCallback = initiate
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,7 +463,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
fun onLowMemory(initiate: Application.() -> Unit) {
|
||||
YukiHookBridge.AppLifecycleCallback.onLowMemoryCallback = initiate
|
||||
AppParasitics.AppLifecycleCallback.onLowMemoryCallback = initiate
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,7 +471,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @param result 回调 - ([Application] 当前实例,[Int] 类型)
|
||||
*/
|
||||
fun onTrimMemory(result: (self: Application, level: Int) -> Unit) {
|
||||
YukiHookBridge.AppLifecycleCallback.onTrimMemoryCallback = result
|
||||
AppParasitics.AppLifecycleCallback.onTrimMemoryCallback = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,7 +479,7 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @param result 回调 - ([Application] 当前实例,[Configuration] 配置实例)
|
||||
*/
|
||||
fun onConfigurationChanged(result: (self: Application, config: Configuration) -> Unit) {
|
||||
YukiHookBridge.AppLifecycleCallback.onConfigurationChangedCallback = result
|
||||
AppParasitics.AppLifecycleCallback.onConfigurationChangedCallback = result
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,13 +488,13 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
|
||||
* @param result 回调 - ([Context] 当前上下文,[Intent] 当前 Intent)
|
||||
*/
|
||||
fun registerReceiver(vararg action: String, result: (context: Context, intent: Intent) -> Unit) {
|
||||
if (action.isNotEmpty()) YukiHookBridge.AppLifecycleCallback.onReceiversCallback[action.value()] = Pair(action, result)
|
||||
if (action.isNotEmpty()) AppParasitics.AppLifecycleCallback.onReceiversCallback[action.value()] = Pair(action, result)
|
||||
}
|
||||
|
||||
/** 设置创建生命周期监听回调 */
|
||||
@PublishedApi
|
||||
internal fun build() {
|
||||
YukiHookBridge.AppLifecycleCallback.isCallbackSetUp = true
|
||||
AppParasitics.AppLifecycleCallback.isCallbackSetUp = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -29,38 +29,24 @@
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.bridge
|
||||
|
||||
import android.app.Application
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.annotation.YukiGenerateApi
|
||||
import com.highcapable.yukihookapi.hook.factory.*
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.factory.field
|
||||
import com.highcapable.yukihookapi.hook.factory.hasClass
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.type.android.*
|
||||
import com.highcapable.yukihookapi.hook.type.java.BooleanType
|
||||
import com.highcapable.yukihookapi.hook.type.java.IntType
|
||||
import com.highcapable.yukihookapi.hook.type.java.JavaClassLoader
|
||||
import com.highcapable.yukihookapi.hook.type.java.StringType
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberReplacement
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.inject.YukiHookBridge_Injector
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
import dalvik.system.PathClassLoader
|
||||
import de.robv.android.xposed.IXposedHookInitPackageResources
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||
@@ -79,16 +65,9 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
@YukiGenerateApi
|
||||
object YukiHookBridge {
|
||||
|
||||
/** Android 系统框架名称 */
|
||||
@PublishedApi
|
||||
internal const val SYSTEM_FRAMEWORK_NAME = "android"
|
||||
|
||||
/** Xposed 是否装载完成 */
|
||||
private var isXposedInitialized = false
|
||||
|
||||
/** [YukiHookDataChannel] 是否已经注册 */
|
||||
private var isDataChannelRegister = false
|
||||
|
||||
/** 当前 Hook 进程是否正处于 [IXposedHookZygoteInit.initZygote] */
|
||||
private var isInitializingZygote = false
|
||||
|
||||
@@ -98,49 +77,12 @@ object YukiHookBridge {
|
||||
/** 当前 [PackageParamWrapper] 实例数组 */
|
||||
private val packageParamWrappers = HashMap<String, PackageParamWrapper>()
|
||||
|
||||
/** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */
|
||||
private val injectedHostResourcesHashCodes = HashSet<Int>()
|
||||
|
||||
/** 当前 [PackageParam] 方法体回调 */
|
||||
internal var packageParamCallback: (PackageParam.() -> Unit)? = null
|
||||
|
||||
/** 当前 Hook Framework 是否支持 Resources Hook */
|
||||
internal var isSupportResourcesHook = false
|
||||
|
||||
/**
|
||||
* 当前 Hook APP (宿主) 的全局生命周期 [Application]
|
||||
*
|
||||
* 需要 [YukiHookAPI.Configs.isEnableDataChannel] 或 [AppLifecycleCallback.isCallbackSetUp] 才会生效
|
||||
*/
|
||||
internal var hostApplication: Application? = null
|
||||
|
||||
/** 当前 Xposed 模块自身 APK 路径 */
|
||||
internal var moduleAppFilePath = ""
|
||||
|
||||
/** 当前 Xposed 模块自身 [Resources] */
|
||||
internal var moduleAppResources: YukiModuleResources? = null
|
||||
|
||||
/**
|
||||
* 当前环境中使用的 [ClassLoader]
|
||||
*
|
||||
* 装载位于 (Xposed) 宿主环境与模块环境时均使用当前 DEX 内的 [ClassLoader]
|
||||
* @return [ClassLoader]
|
||||
* @throws IllegalStateException 如果 [ClassLoader] 为空
|
||||
*/
|
||||
internal val baseClassLoader get() = classOf<YukiHookAPI>().classLoader ?: error("Operating system not supported")
|
||||
|
||||
/**
|
||||
* 获取当前 Xposed 模块自身动态 [Resources]
|
||||
* @return [YukiModuleResources] or null
|
||||
*/
|
||||
internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull()
|
||||
|
||||
/**
|
||||
* 自动生成的 Xposed 模块构建版本号
|
||||
* @return [String]
|
||||
*/
|
||||
internal val moduleGeneratedVersion get() = YukiHookBridge_Injector.getModuleGeneratedVersion()
|
||||
|
||||
/**
|
||||
* 当前宿主正在进行的 Hook 进程标识名称
|
||||
* @return [String]
|
||||
@@ -148,14 +90,18 @@ object YukiHookBridge {
|
||||
internal val hostProcessName get() = if (isInitializingZygote) "android-zygote" else YukiHookAppHelper.currentPackageName() ?: "unknown"
|
||||
|
||||
/**
|
||||
* 获取当前系统框架的 [Context]
|
||||
* @return [Context] ContextImpl 实例对象
|
||||
* @throws IllegalStateException 如果获取不到系统框架的 [Context]
|
||||
* 自动生成的 Xposed 模块构建版本号
|
||||
* @return [String]
|
||||
*/
|
||||
internal val systemContext
|
||||
get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let {
|
||||
ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke<Context?>()
|
||||
} ?: error("Failed to got SystemContext")
|
||||
internal val moduleGeneratedVersion get() = YukiHookBridge_Injector.getModuleGeneratedVersion()
|
||||
|
||||
/**
|
||||
* 预设的 Xposed 模块包名
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
var modulePackageName = ""
|
||||
|
||||
/**
|
||||
* 模块是否装载了 Xposed 回调方法
|
||||
@@ -167,14 +113,6 @@ object YukiHookBridge {
|
||||
val isXposedCallbackSetUp
|
||||
get() = isXposedInitialized.not() && packageParamCallback != null
|
||||
|
||||
/**
|
||||
* 预设的 Xposed 模块包名
|
||||
*
|
||||
* - ❗装载代码将自动生成 - 请勿手动修改 - 会引发未知异常
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
var modulePackageName = ""
|
||||
|
||||
/**
|
||||
* 获取当前 Hook 框架的名称
|
||||
*
|
||||
@@ -201,19 +139,6 @@ object YukiHookBridge {
|
||||
*/
|
||||
internal val hasXposedBridge get() = executorVersion >= 0
|
||||
|
||||
/**
|
||||
* 获取指定 [packageName] 的用户 ID
|
||||
*
|
||||
* 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同
|
||||
* @param packageName 当前包名
|
||||
* @return [Int]
|
||||
*/
|
||||
internal fun findUserId(packageName: String) =
|
||||
UserHandleClass.method {
|
||||
name = "getUserId"
|
||||
param(IntType)
|
||||
}.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid)
|
||||
|
||||
/**
|
||||
* 自动忽略 MIUI 系统可能出现的日志收集注入实例
|
||||
* @param packageName 当前包名
|
||||
@@ -259,12 +184,12 @@ object YukiHookBridge {
|
||||
if (type == HookEntryType.ZYGOTE || appClassLoader != null)
|
||||
PackageParamWrapper(
|
||||
type = type,
|
||||
packageName = packageName ?: SYSTEM_FRAMEWORK_NAME,
|
||||
processName = processName ?: SYSTEM_FRAMEWORK_NAME,
|
||||
packageName = packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME,
|
||||
processName = processName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME,
|
||||
appClassLoader = appClassLoader ?: XposedBridge.BOOTCLASSLOADER,
|
||||
appInfo = appInfo,
|
||||
appResources = appResources
|
||||
).also { packageParamWrappers[packageName ?: SYSTEM_FRAMEWORK_NAME] = it }
|
||||
).also { packageParamWrappers[packageName ?: AppParasitics.SYSTEM_FRAMEWORK_NAME] = it }
|
||||
else null
|
||||
else packageParamWrappers[packageName]?.also {
|
||||
it.type = type
|
||||
@@ -276,125 +201,6 @@ object YukiHookBridge {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入当前 Hook APP (宿主) 全局生命周期
|
||||
* @param packageName 包名
|
||||
*/
|
||||
private fun registerToAppLifecycle(packageName: String) {
|
||||
/** Hook [Application] 装载方法 */
|
||||
runCatching {
|
||||
if (AppLifecycleCallback.isCallbackSetUp) {
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(ContextClass) }, object : YukiMemberHook() {
|
||||
override fun beforeHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onTerminate" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onLowMemory" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
val self = wrapper.instance as? Application? ?: return
|
||||
val type = wrapper.args?.get(0) as? Int? ?: return
|
||||
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type)
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
val self = wrapper.instance as? Application? ?: return
|
||||
val config = wrapper.args?.get(0) as? Configuration? ?: return
|
||||
AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config)
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
}
|
||||
if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp)
|
||||
YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.args?.get(0) as? Application?)?.also {
|
||||
hostApplication = it
|
||||
AppLifecycleCallback.onCreateCallback?.invoke(it)
|
||||
AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) ->
|
||||
if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (context == null || intent == null) return
|
||||
if (e.first.any { e -> e == intent.action }) e.second(context, intent)
|
||||
}
|
||||
}, IntentFilter().apply { e.first.forEach { e -> addAction(e) } })
|
||||
}
|
||||
if (isDataChannelRegister) return
|
||||
isDataChannelRegister = true
|
||||
runCatching { YukiHookDataChannel.instance().register(it, packageName) }
|
||||
}
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向 Hook APP (宿主) 注入当前 Xposed 模块的资源
|
||||
* @param hostResources 需要注入的宿主 [Resources]
|
||||
*/
|
||||
internal fun injectModuleAppResources(hostResources: Resources) {
|
||||
if (injectedHostResourcesHashCodes.contains(hostResources.hashCode())) return
|
||||
if (hasXposedBridge) runCatching {
|
||||
hostResources.assets.current {
|
||||
method {
|
||||
name = "addAssetPath"
|
||||
param(StringType)
|
||||
}.call(moduleAppFilePath)
|
||||
}
|
||||
injectedHostResourcesHashCodes.add(hostResources.hashCode())
|
||||
}.onFailure {
|
||||
yLoggerE(msg = "Failed to inject module resources into [$hostResources]", e = it)
|
||||
} else yLoggerW(msg = "You can only inject module resources in Xposed Environment")
|
||||
}
|
||||
|
||||
/** 刷新当前 Xposed 模块自身 [Resources] */
|
||||
internal fun refreshModuleAppResources() {
|
||||
dynamicModuleAppResources?.let { moduleAppResources = it }
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法
|
||||
* @param loader 当前 [ClassLoader]
|
||||
* @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve)
|
||||
*/
|
||||
internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) {
|
||||
runCatching {
|
||||
YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringType, BooleanType) }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
if (wrapper.instance?.javaClass?.name == loader?.javaClass?.name)
|
||||
(wrapper.result as? Class<*>?)?.also { result(it, wrapper.args?.get(1) as? Boolean ?: false) }
|
||||
}
|
||||
})
|
||||
}.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 模块自身激活状态和 Resources Hook 支持状态
|
||||
*
|
||||
@@ -441,8 +247,8 @@ object YukiHookBridge {
|
||||
*/
|
||||
@YukiGenerateApi
|
||||
fun callXposedZygoteLoaded(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleAppFilePath = sparam.modulePath
|
||||
refreshModuleAppResources()
|
||||
AppParasitics.moduleAppFilePath = sparam.modulePath
|
||||
AppParasitics.refreshModuleAppResources()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,7 +274,7 @@ object YukiHookBridge {
|
||||
resparam: XC_InitPackageResources.InitPackageResourcesParam? = null
|
||||
) {
|
||||
if (isMiuiCatcherPatch(packageName = lpparam?.packageName ?: resparam?.packageName).not()) when {
|
||||
isZygoteLoaded -> assignWrapper(HookEntryType.ZYGOTE, SYSTEM_FRAMEWORK_NAME, SYSTEM_FRAMEWORK_NAME)
|
||||
isZygoteLoaded -> assignWrapper(HookEntryType.ZYGOTE, AppParasitics.SYSTEM_FRAMEWORK_NAME, AppParasitics.SYSTEM_FRAMEWORK_NAME)
|
||||
lpparam != null ->
|
||||
if (isPackageLoaded(lpparam.packageName, HookEntryType.PACKAGE).not())
|
||||
assignWrapper(HookEntryType.PACKAGE, lpparam.packageName, lpparam.processName, lpparam.classLoader, lpparam.appInfo)
|
||||
@@ -480,38 +286,8 @@ object YukiHookBridge {
|
||||
else -> null
|
||||
}?.also {
|
||||
YukiHookAPI.onXposedLoaded(it)
|
||||
if (it.type == HookEntryType.PACKAGE) registerToAppLifecycle(it.packageName)
|
||||
if (it.type == HookEntryType.PACKAGE) AppParasitics.registerToAppLifecycle(it.packageName)
|
||||
if (it.type == HookEntryType.RESOURCES) isSupportResourcesHook = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前 Hook APP (宿主) 的生命周期回调处理类
|
||||
*/
|
||||
internal object AppLifecycleCallback {
|
||||
|
||||
/** 是否已设置回调 */
|
||||
internal var isCallbackSetUp = false
|
||||
|
||||
/** [Application.attachBaseContext] 回调 */
|
||||
internal var attachBaseContextCallback: ((Context, Boolean) -> Unit)? = null
|
||||
|
||||
/** [Application.onCreate] 回调 */
|
||||
internal var onCreateCallback: (Application.() -> Unit)? = null
|
||||
|
||||
/** [Application.onTerminate] 回调 */
|
||||
internal var onTerminateCallback: (Application.() -> Unit)? = null
|
||||
|
||||
/** [Application.onLowMemory] 回调 */
|
||||
internal var onLowMemoryCallback: (Application.() -> Unit)? = null
|
||||
|
||||
/** [Application.onTrimMemory] 回调 */
|
||||
internal var onTrimMemoryCallback: ((Application, Int) -> Unit)? = null
|
||||
|
||||
/** [Application.onConfigurationChanged] 回调 */
|
||||
internal var onConfigurationChangedCallback: ((Application, Configuration) -> Unit)? = null
|
||||
|
||||
/** 系统广播监听回调 */
|
||||
internal val onReceiversCallback = HashMap<String, Pair<Array<out String>, (Context, Intent) -> Unit>>()
|
||||
}
|
||||
}
|
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/14.
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
||||
import com.highcapable.yukihookapi.hook.type.android.*
|
||||
import com.highcapable.yukihookapi.hook.type.java.BooleanType
|
||||
import com.highcapable.yukihookapi.hook.type.java.IntType
|
||||
import com.highcapable.yukihookapi.hook.type.java.JavaClassLoader
|
||||
import com.highcapable.yukihookapi.hook.type.java.StringType
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||
|
||||
/**
|
||||
* 这是一个管理 APP 寄生功能的控制类
|
||||
*
|
||||
* 通过这些功能即可轻松实现对 (Xposed) 宿主环境的 [Resources] 注入以及 [Activity] 代理
|
||||
*/
|
||||
@PublishedApi
|
||||
internal object AppParasitics {
|
||||
|
||||
/** Android 系统框架名称 */
|
||||
@PublishedApi
|
||||
internal const val SYSTEM_FRAMEWORK_NAME = "android"
|
||||
|
||||
/** [YukiHookDataChannel] 是否已经注册 */
|
||||
private var isDataChannelRegister = false
|
||||
|
||||
/** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */
|
||||
private val injectedHostResourcesHashCodes = HashSet<Int>()
|
||||
|
||||
/**
|
||||
* 当前 Hook APP (宿主) 的全局生命周期 [Application]
|
||||
*
|
||||
* 需要 [YukiHookAPI.Configs.isEnableDataChannel] 或 [AppLifecycleCallback.isCallbackSetUp] 才会生效
|
||||
*/
|
||||
internal var hostApplication: Application? = null
|
||||
|
||||
/** 当前 Xposed 模块自身 APK 路径 */
|
||||
internal var moduleAppFilePath = ""
|
||||
|
||||
/** 当前 Xposed 模块自身 [Resources] */
|
||||
internal var moduleAppResources: YukiModuleResources? = null
|
||||
|
||||
/**
|
||||
* 当前环境中使用的 [ClassLoader]
|
||||
*
|
||||
* 装载位于 (Xposed) 宿主环境与模块环境时均使用当前 DEX 内的 [ClassLoader]
|
||||
* @return [ClassLoader]
|
||||
* @throws IllegalStateException 如果 [ClassLoader] 为空
|
||||
*/
|
||||
internal val baseClassLoader get() = classOf<YukiHookAPI>().classLoader ?: error("Operating system not supported")
|
||||
|
||||
/**
|
||||
* 获取当前 Xposed 模块自身动态 [Resources]
|
||||
* @return [YukiModuleResources] or null
|
||||
*/
|
||||
internal val dynamicModuleAppResources get() = runCatching { YukiModuleResources.wrapper(moduleAppFilePath) }.getOrNull()
|
||||
|
||||
/**
|
||||
* 获取当前系统框架的 [Context]
|
||||
* @return [Context] ContextImpl 实例对象
|
||||
* @throws IllegalStateException 如果获取不到系统框架的 [Context]
|
||||
*/
|
||||
internal val systemContext
|
||||
get() = ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()?.let {
|
||||
ActivityThreadClass.method { name = "getSystemContext" }.ignored().get(it).invoke<Context?>()
|
||||
} ?: error("Failed to got SystemContext")
|
||||
|
||||
/**
|
||||
* 获取指定 [packageName] 的用户 ID
|
||||
*
|
||||
* 机主为 0 - 应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同
|
||||
* @param packageName 当前包名
|
||||
* @return [Int]
|
||||
*/
|
||||
internal fun findUserId(packageName: String) =
|
||||
UserHandleClass.method {
|
||||
name = "getUserId"
|
||||
param(IntType)
|
||||
}.ignored().get().int(systemContext.packageManager.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES).uid)
|
||||
|
||||
/**
|
||||
* 注入当前 Hook APP (宿主) 全局生命周期
|
||||
* @param packageName 包名
|
||||
*/
|
||||
internal fun registerToAppLifecycle(packageName: String) {
|
||||
/** Hook [Application] 装载方法 */
|
||||
runCatching {
|
||||
if (AppLifecycleCallback.isCallbackSetUp) {
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "attach"; param(ContextClass) }, object : YukiMemberHook() {
|
||||
override fun beforeHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, false) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.args?.get(0) as? Context?)?.also { AppLifecycleCallback.attachBaseContextCallback?.invoke(it, true) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onTerminate" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onTerminateCallback?.invoke(it) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onLowMemory" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.instance as? Application?)?.also { AppLifecycleCallback.onLowMemoryCallback?.invoke(it) }
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onTrimMemory"; param(IntType) }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
val self = wrapper.instance as? Application? ?: return
|
||||
val type = wrapper.args?.get(0) as? Int? ?: return
|
||||
AppLifecycleCallback.onTrimMemoryCallback?.invoke(self, type)
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
YukiHookHelper.hook(ApplicationClass.method { name = "onConfigurationChanged" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
val self = wrapper.instance as? Application? ?: return
|
||||
val config = wrapper.args?.get(0) as? Configuration? ?: return
|
||||
AppLifecycleCallback.onConfigurationChangedCallback?.invoke(self, config)
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
}
|
||||
if (YukiHookAPI.Configs.isEnableDataChannel || AppLifecycleCallback.isCallbackSetUp)
|
||||
YukiHookHelper.hook(InstrumentationClass.method { name = "callApplicationOnCreate" }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
runCatching {
|
||||
(wrapper.args?.get(0) as? Application?)?.also {
|
||||
hostApplication = it
|
||||
AppLifecycleCallback.onCreateCallback?.invoke(it)
|
||||
AppLifecycleCallback.onReceiversCallback.takeIf { e -> e.isNotEmpty() }?.forEach { (_, e) ->
|
||||
if (e.first.isNotEmpty()) it.registerReceiver(object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (context == null || intent == null) return
|
||||
if (e.first.any { e -> e == intent.action }) e.second(context, intent)
|
||||
}
|
||||
}, IntentFilter().apply { e.first.forEach { e -> addAction(e) } })
|
||||
}
|
||||
if (isDataChannelRegister) return
|
||||
isDataChannelRegister = true
|
||||
runCatching { YukiHookDataChannel.instance().register(it, packageName) }
|
||||
}
|
||||
}.onFailure { wrapper.throwable = it }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听并 Hook 当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法
|
||||
* @param loader 当前 [ClassLoader]
|
||||
* @param result 回调 - ([Class] 实例对象,[Boolean] 是否 resolve)
|
||||
*/
|
||||
internal fun hookClassLoader(loader: ClassLoader?, result: (clazz: Class<*>, resolve: Boolean) -> Unit) {
|
||||
runCatching {
|
||||
YukiHookHelper.hook(JavaClassLoader.method { name = "loadClass"; param(StringType, BooleanType) }, object : YukiMemberHook() {
|
||||
override fun afterHookedMember(wrapper: HookParamWrapper) {
|
||||
if (wrapper.instance?.javaClass?.name == loader?.javaClass?.name)
|
||||
(wrapper.result as? Class<*>?)?.also { result(it, wrapper.args?.get(1) as? Boolean ?: false) }
|
||||
}
|
||||
})
|
||||
}.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") }
|
||||
}
|
||||
|
||||
/**
|
||||
* 向 Hook APP (宿主) 注入当前 Xposed 模块的资源
|
||||
* @param hostResources 需要注入的宿主 [Resources]
|
||||
*/
|
||||
internal fun injectModuleAppResources(hostResources: Resources) {
|
||||
if (injectedHostResourcesHashCodes.contains(hostResources.hashCode())) return
|
||||
if (YukiHookBridge.hasXposedBridge) runCatching {
|
||||
hostResources.assets.current {
|
||||
method {
|
||||
name = "addAssetPath"
|
||||
param(StringType)
|
||||
}.call(moduleAppFilePath)
|
||||
}
|
||||
injectedHostResourcesHashCodes.add(hostResources.hashCode())
|
||||
}.onFailure {
|
||||
yLoggerE(msg = "Failed to inject module resources into [$hostResources]", e = it)
|
||||
} else yLoggerW(msg = "You can only inject module resources in Xposed Environment")
|
||||
}
|
||||
|
||||
/** 刷新当前 Xposed 模块自身 [Resources] */
|
||||
internal fun refreshModuleAppResources() {
|
||||
dynamicModuleAppResources?.let { moduleAppResources = it }
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前 Hook APP (宿主) 的生命周期回调处理类
|
||||
*/
|
||||
internal object AppLifecycleCallback {
|
||||
|
||||
/** 是否已设置回调 */
|
||||
internal var isCallbackSetUp = false
|
||||
|
||||
/** [Application.attachBaseContext] 回调 */
|
||||
internal var attachBaseContextCallback: ((Context, Boolean) -> Unit)? = null
|
||||
|
||||
/** [Application.onCreate] 回调 */
|
||||
internal var onCreateCallback: (Application.() -> Unit)? = null
|
||||
|
||||
/** [Application.onTerminate] 回调 */
|
||||
internal var onTerminateCallback: (Application.() -> Unit)? = null
|
||||
|
||||
/** [Application.onLowMemory] 回调 */
|
||||
internal var onLowMemoryCallback: (Application.() -> Unit)? = null
|
||||
|
||||
/** [Application.onTrimMemory] 回调 */
|
||||
internal var onTrimMemoryCallback: ((Application, Int) -> Unit)? = null
|
||||
|
||||
/** [Application.onConfigurationChanged] 回调 */
|
||||
internal var onConfigurationChangedCallback: ((Application, Configuration) -> Unit)? = null
|
||||
|
||||
/** 系统广播监听回调 */
|
||||
internal val onReceiversCallback = HashMap<String, Pair<Array<out String>, (Context, Intent) -> Unit>>()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user