refactor: take over KavaRef log to YLog

This commit is contained in:
2025-06-17 16:32:01 +08:00
parent beb7b431f8
commit 4ecc67507a
6 changed files with 95 additions and 43 deletions

View File

@@ -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. > 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. The logs of `KavaRef` can be managed separately by itself. For detailed configuration plans,
For detailed configuration plans, you can refer to [here](https://highcapable.github.io/KavaRef/en/library/kavaref-core#exception-handling), you can refer to [here](https://highcapable.github.io/KavaRef/en/library/kavaref-core#log-management), which will jump to the `KavaRef` document.
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.
::: :::

View File

@@ -2,9 +2,11 @@
> 日志是调试过程最重要的一环,`YukiHookAPI` 为开发者封装了一套稳定高效的调试日志功能。 > 日志是调试过程最重要的一环,`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` 的日志功能。
::: :::

View File

@@ -25,6 +25,8 @@ package com.highcapable.yukihookapi.hook.log
import android.system.ErrnoException import android.system.ErrnoException
import android.util.Log import android.util.Log
import com.highcapable.kavaref.KavaRef
import com.highcapable.kavaref.runtime.KavaRefRuntime
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper
import com.highcapable.yukihookapi.hook.log.data.YLogData import com.highcapable.yukihookapi.hook.log.data.YLogData
@@ -311,6 +313,8 @@ object YLog {
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID * @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
*/ */
private fun log(env: EnvType, data: YLogData, isImplicit: Boolean = false) { private fun log(env: EnvType, data: YLogData, isImplicit: Boolean = false) {
initKavaRefLoggerIfNot()
/** 是否为有效日志 */ /** 是否为有效日志 */
val isNotBlankLog = data.msg.isNotBlank() || (data.msg.isBlank() && data.throwable != null) 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) 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
}
}
/** /**
* 需要打印的日志环境类型 * 需要打印的日志环境类型
* *

View File

@@ -111,7 +111,9 @@ internal object YukiXposedModuleStatus {
* @param name 方法名称 * @param name 方法名称
* @return [MethodResolver] or null * @return [MethodResolver] or null
*/ */
private fun classMethod(name: String) = className.toClassOrNull()?.resolve()?.optional()?.firstMethodOrNull { this.name = name }.apply { private fun classMethod(name: String) = className.toClassOrNull()?.resolve()
if (this == null) YLog.innerW("Failed to initialize YukiXposedModuleStatus") ?.optional(silent = true)
} ?.firstMethodOrNull { this.name = name }.apply {
if (this == null) YLog.innerW("Failed to initialize YukiXposedModuleStatus")
}
} }

View File

@@ -121,7 +121,7 @@ internal object AppParasitics {
internal val systemContext get(): Context? { internal val systemContext get(): Context? {
val scope = ActivityThreadClass.resolve() val scope = ActivityThreadClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
val current = scope.firstMethodOrNull { val current = scope.firstMethodOrNull {
name = "currentActivityThread" name = "currentActivityThread"
emptyParameters() emptyParameters()
@@ -140,7 +140,7 @@ internal object AppParasitics {
get() = runCatching { AndroidAppHelper.currentApplication() }.getOrNull() get() = runCatching { AndroidAppHelper.currentApplication() }.getOrNull()
?: ActivityThreadClass.resolve() ?: ActivityThreadClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { name = "currentApplication" } .firstMethodOrNull { name = "currentApplication" }
?.invoke<Application>() ?.invoke<Application>()
@@ -153,14 +153,14 @@ internal object AppParasitics {
?: let { ?: let {
val scope = ActivityThreadClass.resolve() val scope = ActivityThreadClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
val current = scope.firstMethodOrNull { val current = scope.firstMethodOrNull {
name = "currentActivityThread" name = "currentActivityThread"
emptyParameters() emptyParameters()
}?.invoke() }?.invoke()
val currentScope = current?.resolve()?.optional() val currentScope = current?.resolve()?.optional(silent = true)
val mBoundApplication = currentScope?.firstFieldOrNull { name = "mBoundApplication" }?.get() val mBoundApplication = currentScope?.firstFieldOrNull { name = "mBoundApplication" }?.get()
val appScope = mBoundApplication?.resolve()?.optional() val appScope = mBoundApplication?.resolve()?.optional(silent = true)
appScope?.firstFieldOrNull { name = "appInfo" }?.get<ApplicationInfo>() appScope?.firstFieldOrNull { name = "appInfo" }?.get<ApplicationInfo>()
} }
@@ -178,7 +178,7 @@ internal object AppParasitics {
get() = runCatching { AndroidAppHelper.currentProcessName() }.getOrNull() get() = runCatching { AndroidAppHelper.currentProcessName() }.getOrNull()
?: ActivityThreadClass.resolve() ?: ActivityThreadClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { name = "currentPackageName" } .firstMethodOrNull { name = "currentPackageName" }
?.invoke<String>() ?.invoke<String>()
?.takeIf { it.isNotBlank() } ?.takeIf { it.isNotBlank() }
@@ -196,7 +196,7 @@ internal object AppParasitics {
?: return 0 ?: return 0
return UserHandle::class.resolve() return UserHandle::class.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { .firstMethodOrNull {
name = "getUserId" name = "getUserId"
parameters(Int::class) 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") if (YukiXposedModule.isXposedEnvironment.not()) return YLog.innerW("You can only use hook ClassLoader method in Xposed Environment")
classLoaderCallbacks[loader.hashCode()] = result classLoaderCallbacks[loader.hashCode()] = result
if (isClassLoaderHooked) return if (isClassLoaderHooked) return
val loadClass = ClassLoader::class.resolve().optional().firstMethodOrNull { val loadClass = ClassLoader::class.resolve()
name = "loadClass" .optional(silent = true)
parameters(String::class, Boolean::class) .firstMethodOrNull {
} name = "loadClass"
parameters(String::class, Boolean::class)
}
runCatching { runCatching {
YukiHookHelper.hook(loadClass, object : YukiMemberHook() { YukiHookHelper.hook(loadClass, object : YukiMemberHook() {
override fun afterHookedMember(param: Param) { override fun afterHookedMember(param: Param) {
@@ -239,7 +241,7 @@ internal object AppParasitics {
YukiHookHelper.hook( YukiHookHelper.hook(
ContextImplClass.resolve() ContextImplClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { name = "setFilePermissionsFromMode" }, .firstMethodOrNull { name = "setFilePermissionsFromMode" },
object : YukiMemberHook() { object : YukiMemberHook() {
override fun beforeHookedMember(param: Param) { 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) { if (type != HookEntryType.RESOURCES) {
YukiHookHelper.hook(firstMethodOrNull { name = YukiXposedModuleStatus.IS_ACTIVE_METHOD_NAME }, YukiHookHelper.hook(firstMethodOrNull { name = YukiXposedModuleStatus.IS_ACTIVE_METHOD_NAME },
object : YukiMemberReplacement() { object : YukiMemberReplacement() {
@@ -294,7 +296,7 @@ internal object AppParasitics {
} }
/** Hook [Application] 装载方法 */ /** Hook [Application] 装载方法 */
runCatching { 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() { YukiHookHelper.hook(firstMethod { name = "attach"; parameters(Context::class) }, object : YukiMemberHook() {
override fun beforeHookedMember(param: Param) { override fun beforeHookedMember(param: Param) {
runCatching { runCatching {
@@ -351,7 +353,7 @@ internal object AppParasitics {
} }
if (YukiHookAPI.Configs.isEnableDataChannel || appLifecycleActors.isNotEmpty()) if (YukiHookAPI.Configs.isEnableDataChannel || appLifecycleActors.isNotEmpty())
YukiHookHelper.hook( YukiHookHelper.hook(
Instrumentation::class.resolve().optional().firstMethodOrNull { name = "callApplicationOnCreate" }, Instrumentation::class.resolve().optional(silent = true).firstMethodOrNull { name = "callApplicationOnCreate" },
object : YukiMemberHook() { object : YukiMemberHook() {
override fun afterHookedMember(param: Param) { override fun afterHookedMember(param: Param) {
runCatching { runCatching {
@@ -414,7 +416,7 @@ internal object AppParasitics {
return YLog.innerE("You cannot inject module resources into yourself") return YLog.innerE("You cannot inject module resources into yourself")
hostResources.assets.resolve() hostResources.assets.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { .firstMethodOrNull {
name = "addAssetPath" name = "addAssetPath"
parameters(String::class) parameters(String::class)
@@ -450,7 +452,7 @@ internal object AppParasitics {
queryIntentActivities(getLaunchIntentForPackage(context.packageName)!!, 0).first().activityInfo.name queryIntentActivities(getLaunchIntentForPackage(context.packageName)!!, 0).first().activityInfo.name
}?.getOrNull() ?: "" }?.getOrNull() ?: ""
val checkIsActivity = proxyClassName.toClassOrNull(context.classLoader) val checkIsActivity = proxyClassName.toClassOrNull(context.classLoader)
?.resolve()?.optional() ?.resolve()?.optional(silent = true)
?.firstMethodOrNull { ?.firstMethodOrNull {
name = "setIntent" name = "setIntent"
parameters(Intent::class) parameters(Intent::class)
@@ -464,27 +466,27 @@ internal object AppParasitics {
/** Patched [Instrumentation] */ /** Patched [Instrumentation] */
val sCurrentActivityThread = ActivityThreadClass.resolve() val sCurrentActivityThread = ActivityThreadClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstFieldOrNull { name = "sCurrentActivityThread" } .firstFieldOrNull { name = "sCurrentActivityThread" }
?.get() ?.get()
val instrumentation = sCurrentActivityThread?.resolve() val instrumentation = sCurrentActivityThread?.resolve()
?.processor(AndroidHiddenApiBypassResolver.get()) ?.processor(AndroidHiddenApiBypassResolver.get())
?.optional() ?.optional(silent = true)
?.firstMethodOrNull { name = "getInstrumentation" } ?.firstMethodOrNull { name = "getInstrumentation" }
?.invoke<Instrumentation>() ?: error("Could not found Instrumentation in ActivityThread") ?.invoke<Instrumentation>() ?: error("Could not found Instrumentation in ActivityThread")
sCurrentActivityThread.resolve() sCurrentActivityThread.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstFieldOrNull { name = "mInstrumentation" } .firstFieldOrNull { name = "mInstrumentation" }
?.set(InstrumentationDelegate.wrapper(instrumentation)) ?.set(InstrumentationDelegate.wrapper(instrumentation))
val mH = sCurrentActivityThread.resolve() val mH = sCurrentActivityThread.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstFieldOrNull { name = "mH" } .firstFieldOrNull { name = "mH" }
?.get<Handler>() ?: error("Could not found mH in ActivityThread") ?.get<Handler>() ?: error("Could not found mH in ActivityThread")
val mCallbackResolver = Handler::class.resolve() val mCallbackResolver = Handler::class.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstFieldOrNull { name = "mCallback" } .firstFieldOrNull { name = "mCallback" }
?.of(mH) ?.of(mH)
val mCallback = mCallbackResolver?.get<Handler.Callback>() val mCallback = mCallbackResolver?.get<Handler.Callback>()
@@ -506,7 +508,7 @@ internal object AppParasitics {
}?.get() }?.get()
val mInstanceResolver = SingletonClass.resolve() val mInstanceResolver = SingletonClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstFieldOrNull { name = "mInstance" } .firstFieldOrNull { name = "mInstance" }
?.of(gDefault) ?.of(gDefault)
val mInstance = mInstanceResolver?.get() val mInstance = mInstanceResolver?.get()
@@ -520,7 +522,7 @@ internal object AppParasitics {
?.get() ?.get()
SingletonClass.resolve() SingletonClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { name = "get" } .firstMethodOrNull { name = "get" }
?.of(singleton) ?.of(singleton)
?.invokeQuietly() ?.invokeQuietly()

View File

@@ -52,7 +52,7 @@ internal object HandlerDelegateCaller {
private val ClientTransactionClass by lazyClass("android.app.servertransaction.ClientTransaction") private val ClientTransactionClass by lazyClass("android.app.servertransaction.ClientTransaction")
private val mExtrasResolver by lazy { 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 -> { LAUNCH_ACTIVITY -> {
val intentResolver = msg.obj.resolve() val intentResolver = msg.obj.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstFieldOrNull { name = "intent" } .firstFieldOrNull { name = "intent" }
val intent = intentResolver?.get<Intent>() val intent = intentResolver?.get<Intent>()
val mExtras = mExtrasResolver?.copy()?.of(intent)?.getQuietly<Bundle>() val mExtras = mExtrasResolver?.copy()?.of(intent)?.getQuietly<Bundle>()
@@ -78,14 +78,14 @@ internal object HandlerDelegateCaller {
EXECUTE_TRANSACTION -> { EXECUTE_TRANSACTION -> {
val callbacks = ClientTransactionClass.resolve() val callbacks = ClientTransactionClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { .firstMethodOrNull {
name = "getCallbacks" name = "getCallbacks"
}?.of(msg.obj) }?.of(msg.obj)
?.invokeQuietly<List<Any>>() ?.invokeQuietly<List<Any>>()
?.takeIf { it.isNotEmpty() } ?.takeIf { it.isNotEmpty() }
callbacks?.filter { it.javaClass.name.contains("LaunchActivityItem") }?.forEach { item -> callbacks?.filter { it.javaClass.name.contains("LaunchActivityItem") }?.forEach { item ->
val itemResolver = item.resolve().optional() val itemResolver = item.resolve().optional(silent = true)
.firstFieldOrNull { name = "mIntent" } .firstFieldOrNull { name = "mIntent" }
val intent = itemResolver?.get<Intent>() val intent = itemResolver?.get<Intent>()
val mExtras = mExtrasResolver?.copy()?.of(intent)?.getQuietly<Bundle>() val mExtras = mExtrasResolver?.copy()?.of(intent)?.getQuietly<Bundle>()
@@ -96,31 +96,30 @@ internal object HandlerDelegateCaller {
if (Build.VERSION.SDK_INT >= 31) { if (Build.VERSION.SDK_INT >= 31) {
val currentActivityThread = ActivityThreadClass.resolve() val currentActivityThread = ActivityThreadClass.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { name = "currentActivityThread" } .firstMethodOrNull { name = "currentActivityThread" }
?.invoke() ?.invoke()
val token = msg.obj.resolve() val token = msg.obj.resolve()
.processor(AndroidHiddenApiBypassResolver.get()) .processor(AndroidHiddenApiBypassResolver.get())
.optional() .optional(silent = true)
.firstMethodOrNull { name = "getActivityToken" } .firstMethodOrNull { name = "getActivityToken" }
?.invokeQuietly() ?.invokeQuietly()
val launchingActivity = currentActivityThread?.resolve() val launchingActivity = currentActivityThread?.resolve()
?.processor(AndroidHiddenApiBypassResolver.get()) ?.processor(AndroidHiddenApiBypassResolver.get())
?.optional() ?.optional(silent = true)
?.firstMethodOrNull { ?.firstMethodOrNull {
name = "getLaunchingActivity" name = "getLaunchingActivity"
parameters(IBinder::class) parameters(IBinder::class)
}?.invokeQuietly(token) }?.invokeQuietly(token)
launchingActivity?.resolve() launchingActivity?.resolve()
?.processor(AndroidHiddenApiBypassResolver.get()) ?.processor(AndroidHiddenApiBypassResolver.get())
?.optional() ?.optional(silent = true)
?.firstFieldOrNull { name = "intent" } ?.firstFieldOrNull { name = "intent" }
?.set(subIntent) ?.set(subIntent)
}; itemResolver.set(subIntent) }; itemResolver.set(subIntent)
} }
} }
} }
} }; return baseInstance?.handleMessage(msg) ?: false
return baseInstance?.handleMessage(msg) ?: false
} }
} }