From 4ecc67507a9623f76e3288344870caac4bce6bb8 Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Tue, 17 Jun 2025 16:32:01 +0800 Subject: [PATCH] refactor: take over KavaRef log to YLog --- .../src/en/api/special-features/logger.md | 9 ++-- .../src/zh-cn/api/special-features/logger.md | 6 ++- .../highcapable/yukihookapi/hook/log/YLog.kt | 46 +++++++++++++++++ .../bridge/status/YukiXposedModuleStatus.kt | 8 +-- .../hook/xposed/parasitic/AppParasitics.kt | 50 ++++++++++--------- .../delegate/caller/HandlerDelegateCaller.kt | 19 ++++--- 6 files changed, 95 insertions(+), 43 deletions(-) diff --git a/docs-source/src/en/api/special-features/logger.md b/docs-source/src/en/api/special-features/logger.md index 429da1a9..e3fe4c27 100644 --- a/docs-source/src/en/api/special-features/logger.md +++ b/docs-source/src/en/api/special-features/logger.md @@ -2,11 +2,12 @@ > Log is the most important part of the debugging process, `YukiHookAPI` encapsulates a set of stable and efficient debugging log functions for developers. -::: warning +::: tip -The log of `KavaRef` will be managed separately by itself. -For detailed configuration plans, you can refer to [here](https://highcapable.github.io/KavaRef/en/library/kavaref-core#exception-handling), -which will jump to the `KavaRef` document. +The logs of `KavaRef` can be managed separately by itself. For detailed configuration plans, +you can refer to [here](https://highcapable.github.io/KavaRef/en/library/kavaref-core#log-management), which will jump to the `KavaRef` document. + +`YukiHookAPI` has taken over the logging function of `KavaRef` by default, and you can also configure the logging function of `KavaRef` yourself. ::: diff --git a/docs-source/src/zh-cn/api/special-features/logger.md b/docs-source/src/zh-cn/api/special-features/logger.md index 59f261d8..dd743d3f 100644 --- a/docs-source/src/zh-cn/api/special-features/logger.md +++ b/docs-source/src/zh-cn/api/special-features/logger.md @@ -2,9 +2,11 @@ > 日志是调试过程最重要的一环,`YukiHookAPI` 为开发者封装了一套稳定高效的调试日志功能。 -::: warning +::: tip -`KavaRef` 的日志将由其自身单独管理,详细的配置方案你可以参考 [这里](https://highcapable.github.io/KavaRef/zh-cn/library/kavaref-core#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86),这将跳转到 `KavaRef` 的文档。 +`KavaRef` 的日志可以由其自身单独管理,详细的配置方案你可以参考 [这里](https://highcapable.github.io/KavaRef/zh-cn/library/kavaref-core#%E6%97%A5%E5%BF%97%E7%AE%A1%E7%90%86),这将跳转到 `KavaRef` 的文档。 + +`YukiHookAPI` 默认接管了 `KavaRef` 的日志功能,你也可以自己配置 `KavaRef` 的日志功能。 ::: diff --git a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/log/YLog.kt b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/log/YLog.kt index 6b0a1b0b..5d3b51c7 100644 --- a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/log/YLog.kt +++ b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/log/YLog.kt @@ -25,6 +25,8 @@ package com.highcapable.yukihookapi.hook.log import android.system.ErrnoException import android.util.Log +import com.highcapable.kavaref.KavaRef +import com.highcapable.kavaref.runtime.KavaRefRuntime import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper import com.highcapable.yukihookapi.hook.log.data.YLogData @@ -311,6 +313,8 @@ object YLog { * @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID */ private fun log(env: EnvType, data: YLogData, isImplicit: Boolean = false) { + initKavaRefLoggerIfNot() + /** 是否为有效日志 */ val isNotBlankLog = data.msg.isNotBlank() || (data.msg.isBlank() && data.throwable != null) @@ -339,6 +343,48 @@ object YLog { if (isImplicit.not() && Configs.isRecord && isNotBlankLog) inMemoryData.add(data) } + /** 定义 [KavaRef] 日志记录器 */ + private val kavaRefLogger = object : KavaRefRuntime.Logger { + + override val tag get() = Configs.tag + + override fun debug(msg: Any?, throwable: Throwable?) { + this@YLog.debug(msg.toString(), throwable) + } + + override fun error(msg: Any?, throwable: Throwable?) { + this@YLog.error(msg.toString(), throwable) + } + + override fun info(msg: Any?, throwable: Throwable?) { + this@YLog.info(msg.toString(), throwable) + } + + override fun warn(msg: Any?, throwable: Throwable?) { + this@YLog.warn(msg.toString(), throwable) + } + } + + /** 是否已初始化 [KavaRef] 日志记录器 */ + private var isKavaRefLoggerInit = false + + /** 初始化 [KavaRef] 日志记录器 - 仅在第一次调用时进行初始化 */ + private fun initKavaRefLoggerIfNot() { + updateKavaRefLogLevel() + if (isKavaRefLoggerInit) return + KavaRef.setLogger(kavaRefLogger) + isKavaRefLoggerInit = true + } + + /** 更新 [KavaRef] 日志记录器的日志级别 */ + private fun updateKavaRefLogLevel() { + KavaRef.logLevel = when { + !Configs.isEnable -> KavaRefRuntime.LogLevel.OFF + YukiHookAPI.Configs.isDebug -> KavaRefRuntime.LogLevel.DEBUG + else -> KavaRefRuntime.LogLevel.INFO + } + } + /** * 需要打印的日志环境类型 * diff --git a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus.kt b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus.kt index a886b866..6ca410d8 100644 --- a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus.kt +++ b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/bridge/status/YukiXposedModuleStatus.kt @@ -111,7 +111,9 @@ internal object YukiXposedModuleStatus { * @param name 方法名称 * @return [MethodResolver] or null */ - private fun classMethod(name: String) = className.toClassOrNull()?.resolve()?.optional()?.firstMethodOrNull { this.name = name }.apply { - if (this == null) YLog.innerW("Failed to initialize YukiXposedModuleStatus") - } + private fun classMethod(name: String) = className.toClassOrNull()?.resolve() + ?.optional(silent = true) + ?.firstMethodOrNull { this.name = name }.apply { + if (this == null) YLog.innerW("Failed to initialize YukiXposedModuleStatus") + } } \ No newline at end of file diff --git a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt index e197794a..d37b05e2 100644 --- a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt +++ b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/AppParasitics.kt @@ -121,7 +121,7 @@ internal object AppParasitics { internal val systemContext get(): Context? { val scope = ActivityThreadClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) val current = scope.firstMethodOrNull { name = "currentActivityThread" emptyParameters() @@ -140,7 +140,7 @@ internal object AppParasitics { get() = runCatching { AndroidAppHelper.currentApplication() }.getOrNull() ?: ActivityThreadClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "currentApplication" } ?.invoke() @@ -153,14 +153,14 @@ internal object AppParasitics { ?: let { val scope = ActivityThreadClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) val current = scope.firstMethodOrNull { name = "currentActivityThread" emptyParameters() }?.invoke() - val currentScope = current?.resolve()?.optional() + val currentScope = current?.resolve()?.optional(silent = true) val mBoundApplication = currentScope?.firstFieldOrNull { name = "mBoundApplication" }?.get() - val appScope = mBoundApplication?.resolve()?.optional() + val appScope = mBoundApplication?.resolve()?.optional(silent = true) appScope?.firstFieldOrNull { name = "appInfo" }?.get() } @@ -178,7 +178,7 @@ internal object AppParasitics { get() = runCatching { AndroidAppHelper.currentProcessName() }.getOrNull() ?: ActivityThreadClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "currentPackageName" } ?.invoke() ?.takeIf { it.isNotBlank() } @@ -196,7 +196,7 @@ internal object AppParasitics { ?: return 0 return UserHandle::class.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "getUserId" parameters(Int::class) @@ -213,10 +213,12 @@ internal object AppParasitics { if (YukiXposedModule.isXposedEnvironment.not()) return YLog.innerW("You can only use hook ClassLoader method in Xposed Environment") classLoaderCallbacks[loader.hashCode()] = result if (isClassLoaderHooked) return - val loadClass = ClassLoader::class.resolve().optional().firstMethodOrNull { - name = "loadClass" - parameters(String::class, Boolean::class) - } + val loadClass = ClassLoader::class.resolve() + .optional(silent = true) + .firstMethodOrNull { + name = "loadClass" + parameters(String::class, Boolean::class) + } runCatching { YukiHookHelper.hook(loadClass, object : YukiMemberHook() { override fun afterHookedMember(param: Param) { @@ -239,7 +241,7 @@ internal object AppParasitics { YukiHookHelper.hook( ContextImplClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "setFilePermissionsFromMode" }, object : YukiMemberHook() { override fun beforeHookedMember(param: Param) { @@ -247,7 +249,7 @@ internal object AppParasitics { } } ) - YukiXposedModuleStatus.className.toClassOrNull(loader)?.resolve()?.optional()?.apply { + YukiXposedModuleStatus.className.toClassOrNull(loader)?.resolve()?.optional(silent = true)?.apply { if (type != HookEntryType.RESOURCES) { YukiHookHelper.hook(firstMethodOrNull { name = YukiXposedModuleStatus.IS_ACTIVE_METHOD_NAME }, object : YukiMemberReplacement() { @@ -294,7 +296,7 @@ internal object AppParasitics { } /** Hook [Application] 装载方法 */ runCatching { - if (appLifecycleActors.isNotEmpty()) Application::class.resolve().apply { + if (appLifecycleActors.isNotEmpty()) Application::class.resolve().optional(silent = true).apply { YukiHookHelper.hook(firstMethod { name = "attach"; parameters(Context::class) }, object : YukiMemberHook() { override fun beforeHookedMember(param: Param) { runCatching { @@ -351,7 +353,7 @@ internal object AppParasitics { } if (YukiHookAPI.Configs.isEnableDataChannel || appLifecycleActors.isNotEmpty()) YukiHookHelper.hook( - Instrumentation::class.resolve().optional().firstMethodOrNull { name = "callApplicationOnCreate" }, + Instrumentation::class.resolve().optional(silent = true).firstMethodOrNull { name = "callApplicationOnCreate" }, object : YukiMemberHook() { override fun afterHookedMember(param: Param) { runCatching { @@ -414,7 +416,7 @@ internal object AppParasitics { return YLog.innerE("You cannot inject module resources into yourself") hostResources.assets.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "addAssetPath" parameters(String::class) @@ -450,7 +452,7 @@ internal object AppParasitics { queryIntentActivities(getLaunchIntentForPackage(context.packageName)!!, 0).first().activityInfo.name }?.getOrNull() ?: "" val checkIsActivity = proxyClassName.toClassOrNull(context.classLoader) - ?.resolve()?.optional() + ?.resolve()?.optional(silent = true) ?.firstMethodOrNull { name = "setIntent" parameters(Intent::class) @@ -464,27 +466,27 @@ internal object AppParasitics { /** Patched [Instrumentation] */ val sCurrentActivityThread = ActivityThreadClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstFieldOrNull { name = "sCurrentActivityThread" } ?.get() val instrumentation = sCurrentActivityThread?.resolve() ?.processor(AndroidHiddenApiBypassResolver.get()) - ?.optional() + ?.optional(silent = true) ?.firstMethodOrNull { name = "getInstrumentation" } ?.invoke() ?: error("Could not found Instrumentation in ActivityThread") sCurrentActivityThread.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstFieldOrNull { name = "mInstrumentation" } ?.set(InstrumentationDelegate.wrapper(instrumentation)) val mH = sCurrentActivityThread.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstFieldOrNull { name = "mH" } ?.get() ?: error("Could not found mH in ActivityThread") val mCallbackResolver = Handler::class.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstFieldOrNull { name = "mCallback" } ?.of(mH) val mCallback = mCallbackResolver?.get() @@ -506,7 +508,7 @@ internal object AppParasitics { }?.get() val mInstanceResolver = SingletonClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstFieldOrNull { name = "mInstance" } ?.of(gDefault) val mInstance = mInstanceResolver?.get() @@ -520,7 +522,7 @@ internal object AppParasitics { ?.get() SingletonClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "get" } ?.of(singleton) ?.invokeQuietly() diff --git a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/caller/HandlerDelegateCaller.kt b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/caller/HandlerDelegateCaller.kt index 76b03dad..d2f74d44 100644 --- a/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/caller/HandlerDelegateCaller.kt +++ b/yukihookapi-core/src/main/java/com/highcapable/yukihookapi/hook/xposed/parasitic/activity/delegate/caller/HandlerDelegateCaller.kt @@ -52,7 +52,7 @@ internal object HandlerDelegateCaller { private val ClientTransactionClass by lazyClass("android.app.servertransaction.ClientTransaction") private val mExtrasResolver by lazy { - Intent::class.resolve().optional().firstFieldOrNull { name = "mExtras" } + Intent::class.resolve().optional(silent = true).firstFieldOrNull { name = "mExtras" } } /** @@ -66,7 +66,7 @@ internal object HandlerDelegateCaller { LAUNCH_ACTIVITY -> { val intentResolver = msg.obj.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstFieldOrNull { name = "intent" } val intent = intentResolver?.get() val mExtras = mExtrasResolver?.copy()?.of(intent)?.getQuietly() @@ -78,14 +78,14 @@ internal object HandlerDelegateCaller { EXECUTE_TRANSACTION -> { val callbacks = ClientTransactionClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "getCallbacks" }?.of(msg.obj) ?.invokeQuietly>() ?.takeIf { it.isNotEmpty() } callbacks?.filter { it.javaClass.name.contains("LaunchActivityItem") }?.forEach { item -> - val itemResolver = item.resolve().optional() + val itemResolver = item.resolve().optional(silent = true) .firstFieldOrNull { name = "mIntent" } val intent = itemResolver?.get() val mExtras = mExtrasResolver?.copy()?.of(intent)?.getQuietly() @@ -96,31 +96,30 @@ internal object HandlerDelegateCaller { if (Build.VERSION.SDK_INT >= 31) { val currentActivityThread = ActivityThreadClass.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "currentActivityThread" } ?.invoke() val token = msg.obj.resolve() .processor(AndroidHiddenApiBypassResolver.get()) - .optional() + .optional(silent = true) .firstMethodOrNull { name = "getActivityToken" } ?.invokeQuietly() val launchingActivity = currentActivityThread?.resolve() ?.processor(AndroidHiddenApiBypassResolver.get()) - ?.optional() + ?.optional(silent = true) ?.firstMethodOrNull { name = "getLaunchingActivity" parameters(IBinder::class) }?.invokeQuietly(token) launchingActivity?.resolve() ?.processor(AndroidHiddenApiBypassResolver.get()) - ?.optional() + ?.optional(silent = true) ?.firstFieldOrNull { name = "intent" } ?.set(subIntent) }; itemResolver.set(subIntent) } } } - } - return baseInstance?.handleMessage(msg) ?: false + }; return baseInstance?.handleMessage(msg) ?: false } } \ No newline at end of file