refactor: split LoggerFactory to YLog and YLogData

This commit is contained in:
2023-09-28 00:50:45 +08:00
parent ee1fc85a4e
commit c651776da0
26 changed files with 829 additions and 576 deletions

View File

@@ -44,9 +44,7 @@ import com.highcapable.yukihookapi.hook.core.api.compat.type.ExecutorType
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.isTaiChiModuleActive
import com.highcapable.yukihookapi.hook.factory.processName
import com.highcapable.yukihookapi.hook.log.YukiHookLogger
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerI
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
@@ -248,23 +246,23 @@ object YukiHookAPI {
object Configs {
/**
* 配置 [YukiHookLogger.Configs] 相关参数
* 配置 [YLog.Configs] 相关参数
* @param initiate 方法体
*/
inline fun debugLog(initiate: YukiHookLogger.Configs.() -> Unit) = YukiHookLogger.Configs.apply(initiate).build()
inline fun debugLog(initiate: YLog.Configs.() -> Unit) = YLog.Configs.apply(initiate).build()
/**
* 这是一个调试日志的全局标识
*
* - 此方法已弃用 - 在之后的版本中将直接被删除
*
* - 请现在迁移到 [debugLog] 并使用 [YukiHookLogger.Configs.tag]
* - 请现在迁移到 [debugLog] 并使用 [YLog.Configs.tag]
*/
@Deprecated(message = "请使用新方式来实现此功能")
var debugTag
get() = YukiHookLogger.Configs.tag
get() = YLog.Configs.tag
set(value) {
YukiHookLogger.Configs.tag = value
YLog.Configs.tag = value
}
/**
@@ -272,7 +270,7 @@ object YukiHookAPI {
*
* 启用后将交由日志输出管理器打印详细 Hook 日志到控制台
*
* 当 [YukiHookLogger.Configs.isEnable] 关闭后 [isDebug] 也将同时关闭
* 当 [YLog.Configs.isEnable] 关闭后 [isDebug] 也将同时关闭
*/
var isDebug = true
@@ -281,13 +279,13 @@ object YukiHookAPI {
*
* - 此方法已弃用 - 在之后的版本中将直接被删除
*
* - 请现在迁移到 [debugLog] 并使用 [YukiHookLogger.Configs.isEnable]
* - 请现在迁移到 [debugLog] 并使用 [YLog.Configs.isEnable]
*/
@Deprecated(message = "请使用新方式来实现此功能")
var isAllowPrintingLogs
get() = YukiHookLogger.Configs.isEnable
get() = YLog.Configs.isEnable
set(value) {
YukiHookLogger.Configs.isEnable = value
YLog.Configs.isEnable = value
}
/**
@@ -411,7 +409,7 @@ object YukiHookAPI {
YukiXposedModule.packageParamCallback = {
if (hooker.isNotEmpty())
hooker.forEach { it.assignInstance(packageParam = this) }
else yLoggerE(msg = "Failed to passing \"encase\" method because your hooker param is empty", isImplicit = true)
else YLog.innerE("Failed to passing \"encase\" method because your hooker param is empty", isImplicit = true)
}
else printNotFoundHookApiError()
}
@@ -471,7 +469,7 @@ object YukiHookAPI {
if (hooker.isNotEmpty()) {
printSplashInfo()
hooker.forEach { it.assignInstance(packageParam = baseContext.createPackageParam()) }
} else yLoggerE(msg = "Failed to passing \"encase\" method because your hooker param is empty", isImplicit = true))
} else YLog.innerE("Failed to passing \"encase\" method because your hooker param is empty", isImplicit = true))
else printNotFoundHookApiError()
}
@@ -479,15 +477,12 @@ object YukiHookAPI {
internal fun printSplashInfo() {
if (Configs.isDebug.not() || isShowSplashLogOnceTime.not()) return
isShowSplashLogOnceTime = false
yLoggerI(
msg = "Welcome to YukiHookAPI $VERSION! Using ${Status.Executor.name} API ${Status.Executor.apiLevel}",
isImplicit = true
)
YLog.innerI("Welcome to YukiHookAPI $VERSION! Using ${Status.Executor.name} API ${Status.Executor.apiLevel}", isImplicit = true)
}
/** 输出找不到 Hook API 的错误日志 */
private fun printNotFoundHookApiError() =
yLoggerE(msg = "Could not found any available Hook APIs in current environment! Aborted", isImplicit = true)
YLog.innerE("Could not found any available Hook APIs in current environment! Aborted", isImplicit = true)
/**
* 通过 baseContext 创建 Hook 入口类

View File

@@ -53,9 +53,7 @@ import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.factory.notExtends
import com.highcapable.yukihookapi.hook.factory.notImplements
import com.highcapable.yukihookapi.hook.factory.toJavaPrimitiveType
import com.highcapable.yukihookapi.hook.log.yLoggerD
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.HookParam
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.type.java.AnyClass
@@ -161,7 +159,7 @@ class YukiMemberHookCreator internal constructor(private val packageParam: Packa
HookApiCategoryHelper.hasAvailableHookApi.not() -> Result()
/** 过滤 [HookEntryType.ZYGOTE] and [HookEntryType.PACKAGE] or [HookParam.isCallbackCalled] 已被执行 */
packageParam.wrapper?.type == HookEntryType.RESOURCES && HookParam.isCallbackCalled.not() -> Result()
preHookMembers.isEmpty() -> Result().also { yLoggerW(msg = "Hook Members is empty in [${hookClass.name}], hook aborted") }
preHookMembers.isEmpty() -> Result().also { YLog.innerW(msg = "Hook Members is empty in [${hookClass.name}], hook aborted") }
else -> Result().await {
when {
isDisableCreatorRunHook.not() && hookClass.instance != null -> runCatching {
@@ -170,12 +168,12 @@ class YukiMemberHookCreator internal constructor(private val packageParam: Packa
preHookMembers.forEach { (_, m) -> m.hook() }
}.onFailure {
if (onHookClassNotFoundFailureCallback == null)
yLoggerE(msg = "Hook initialization failed because got an Exception", e = it)
YLog.innerE(msg = "Hook initialization failed because got an Exception", e = it)
else onHookClassNotFoundFailureCallback?.invoke(it)
}
isDisableCreatorRunHook.not() && hookClass.instance == null ->
if (onHookClassNotFoundFailureCallback == null)
yLoggerE(msg = "HookClass [${hookClass.name}] not found", e = hookClass.throwable)
YLog.innerE(msg = "HookClass [${hookClass.name}] not found", e = hookClass.throwable)
else onHookClassNotFoundFailureCallback?.invoke(hookClass.throwable ?: Throwable("[${hookClass.name}] not found"))
}
}
@@ -576,7 +574,7 @@ class YukiMemberHookCreator internal constructor(private val packageParam: Packa
onNoSuchMemberFailureCallback?.invoke(it)
onHookingFailureCallback?.invoke(it)
onAllFailureCallback?.invoke(it)
if (isNotIgnoredNoSuchMemberFailure) yLoggerE(
if (isNotIgnoredNoSuchMemberFailure) YLog.innerE(
msg = (if (isHookMemberSetup)
"Hooked Member with a finding error by $hookClass [$tag]"
else "Hooked Member cannot be non-null by $hookClass [$tag]"),
@@ -676,7 +674,7 @@ class YukiMemberHookCreator internal constructor(private val packageParam: Packa
* @param msg 调试日志内容
*/
private fun hookDebugMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug) yLoggerD(msg = msg)
if (YukiHookAPI.Configs.isDebug) YLog.innerD(msg)
}
/**
@@ -685,7 +683,7 @@ class YukiMemberHookCreator internal constructor(private val packageParam: Packa
* @param member 异常 [Member] - 可空
*/
private fun hookErrorMsg(e: Throwable, member: Member? = null) =
yLoggerE(msg = "Try to hook [${hookClass.instance ?: hookClass.name}]${member?.let { "[$it]" } ?: ""} got an Exception [$tag]", e = e)
YLog.innerE(msg = "Try to hook [${hookClass.instance ?: hookClass.name}]${member?.let { "[$it]" } ?: ""} got an Exception [$tag]", e = e)
/**
* 判断是否没有设置 Hook 过程中的任何异常拦截

View File

@@ -34,9 +34,7 @@ import android.util.ArrayMap
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.bean.HookResources
import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper
import com.highcapable.yukihookapi.hook.log.yLoggerD
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources
import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType
@@ -66,7 +64,7 @@ class YukiResourcesHookCreator internal constructor(internal val packageParam: P
if (HookApiCategoryHelper.hasAvailableHookApi.not()) return
/** 过滤 [HookEntryType.ZYGOTE] 与 [HookEntryType.RESOURCES] */
if (packageParam.wrapper?.type == HookEntryType.PACKAGE) return
if (preHookResources.isEmpty()) return yLoggerW(msg = "Hook Resources is empty, hook aborted")
if (preHookResources.isEmpty()) return YLog.innerW("Hook Resources is empty, hook aborted")
preHookResources.forEach { (_, r) -> r.hook() }
}
@@ -174,25 +172,25 @@ class YukiResourcesHookCreator internal constructor(internal val packageParam: P
isHooked = true
if (isDisableCreatorRunHook.not()) runCatching {
when {
conditions == null -> yLoggerE(msg = "You must set the conditions before hook a Resources [$tag]")
replaceInstance == null && layoutInstance == null -> yLoggerE(msg = "Resources Hook got null replaceInstance [$tag]")
conditions == null -> YLog.innerE("You must set the conditions before hook a Resources [$tag]")
replaceInstance == null && layoutInstance == null -> YLog.innerE("Resources Hook got null replaceInstance [$tag]")
packageParam.wrapper?.type == HookEntryType.RESOURCES && hookResources.instance != null ->
if (resourceId == -1) when {
layoutInstance != null ->
hookResources.instance?.hookLayout(
packageParam.packageName, conditions!!.type,
conditions!!.name, layoutInstance!!
) { onHookLogMsg(msg = "Hook Resources Layout $conditions done [$tag]") }
) { hookDebugMsg(msg = "Hook Resources Layout $conditions done [$tag]") }
else -> hookResources.instance?.setReplacement(
packageParam.packageName, conditions!!.type,
conditions!!.name, compat(replaceInstance)
) { onHookLogMsg(msg = "Hook Resources Value $conditions done [$tag]") }
) { hookDebugMsg(msg = "Hook Resources Value $conditions done [$tag]") }
} else when {
layoutInstance != null -> hookResources.instance?.hookLayout(resourceId, layoutInstance!!) {
onHookLogMsg(msg = "Hook Resources Layout Id $resourceId done [$tag]")
hookDebugMsg(msg = "Hook Resources Layout Id $resourceId done [$tag]")
}
else -> hookResources.instance?.setReplacement(resourceId, compat(replaceInstance)) {
onHookLogMsg(msg = "Hook Resources Value Id $resourceId done [$tag]")
hookDebugMsg(msg = "Hook Resources Value Id $resourceId done [$tag]")
}
}
packageParam.wrapper?.type == HookEntryType.ZYGOTE ->
@@ -201,24 +199,24 @@ class YukiResourcesHookCreator internal constructor(internal val packageParam: P
YukiResources.hookSystemWideLayout(
packageParam.packageName, conditions!!.type,
conditions!!.name, layoutInstance!!
) { onHookLogMsg(msg = "Hook Wide Resources Layout $conditions done [$tag]") }
) { hookDebugMsg(msg = "Hook Wide Resources Layout $conditions done [$tag]") }
else -> YukiResources.setSystemWideReplacement(
packageParam.packageName, conditions!!.type,
conditions!!.name, compat(replaceInstance)
) { onHookLogMsg(msg = "Hook Wide Resources Value $conditions done [$tag]") }
) { hookDebugMsg(msg = "Hook Wide Resources Value $conditions done [$tag]") }
} else when {
layoutInstance != null -> YukiResources.hookSystemWideLayout(resourceId, layoutInstance!!) {
onHookLogMsg(msg = "Hook Wide Resources Layout Id $resourceId done [$tag]")
hookDebugMsg(msg = "Hook Wide Resources Layout Id $resourceId done [$tag]")
}
else -> YukiResources.setSystemWideReplacement(resourceId, compat(replaceInstance)) {
onHookLogMsg(msg = "Hook Wide Resources Value Id $resourceId done [$tag]")
hookDebugMsg(msg = "Hook Wide Resources Value Id $resourceId done [$tag]")
}
}
else -> yLoggerE(msg = "Resources Hook type is invalid [$tag]")
else -> YLog.innerE("Resources Hook type is invalid [$tag]")
}
}.onFailure {
if (onHookFailureCallback == null)
yLoggerE(msg = "Resources Hook got an Exception [$tag]", e = it)
YLog.innerE("Resources Hook got an Exception [$tag]", it)
else onHookFailureCallback?.invoke(it)
}
}
@@ -227,8 +225,8 @@ class YukiResourcesHookCreator internal constructor(internal val packageParam: P
* Hook 过程中开启了 [YukiHookAPI.Configs.isDebug] 输出调试信息
* @param msg 调试日志内容
*/
private fun onHookLogMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug) yLoggerD(msg = msg)
private fun hookDebugMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug) YLog.innerD(msg)
}
/**

View File

@@ -35,7 +35,7 @@ import com.highcapable.yukihookapi.hook.core.api.store.YukiHookCacheStore
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.core.finder.members.ConstructorFinder
import com.highcapable.yukihookapi.hook.core.finder.members.MethodFinder
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
import java.lang.reflect.Member
/**
@@ -56,7 +56,7 @@ internal object YukiHookHelper {
else -> error("Unexpected BaseFinder result interface type")
}
hookMember(member, callback)
}.onFailure { yLoggerE(msg = "An exception occurred when hooking internal function", e = it) }.getOrNull() ?: YukiHookResult()
}.onFailure { YLog.innerE("An exception occurred when hooking internal function", it) }.getOrNull() ?: YukiHookResult()
/**
* Hook [Member]
@@ -99,7 +99,7 @@ internal object YukiHookHelper {
if (isMemberHooked(member)) member?.let {
runCatching { HookCompatHelper.invokeOriginalMember(member, instance, args) }.onFailure {
if (it.message?.lowercase()?.contains("wrong number of arguments") == true) error(it.message ?: it.toString())
yLoggerE(msg = "Invoke original Member [$member] failed", e = it)
YLog.innerE("Invoke original Member [$member] failed", it)
}.getOrNull()
} else null

View File

@@ -29,8 +29,7 @@ package com.highcapable.yukihookapi.hook.core.finder.base
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper
import com.highcapable.yukihookapi.hook.log.yLoggerD
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
/**
* 这是 [Class] 查找类功能的基本类实现
@@ -63,7 +62,7 @@ abstract class ClassBaseFinder internal constructor(internal open val loaderSet:
* @param msg 消息内容
*/
internal fun debugMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug && HookApiCategoryHelper.hasAvailableHookApi) yLoggerD(msg = msg)
if (YukiHookAPI.Configs.isDebug && HookApiCategoryHelper.hasAvailableHookApi) YLog.innerD(msg)
}
/**
@@ -74,7 +73,7 @@ abstract class ClassBaseFinder internal constructor(internal open val loaderSet:
if (isIgnoreErrorLogs) return
/** 判断是否为 [LOADERSET_IS_NULL] */
if (e?.message == LOADERSET_IS_NULL) return
yLoggerE(msg = "NoClassDefFound happend in [$loaderSet]", e = e)
YLog.innerE("NoClassDefFound happend in [$loaderSet]", e)
}
override fun failure(throwable: Throwable?) = error("DexClassFinder does not contain this usage")

View File

@@ -30,8 +30,7 @@ package com.highcapable.yukihookapi.hook.core.finder.base
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator
import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper
import com.highcapable.yukihookapi.hook.log.yLoggerD
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.utils.factory.await
import java.lang.reflect.Constructor
import java.lang.reflect.Field
@@ -96,7 +95,7 @@ abstract class MemberBaseFinder internal constructor(private val tag: String, in
*/
internal fun debugMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug && HookApiCategoryHelper.hasAvailableHookApi && hookerManager.instance != null)
yLoggerD(msg = "$msg${hookerManager.tailTag}")
YLog.innerD("$msg${hookerManager.tailTag}")
}
/**
@@ -112,8 +111,8 @@ abstract class MemberBaseFinder internal constructor(private val tag: String, in
await {
if (isIgnoreErrorLogs || hookerManager.isNotIgnoredNoSuchMemberFailure.not()) return@await
if (isAlwaysMode.not() && isUsingRemedyPlan) return@await
yLoggerE(msg = "NoSuch$tag happend in [$classSet] $msg${hookerManager.tailTag}".trim(), e = e)
es.forEachIndexed { index, e -> yLoggerE(msg = "Throwable [${index + 1}]", e = e) }
YLog.innerE("NoSuch$tag happend in [$classSet] $msg${hookerManager.tailTag}".trim(), e)
es.forEachIndexed { index, e -> YLog.innerE("Throwable [${index + 1}]", e) }
}
}

View File

@@ -48,7 +48,7 @@ import com.highcapable.yukihookapi.hook.core.finder.type.factory.NameConditions
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.searchClass
import com.highcapable.yukihookapi.hook.factory.toClass
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.utils.factory.await
import com.highcapable.yukihookapi.hook.utils.factory.runBlocking
@@ -109,7 +109,7 @@ class DexClassFinder internal constructor(
*/
fun clearCache(context: Context? = currentContext, versionName: String? = null, versionCode: Long? = null) {
context?.currentSp(versionName, versionCode)?.edit()?.clear()?.apply()
?: yLoggerW(msg = "Cannot clear cache for DexClassFinder because got null context instance")
?: YLog.innerW("Cannot clear cache for DexClassFinder because got null context instance")
}
}

View File

@@ -40,7 +40,7 @@ import com.highcapable.yukihookapi.hook.core.finder.type.factory.CountConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ModifierConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ObjectsConditions
import com.highcapable.yukihookapi.hook.factory.hasExtends
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
import com.highcapable.yukihookapi.hook.type.defined.VagueType
import com.highcapable.yukihookapi.hook.utils.factory.runBlocking
@@ -333,7 +333,7 @@ class ConstructorFinder internal constructor(override val classSet: Class<*>? =
if (isFindSuccess) return
errorMsg(msg = "RemedyPlan failed after ${remedyPlans.size} attempts", es = errors, isAlwaysMode = true)
remedyPlans.clear()
} else yLoggerW(msg = "RemedyPlan is empty, forgot it?${hookerManager.tailTag}")
} else YLog.innerW("RemedyPlan is empty, forgot it?${hookerManager.tailTag}")
}
/**

View File

@@ -42,7 +42,7 @@ import com.highcapable.yukihookapi.hook.core.finder.type.factory.NameConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ObjectConditions
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.hasExtends
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.utils.factory.runBlocking
import java.lang.reflect.Field
@@ -282,7 +282,7 @@ class FieldFinder internal constructor(override val classSet: Class<*>? = null)
if (isFindSuccess) return
errorMsg(msg = "RemedyPlan failed after ${remedyPlans.size} attempts", es = errors, isAlwaysMode = true)
remedyPlans.clear()
} else yLoggerW(msg = "RemedyPlan is empty, forgot it?${hookerManager.tailTag}")
} else YLog.innerW("RemedyPlan is empty, forgot it?${hookerManager.tailTag}")
}
/**

View File

@@ -43,7 +43,7 @@ import com.highcapable.yukihookapi.hook.core.finder.type.factory.NameConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ObjectConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ObjectsConditions
import com.highcapable.yukihookapi.hook.factory.hasExtends
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
import com.highcapable.yukihookapi.hook.type.defined.VagueType
import com.highcapable.yukihookapi.hook.utils.factory.runBlocking
@@ -427,7 +427,7 @@ class MethodFinder internal constructor(override val classSet: Class<*>? = null)
if (isFindSuccess) return
errorMsg(msg = "RemedyPlan failed after ${remedyPlans.size} attempts", es = errors, isAlwaysMode = true)
remedyPlans.clear()
} else yLoggerW(msg = "RemedyPlan is empty, forgot it?${hookerManager.tailTag}")
} else YLog.innerW("RemedyPlan is empty, forgot it?${hookerManager.tailTag}")
}
/**

View File

@@ -37,22 +37,32 @@ import com.highcapable.yukihookapi.hook.core.finder.members.data.ConstructorRule
import com.highcapable.yukihookapi.hook.core.finder.members.data.FieldRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MemberRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MethodRulesData
import com.highcapable.yukihookapi.hook.factory.*
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.hasExtends
import com.highcapable.yukihookapi.hook.factory.toClass
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
import com.highcapable.yukihookapi.hook.type.defined.VagueType
import com.highcapable.yukihookapi.hook.type.java.DalvikBaseDexClassLoader
import com.highcapable.yukihookapi.hook.type.java.NoClassDefFoundErrorClass
import com.highcapable.yukihookapi.hook.type.java.NoSuchFieldErrorClass
import com.highcapable.yukihookapi.hook.type.java.NoSuchMethodErrorClass
import com.highcapable.yukihookapi.hook.utils.factory.*
import com.highcapable.yukihookapi.hook.utils.factory.conditions
import com.highcapable.yukihookapi.hook.utils.factory.findLastIndex
import com.highcapable.yukihookapi.hook.utils.factory.lastIndex
import com.highcapable.yukihookapi.hook.utils.factory.let
import com.highcapable.yukihookapi.hook.utils.factory.runOrFalse
import com.highcapable.yukihookapi.hook.utils.factory.takeIf
import com.highcapable.yukihookapi.hook.utils.factory.value
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import dalvik.system.BaseDexClassLoader
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Member
import java.lang.reflect.Method
import java.util.*
import java.util.Enumeration
import kotlin.math.abs
/**
@@ -192,7 +202,7 @@ internal object ReflectionTool {
fun MemberRulesData.exists(vararg type: Any?): Boolean {
if (type.isEmpty()) return true
for (i in type.indices) if (type[i] == UndefinedType) {
yLoggerW(msg = "$objectName type[$i] mistake, it will be ignored in current conditions")
YLog.innerW("$objectName type[$i] mistake, it will be ignored in current conditions")
return false
}
return true
@@ -656,7 +666,7 @@ internal object ReflectionTool {
addAll(declaredConstructors.toList())
}.asSequence()
}.onFailure {
yLoggerW(msg = "Failed to get the declared Members in [$this] because got an exception\n$it")
YLog.innerW("Failed to get the declared Members in [$this] because got an exception", it)
}.getOrNull()
/**
@@ -665,7 +675,7 @@ internal object ReflectionTool {
*/
private val Class<*>.existFields
get() = runCatching { declaredFields.asSequence() }.onFailure {
yLoggerW(msg = "Failed to get the declared Fields in [$this] because got an exception\n$it")
YLog.innerW("Failed to get the declared Fields in [$this] because got an exception", it)
}.getOrNull()
/**
@@ -674,7 +684,7 @@ internal object ReflectionTool {
*/
private val Class<*>.existMethods
get() = runCatching { declaredMethods.asSequence() }.onFailure {
yLoggerW(msg = "Failed to get the declared Methods in [$this] because got an exception\n$it")
YLog.innerW("Failed to get the declared Methods in [$this] because got an exception", it)
}.getOrNull()
/**
@@ -683,7 +693,7 @@ internal object ReflectionTool {
*/
private val Class<*>.existConstructors
get() = runCatching { declaredConstructors.asSequence() }.onFailure {
yLoggerW(msg = "Failed to get the declared Constructors in [$this] because got an exception\n$it")
YLog.innerW("Failed to get the declared Constructors in [$this] because got an exception", it)
}.getOrNull()
/**

View File

@@ -1,447 +0,0 @@
/*
* YukiHookAPI - An efficient Hook API and Xposed Module solution built in Kotlin.
* Copyright (C) 2019-2023 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/2/3.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.hook.log
import android.system.ErrnoException
import android.util.Log
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.utils.factory.dumpToString
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import java.io.File
import java.io.Serializable
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/**
* 需要打印的日志类型
*
* 决定于模块与 (Xposed) 宿主环境使用的打印方式
*/
enum class LoggerType {
/** 仅使用 [Log] */
LOGD,
/**
* 仅在 (Xposed) 宿主环境使用
*
* - 此方法已弃用 - 在之后的版本中将直接被删除
*
* - 请现在迁移到 [XPOSED_ENVIRONMENT]
*/
@Deprecated(message = "请使用新的命名方法", ReplaceWith("XPOSED_ENVIRONMENT"))
XPOSEDBRIDGE,
/**
* 仅在 (Xposed) 宿主环境使用
*
* - 只能在 (Xposed) 宿主环境中使用 - 模块环境将不生效
*/
XPOSED_ENVIRONMENT,
/**
* 分区使用
*
* (Xposed) 宿主环境仅使用 [XPOSED_ENVIRONMENT]
*
* 模块环境仅使用 [LOGD]
*/
SCOPE,
/**
* 同时使用
*
* (Xposed) 宿主环境使用 [LOGD] 与 [XPOSED_ENVIRONMENT]
*
* 模块环境仅使用 [LOGD]
*/
BOTH
}
/**
* 调试日志数据实现类
* @param timestamp 当前时间戳
* @param time 当前 UTC 时间
* @param tag 当前标签
* @param priority 当前优先级 - D、I、W、E
* @param packageName 当前包名
* @param userId 当前用户 ID
* @param msg 当前日志内容
* @param throwable 当前异常堆栈
*/
data class YukiLoggerData internal constructor(
var timestamp: Long = 0L,
var time: String = "",
var tag: String = YukiHookLogger.Configs.tag,
var priority: String = "",
var packageName: String = "",
var userId: Int = 0,
var msg: String = "",
var throwable: Throwable? = null
) : Serializable {
/** 是否隐式打印 */
internal var isImplicit = false
init {
timestamp = System.currentTimeMillis()
time = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ROOT).format(Date(timestamp))
packageName = if (YukiXposedModule.isXposedEnvironment) YukiXposedModule.hostProcessName else AppParasitics.currentPackageName
userId = AppParasitics.findUserId(AppParasitics.currentPackageName)
}
/**
* 获取头部时间字符串
* @return [String]
*/
internal val head get() = "$time ------ "
override fun toString(): String {
var content = ""
YukiHookLogger.Configs.elements.takeIf { it.isNotEmpty() }?.forEach {
if (it == YukiHookLogger.Configs.TAG) content += "[$tag]"
if (it == YukiHookLogger.Configs.PRIORITY) content += "[$priority]"
if (it == YukiHookLogger.Configs.PACKAGE_NAME && isImplicit.not() && packageName.isNotBlank()) content += "[$packageName]"
if (it == YukiHookLogger.Configs.USER_ID && isImplicit.not() && userId != 0) content += "[$userId]"
}
return content.takeIf { it.isNotBlank() }?.let { "$content--> $msg" } ?: msg
}
}
/**
* 调试日志实现类
*/
object YukiHookLogger {
/**
* 当前全部已记录的日志数据
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
*/
val inMemoryData = ArrayList<YukiLoggerData>()
/**
* 获取当前日志文件内容
*
* 如果当前没有已记录的日志会返回空字符串
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
* @return [String]
*/
val contents get() = contents()
/**
* 获取、格式化当前日志文件内容
*
* 如果当前没有已记录的日志 ([data] 为空) 会返回空字符串
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
* @param data 日志数据 - 默认为 [inMemoryData]
* @return [String]
*/
fun contents(data: ArrayList<YukiLoggerData> = inMemoryData): String {
var content = ""
data.takeIf { it.isNotEmpty() }?.forEach {
content += "${it.head}$it\n"
it.throwable?.also { e ->
content += "${it.head}Dump stack trace for \"${e.current().name}\":\n"
content += e.dumpToString()
}
}
return content
}
/**
* 清除全部已记录的日志
*
* 你也可以直接获取 [inMemoryData] 来清除
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
*/
fun clear() = inMemoryData.clear()
/**
* 保存当前日志到文件
*
* 若当前未开启 [Configs.isRecord] 或记录为空则不会进行任何操作
*
* 日志文件会追加到 [fileName] 的文件结尾 - 若文件不存在会自动创建
*
* - 文件读写权限取决于当前宿主、模块已获取的权限
* @param fileName 完整文件名 - 例如 /data/data/.../files/xxx.log
* @param data 日志数据 - 默认为 [inMemoryData]
* @throws ErrnoException 如果目标路径不可写
*/
fun saveToFile(fileName: String, data: ArrayList<YukiLoggerData> = inMemoryData) {
if (data.isNotEmpty()) File(fileName).appendText(contents(data))
}
/**
* 配置 [YukiHookLogger]
*/
object Configs {
/**
* 标签
*
* 显示效果如下 ↓
*
* ```
* [YukiHookAPI][...][...]--> ...
* ```
*/
const val TAG = 1000
/**
* 优先级
*
* 显示效果如下 ↓
*
* ```
* [...][E][...]--> ...
* ```
*/
const val PRIORITY = 1001
/**
* 当前宿主的包名
*
* 显示效果如下 ↓
*
* ```
* [...][com.demo.test][...]--> ...
* ```
*/
const val PACKAGE_NAME = 1002
/**
* 当前宿主的用户 ID (主用户不显示)
*
* 显示效果如下 ↓
*
* ```
* [...][...][999]--> ...
* ```
*/
const val USER_ID = 1003
/** 当前已添加的元素顺序列表数组 */
internal var elements = arrayOf(TAG, PRIORITY, PACKAGE_NAME, USER_ID)
/**
* 是否启用调试日志的输出功能 - 默认启用
*
* - 关闭后将会停用 [YukiHookAPI] 对全部日志的输出
*
* 但是不影响当你手动调用下面这些方法输出日志
*
* [loggerD]、[loggerI]、[loggerW]、[loggerE]
*
* 当 [isEnable] 关闭后 [YukiHookAPI.Configs.isDebug] 也将同时关闭
*/
var isEnable = true
/**
* 是否启用调试日志的记录功能 - 默认不启用
*
* 开启后将会在内存中记录全部可用的日志和异常堆栈
*
* 需要同时启用 [isEnable] 才能有效
*
* - 过量的日志可能会导致宿主运行缓慢或造成频繁 GC
*
* 开启后你可以调用 [YukiHookLogger.saveToFile] 实时保存日志到文件或使用 [YukiHookLogger.contents] 获取实时日志文件
*/
var isRecord = false
/**
* 这是一个调试日志的全局标识
*
* 默认文案为 [YukiHookAPI.TAG]
*
* 你可以修改为你自己的文案
*/
var tag = YukiHookAPI.TAG
/**
* 自定义调试日志对外显示的元素
*
* 只对日志记录和 (Xposed) 宿主环境的日志生效
*
* 日志元素的排列将按照你在 [item] 中设置的顺序进行显示
*
* 你还可以留空 [item] 以不显示除日志内容外的全部元素
*
* 可用的元素有:[TAG]、[PRIORITY]、[PACKAGE_NAME]、[USER_ID]
*
* 默认排列方式如下 ↓
*
* ```
* [TAG][PRIORITY][PACKAGE_NAME][USER_ID]--> Message
* ```
* @param item 自定义的元素数组
*/
fun elements(vararg item: Int) {
elements = arrayOf(*item.toTypedArray())
}
/** 结束方法体 */
internal fun build() = Unit
}
}
/**
* 向控制台和 (Xposed) 宿主环境打印日志 - 最终实现方法
* @param type 日志打印的类型
* @param data 日志数据
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
*/
private fun baseLogger(type: LoggerType, data: YukiLoggerData, isImplicit: Boolean = false) {
/** 是否为有效日志 */
val isNotBlankLog = data.msg.isNotBlank() || (data.msg.isBlank() && data.throwable != null)
/** 打印到 [Log] */
fun logByLogd() = when (data.priority) {
"D" -> Log.d(data.tag, data.msg)
"I" -> Log.i(data.tag, data.msg)
"W" -> Log.w(data.tag, data.msg)
"E" -> Log.e(data.tag, data.msg, data.throwable)
else -> Log.wtf(data.tag, data.msg, data.throwable)
}
/** 打印到 (Xposed) 宿主环境 */
fun logByHooker() {
if (isNotBlankLog) YukiHookHelper.logByHooker(data.also { it.isImplicit = isImplicit }.toString(), data.throwable)
}
@Suppress("DEPRECATION")
when (type) {
LoggerType.LOGD -> logByLogd()
LoggerType.XPOSEDBRIDGE, LoggerType.XPOSED_ENVIRONMENT -> logByHooker()
LoggerType.SCOPE -> if (YukiXposedModule.isXposedEnvironment) logByHooker() else logByLogd()
LoggerType.BOTH -> {
logByLogd()
if (YukiXposedModule.isXposedEnvironment) logByHooker()
}
}
if (isImplicit.not() && YukiHookLogger.Configs.isRecord && isNotBlankLog) YukiHookLogger.inMemoryData.add(data)
}
/**
* [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - D
* @param msg 日志打印的内容
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerD(msg: String, isImplicit: Boolean = false, isDisableLog: Boolean = false) {
if (YukiHookLogger.Configs.isEnable.not() || isDisableLog) return
baseLogger(LoggerType.BOTH, YukiLoggerData(priority = "D", msg = msg), isImplicit)
}
/**
* [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - I
* @param msg 日志打印的内容
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerI(msg: String, isImplicit: Boolean = false, isDisableLog: Boolean = false) {
if (YukiHookLogger.Configs.isEnable.not() || isDisableLog) return
baseLogger(LoggerType.BOTH, YukiLoggerData(priority = "I", msg = msg), isImplicit)
}
/**
* [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - W
* @param msg 日志打印的内容
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerW(msg: String, isImplicit: Boolean = false, isDisableLog: Boolean = false) {
if (YukiHookLogger.Configs.isEnable.not() || isDisableLog) return
baseLogger(LoggerType.BOTH, YukiLoggerData(priority = "W", msg = msg), isImplicit)
}
/**
* [YukiHookAPI] 向控制台和 (Xposed) 宿主环境打印日志 - E
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerE(msg: String = "", e: Throwable? = null, isImplicit: Boolean = false, isDisableLog: Boolean = false) {
if (YukiHookLogger.Configs.isEnable.not() || isDisableLog) return
baseLogger(LoggerType.BOTH, YukiLoggerData(priority = "E", msg = msg, throwable = e), isImplicit)
}
/**
* 向控制台和 (Xposed) 宿主环境打印日志 - D
*
* (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag]
* @param msg 日志打印的内容
* @param type 日志打印的类型 - 默认为 [LoggerType.BOTH]
*/
fun loggerD(tag: String = YukiHookLogger.Configs.tag, msg: String, type: LoggerType = LoggerType.BOTH) =
baseLogger(type, YukiLoggerData(priority = "D", tag = tag, msg = msg))
/**
* 向控制台和 (Xposed) 宿主环境打印日志 - I
*
* (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag]
* @param msg 日志打印的内容
* @param type 日志打印的类型 - 默认为 [LoggerType.BOTH]
*/
fun loggerI(tag: String = YukiHookLogger.Configs.tag, msg: String, type: LoggerType = LoggerType.BOTH) =
baseLogger(type, YukiLoggerData(priority = "I", tag = tag, msg = msg))
/**
* 向控制台和 (Xposed) 宿主环境打印日志 - W
*
* (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag]
* @param msg 日志打印的内容
* @param type 日志打印的类型 - 默认为 [LoggerType.BOTH]
*/
fun loggerW(tag: String = YukiHookLogger.Configs.tag, msg: String, type: LoggerType = LoggerType.BOTH) =
baseLogger(type, YukiLoggerData(priority = "W", tag = tag, msg = msg))
/**
* 向控制台和 (Xposed) 宿主环境打印日志 - E
*
* (Xposed) 宿主环境中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookLogger.Configs.tag]
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param type 日志打印的类型 - 默认为 [LoggerType.BOTH]
*/
fun loggerE(tag: String = YukiHookLogger.Configs.tag, msg: String = "", e: Throwable? = null, type: LoggerType = LoggerType.BOTH) =
baseLogger(type, YukiLoggerData(priority = "E", tag = tag, msg = msg, throwable = e))

View File

@@ -0,0 +1,386 @@
/*
* YukiHookAPI - An efficient Hook API and Xposed Module solution built in Kotlin.
* Copyright (C) 2019-2023 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 2023/9/27.
*/
package com.highcapable.yukihookapi.hook.log
import android.system.ErrnoException
import android.util.Log
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.log.data.YLogData
import com.highcapable.yukihookapi.hook.utils.factory.dumpToString
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
import java.io.File
/**
* 全局 Log 管理类
*/
object YLog {
/**
* 配置 [YLog]
*/
object Configs {
/**
* 标签
*
* 显示效果如下 ↓
*
* ```
* [YukiHookAPI][...][...] ...
* ```
*/
const val TAG = 1000
/**
* 优先级
*
* 显示效果如下 ↓
*
* ```
* [...][E][...] ...
* ```
*/
const val PRIORITY = 1001
/**
* 当前宿主的包名
*
* 显示效果如下 ↓
*
* ```
* [...][com.demo.test][...] ...
* ```
*/
const val PACKAGE_NAME = 1002
/**
* 当前宿主的用户 ID (主用户不显示)
*
* 显示效果如下 ↓
*
* ```
* [...][...][999] ...
* ```
*/
const val USER_ID = 1003
/**
* 这是一个调试日志的全局标识
*
* 默认文案为 [YukiHookAPI.TAG]
*
* 你可以修改为你自己的文案
*/
var tag = YukiHookAPI.TAG
/**
* 是否启用调试日志的输出功能 - 默认启用
*
* - 关闭后将会停用 [YukiHookAPI] 对全部日志的输出
*
* 但是不影响当你手动调用下面这些方法输出日志
*
* [debug]、[info]、[warn]、[error]
*
* 当 [isEnable] 关闭后 [YukiHookAPI.Configs.isDebug] 也将同时关闭
*/
var isEnable = true
/**
* 是否启用调试日志的记录功能 - 默认不启用
*
* 开启后将会在内存中记录全部可用的日志和异常堆栈
*
* 需要同时启用 [isEnable] 才能有效
*
* - 过量的日志可能会导致宿主运行缓慢或造成频繁 GC
*
* 开启后你可以调用 [saveToFile] 实时保存日志到文件或使用 [contents] 获取实时日志文件
*/
var isRecord = false
/** 当前已添加的元素顺序列表数组 */
internal var elements = arrayOf(TAG, PRIORITY, PACKAGE_NAME, USER_ID)
/**
* 自定义调试日志对外显示的元素
*
* 只对日志记录和 (Xposed) 宿主环境的日志生效
*
* 日志元素的排列将按照你在 [item] 中设置的顺序进行显示
*
* 你还可以留空 [item] 以不显示除日志内容外的全部元素
*
* 可用的元素有:[TAG]、[PRIORITY]、[PACKAGE_NAME]、[USER_ID]
*
* 默认排列方式如下 ↓
*
* ```
* [TAG][PRIORITY][PACKAGE_NAME][USER_ID] Message
* ```
* @param item 自定义的元素数组
*/
fun elements(vararg item: Int) {
elements = arrayOf(*item.toTypedArray())
}
/** 结束方法体 */
internal fun build() = Unit
}
/**
* 当前全部已记录的日志数据
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
*/
val inMemoryData = mutableListOf<YLogData>()
/**
* 获取当前日志文件内容
*
* 如果当前没有已记录的日志会返回空字符串
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
* @return [String]
*/
val contents get() = contents()
/**
* 获取、格式化当前日志文件内容
*
* 如果当前没有已记录的日志 ([data] 为空) 会返回空字符串
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
* @param data 日志数据 - 默认为 [inMemoryData]
* @return [String]
*/
fun contents(data: List<YLogData> = inMemoryData): String {
var content = ""
data.takeIf { it.isNotEmpty() }?.forEach {
content += "${it.head}$it\n"
it.throwable?.also { e ->
content += "${it.head}Dump stack trace for \"${e.current().name}\":\n"
content += e.dumpToString()
}
}
return content
}
/**
* 清除全部已记录的日志
*
* 你也可以直接获取 [inMemoryData] 来清除
*
* - 获取到的日志数据在 Hook APP (宿主) 及模块进程中是相互隔离的
*/
fun clear() = inMemoryData.clear()
/**
* 保存当前日志到文件
*
* 若当前未开启 [Configs.isRecord] 或记录为空则不会进行任何操作
*
* 日志文件会追加到 [fileName] 的文件结尾 - 若文件不存在会自动创建
*
* - 文件读写权限取决于当前宿主、模块已获取的权限
* @param fileName 完整文件名 - 例如 /data/data/.../files/xxx.log
* @param data 日志数据 - 默认为 [inMemoryData]
* @throws ErrnoException 如果目标路径不可写
*/
fun saveToFile(fileName: String, data: List<YLogData> = inMemoryData) {
if (data.isNotEmpty()) File(fileName).appendText(contents(data))
}
/**
* 打印 Debug 级别 Log
*
* 向控制台和 (Xposed) 宿主环境打印日志
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [Configs.tag]
* @param env 日志打印的环境 - 默认为 [EnvType.BOTH]
*/
fun debug(msg: String = "", e: Throwable? = null, tag: String = Configs.tag, env: EnvType = EnvType.BOTH) =
log(env, YLogData(priority = "D", tag = tag, msg = msg, throwable = e))
/**
* 打印 Info 级别 Log
*
* 向控制台和 (Xposed) 宿主环境打印日志
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [Configs.tag]
* @param env 日志打印的环境 - 默认为 [EnvType.BOTH]
*/
fun info(msg: String = "", e: Throwable? = null, tag: String = Configs.tag, env: EnvType = EnvType.BOTH) =
log(env, YLogData(priority = "I", tag = tag, msg = msg, throwable = e))
/**
* 打印 Warn 级别 Log
*
* 向控制台和 (Xposed) 宿主环境打印日志
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [Configs.tag]
* @param env 日志打印的环境 - 默认为 [EnvType.BOTH]
*/
fun warn(msg: String = "", e: Throwable? = null, tag: String = Configs.tag, env: EnvType = EnvType.BOTH) =
log(env, YLogData(priority = "W", tag = tag, msg = msg, throwable = e))
/**
* 打印 Error 级别 Log
*
* 向控制台和 (Xposed) 宿主环境打印日志
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [Configs.tag]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param env 日志打印的环境 - 默认为 [EnvType.BOTH]
*/
fun error(msg: String = "", e: Throwable? = null, tag: String = Configs.tag, env: EnvType = EnvType.BOTH) =
log(env, YLogData(priority = "E", tag = tag, msg = msg, throwable = e))
/**
* [YukiHookAPI] (内部) 打印 Debug 级别 Log
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisable 禁止打印日志 - 标识后将什么也不做 - 默认否
*/
internal fun innerD(msg: String = "", e: Throwable? = null, isImplicit: Boolean = false, isDisable: Boolean = false) {
if (Configs.isEnable.not() || isDisable) return
log(EnvType.BOTH, YLogData(priority = "D", msg = msg, throwable = e), isImplicit)
}
/**
* [YukiHookAPI] (内部) 打印 Info 级别 Log
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisable 禁止打印日志 - 标识后将什么也不做 - 默认否
*/
internal fun innerI(msg: String = "", e: Throwable? = null, isImplicit: Boolean = false, isDisable: Boolean = false) {
if (Configs.isEnable.not() || isDisable) return
log(EnvType.BOTH, YLogData(priority = "I", msg = msg, throwable = e), isImplicit)
}
/**
* [YukiHookAPI] (内部) 打印 Warn 级别 Log
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisable 禁止打印日志 - 标识后将什么也不做 - 默认否
*/
internal fun innerW(msg: String = "", e: Throwable? = null, isImplicit: Boolean = false, isDisable: Boolean = false) {
if (Configs.isEnable.not() || isDisable) return
log(EnvType.BOTH, YLogData(priority = "W", msg = msg, throwable = e), isImplicit)
}
/**
* [YukiHookAPI] (内部) 打印 Error 级别 Log
* @param msg 日志打印的内容 - 默认空 - 如果你仅想打印异常堆栈可只设置 [e]
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
* @param isDisable 禁止打印日志 - 标识后将什么也不做 - 默认否
*/
internal fun innerE(msg: String = "", e: Throwable? = null, isImplicit: Boolean = false, isDisable: Boolean = false) {
if (Configs.isEnable.not() || isDisable) return
log(EnvType.BOTH, YLogData(priority = "E", msg = msg, throwable = e), isImplicit)
}
/**
* 向控制台和 (Xposed) 宿主环境打印日志 - 最终实现方法
* @param env 日志打印的环境
* @param data 日志数据
* @param isImplicit 是否隐式打印 - 不会记录 - 也不会显示包名和用户 ID
*/
private fun log(env: EnvType, data: YLogData, isImplicit: Boolean = false) {
/** 是否为有效日志 */
val isNotBlankLog = data.msg.isNotBlank() || (data.msg.isBlank() && data.throwable != null)
/** 打印到 [Log] */
fun logByLogd() = when (data.priority) {
"D" -> Log.d(data.tag, data.msg)
"I" -> Log.i(data.tag, data.msg)
"W" -> Log.w(data.tag, data.msg)
"E" -> Log.e(data.tag, data.msg, data.throwable)
else -> Log.wtf(data.tag, data.msg, data.throwable)
}
/** 打印到 (Xposed) 宿主环境 */
fun logByHooker() {
if (isNotBlankLog) YukiHookHelper.logByHooker(data.also { it.isImplicit = isImplicit }.toString(), data.throwable)
}
when (env) {
EnvType.LOGD -> logByLogd()
EnvType.XPOSED_ENVIRONMENT -> logByHooker()
EnvType.SCOPE -> if (YukiXposedModule.isXposedEnvironment) logByHooker() else logByLogd()
EnvType.BOTH -> {
logByLogd()
if (YukiXposedModule.isXposedEnvironment) logByHooker()
}
}
if (isImplicit.not() && Configs.isRecord && isNotBlankLog) inMemoryData.add(data)
}
/**
* 需要打印的日志环境类型
*
* 决定于模块与 (Xposed) 宿主环境使用的打印方式
*/
enum class EnvType {
/** 仅使用 [Log] */
LOGD,
/**
* 仅在 (Xposed) 宿主环境使用
*
* - 只能在 (Xposed) 宿主环境中使用 - 模块环境将不生效
*/
XPOSED_ENVIRONMENT,
/**
* 分区使用
*
* (Xposed) 宿主环境仅使用 [XPOSED_ENVIRONMENT]
*
* 模块环境仅使用 [LOGD]
*/
SCOPE,
/**
* 同时使用
*
* (Xposed) 宿主环境使用 [LOGD] 与 [XPOSED_ENVIRONMENT]
*
* 模块环境仅使用 [LOGD]
*/
BOTH
}
}

View File

@@ -0,0 +1,235 @@
/*
* YukiHookAPI - An efficient Hook API and Xposed Module solution built in Kotlin.
* Copyright (C) 2019-2023 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/2/3.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate", "DeprecatedCallableAddReplaceWith", "DEPRECATION")
package com.highcapable.yukihookapi.hook.log
import java.io.Serializable
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
enum class LoggerType {
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
LOGD,
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
XPOSEDBRIDGE,
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
XPOSED_ENVIRONMENT,
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
SCOPE,
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
BOTH
}
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
data class YukiLoggerData @Deprecated(message = "请迁移到 YLog") internal constructor(
@Deprecated(message = "请迁移到 YLog")
var timestamp: Long = 0L,
@Deprecated(message = "请迁移到 YLog")
var time: String = "",
@Deprecated(message = "请迁移到 YLog")
var tag: String = YukiHookLogger.Configs.tag,
@Deprecated(message = "请迁移到 YLog")
var priority: String = "",
@Deprecated(message = "请迁移到 YLog")
var packageName: String = "",
@Deprecated(message = "请迁移到 YLog")
var userId: Int = 0,
@Deprecated(message = "请迁移到 YLog")
var msg: String = "",
@Deprecated(message = "请迁移到 YLog")
var throwable: Throwable? = null
) : Serializable
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
object YukiHookLogger {
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
val inMemoryData = ArrayList<YukiLoggerData>()
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
val contents get() = ""
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
fun contents(data: ArrayList<YukiLoggerData> = inMemoryData) = ""
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
fun clear() = Unit
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
fun saveToFile(fileName: String, data: ArrayList<YukiLoggerData> = inMemoryData) = Unit
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
object Configs {
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
const val TAG = ""
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
const val PRIORITY = -1
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
const val PACKAGE_NAME = -1
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
const val USER_ID = -1
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
var isEnable = true
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
var isRecord = false
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
var tag = ""
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog")
fun elements(vararg item: Int) = Unit
}
}
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog", ReplaceWith("YLog.debug(msg = msg, tag = tag)"))
fun loggerD(tag: String = YLog.Configs.tag, msg: String, type: LoggerType = LoggerType.BOTH) {
YLog.debug(msg, tag = tag, env = when (type) {
LoggerType.BOTH -> YLog.EnvType.BOTH
LoggerType.LOGD -> YLog.EnvType.LOGD
LoggerType.SCOPE -> YLog.EnvType.SCOPE
LoggerType.XPOSEDBRIDGE, LoggerType.XPOSED_ENVIRONMENT -> YLog.EnvType.XPOSED_ENVIRONMENT
})
}
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog", ReplaceWith("YLog.info(msg = msg, tag = tag)"))
fun loggerI(tag: String = YLog.Configs.tag, msg: String, type: LoggerType = LoggerType.BOTH) {
YLog.info(msg, tag = tag, env = when (type) {
LoggerType.BOTH -> YLog.EnvType.BOTH
LoggerType.LOGD -> YLog.EnvType.LOGD
LoggerType.SCOPE -> YLog.EnvType.SCOPE
LoggerType.XPOSEDBRIDGE, LoggerType.XPOSED_ENVIRONMENT -> YLog.EnvType.XPOSED_ENVIRONMENT
})
}
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog", ReplaceWith("YLog.warn(msg = msg, tag = tag)"))
fun loggerW(tag: String = YLog.Configs.tag, msg: String, type: LoggerType = LoggerType.BOTH) {
YLog.warn(msg, tag = tag, env = when (type) {
LoggerType.BOTH -> YLog.EnvType.BOTH
LoggerType.LOGD -> YLog.EnvType.LOGD
LoggerType.SCOPE -> YLog.EnvType.SCOPE
LoggerType.XPOSEDBRIDGE, LoggerType.XPOSED_ENVIRONMENT -> YLog.EnvType.XPOSED_ENVIRONMENT
})
}
/**
* - LoggerFactory 已被弃用 - 请迁移到 [YLog]
*/
@Deprecated(message = "请迁移到 YLog", ReplaceWith("YLog.error(msg = msg, e = e, tag = tag)"))
fun loggerE(tag: String = YLog.Configs.tag, msg: String = "", e: Throwable? = null, type: LoggerType = LoggerType.BOTH) {
YLog.error(msg, e = e, tag = tag, env = when (type) {
LoggerType.BOTH -> YLog.EnvType.BOTH
LoggerType.LOGD -> YLog.EnvType.LOGD
LoggerType.SCOPE -> YLog.EnvType.SCOPE
LoggerType.XPOSEDBRIDGE, LoggerType.XPOSED_ENVIRONMENT -> YLog.EnvType.XPOSED_ENVIRONMENT
})
}

View File

@@ -0,0 +1,85 @@
/*
* YukiHookAPI - An efficient Hook API and Xposed Module solution built in Kotlin.
* Copyright (C) 2019-2023 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 2023/9/27.
*/
package com.highcapable.yukihookapi.hook.log.data
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import java.io.Serializable
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/**
* 调试日志数据实现类
* @param timestamp 当前时间戳
* @param time 当前 UTC 时间
* @param tag 当前标签
* @param priority 当前优先级 - D、I、W、E
* @param packageName 当前包名
* @param userId 当前用户 ID
* @param msg 当前日志内容
* @param throwable 当前异常堆栈
*/
data class YLogData internal constructor(
var timestamp: Long = 0L,
var time: String = "",
var tag: String = YLog.Configs.tag,
var priority: String = "",
var packageName: String = "",
var userId: Int = 0,
var msg: String = "",
var throwable: Throwable? = null
) : Serializable {
/** 是否隐式打印 */
internal var isImplicit = false
init {
timestamp = System.currentTimeMillis()
time = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ROOT).format(Date(timestamp))
packageName = if (YukiXposedModule.isXposedEnvironment) YukiXposedModule.hostProcessName else AppParasitics.currentPackageName
userId = AppParasitics.findUserId(AppParasitics.currentPackageName)
}
/**
* 获取头部时间字符串
* @return [String]
*/
internal val head get() = "$time ------ "
override fun toString(): String {
var content = ""
YLog.Configs.elements.takeIf { it.isNotEmpty() }?.forEach {
if (it == YLog.Configs.TAG) content += "[$tag]"
if (it == YLog.Configs.PRIORITY) content += "[$priority]"
if (it == YLog.Configs.PACKAGE_NAME && isImplicit.not() && packageName.isNotBlank()) content += "[$packageName]"
if (it == YLog.Configs.USER_ID && isImplicit.not() && userId != 0) content += "[$userId]"
}; return content.takeIf { it.isNotBlank() }?.let { "$content $msg" } ?: msg
}
}

View File

@@ -36,7 +36,7 @@ import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator.MemberHookCre
import com.highcapable.yukihookapi.hook.core.api.helper.YukiHookHelper
import com.highcapable.yukihookapi.hook.core.api.proxy.YukiHookCallback
import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
import java.lang.reflect.Constructor
import java.lang.reflect.Member
import java.lang.reflect.Method
@@ -179,7 +179,7 @@ class HookParam internal constructor(
*/
fun Throwable.throwToApp() {
param?.throwable = this
yLoggerE(msg = message ?: "", e = this)
YLog.innerE(message ?: "", this)
}
/**

View File

@@ -47,7 +47,7 @@ import com.highcapable.yukihookapi.hook.core.finder.classes.DexClassFinder
import com.highcapable.yukihookapi.hook.core.finder.tools.ReflectionTool
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ClassConditions
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
import com.highcapable.yukihookapi.hook.utils.factory.value
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
@@ -422,7 +422,7 @@ open class PackageParam internal constructor(internal var wrapper: PackageParamW
if (it.packageName.isNotBlank() && it.type != HookEntryType.ZYGOTE)
if (it.packageName == wrapper?.packageName)
hooker.assignInstance(packageParam = this)
else yLoggerW(
else YLog.innerW(
msg = "This Hooker \"${hooker::class.java.name}\" is singleton or reused, " +
"but the current process has multiple package name \"${wrapper?.packageName}\", " +
"the original is \"${it.packageName}\"\n" +

View File

@@ -34,7 +34,7 @@ import android.util.ArrayMap
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.core.api.compat.HookApiCategoryHelper
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
import com.highcapable.yukihookapi.hook.xposed.bridge.proxy.IYukiXposedModuleLifecycle
@@ -220,7 +220,7 @@ internal object YukiXposedModule : IYukiXposedModuleLifecycle {
AppParasitics.hookModuleAppRelated(it.appClassLoader, it.type)
if (it.type == HookEntryType.PACKAGE) AppParasitics.registerToAppLifecycle(it.packageName)
if (it.type == HookEntryType.RESOURCES) isSupportResourcesHook = true
}.onFailure { yLoggerE(msg = "An exception occurred in the Hooking Process of YukiHookAPI", e = it) }
}.onFailure { YLog.innerE("An exception occurred in the Hooking Process of YukiHookAPI", it) }
}
}
}

View File

@@ -30,7 +30,7 @@
package com.highcapable.yukihookapi.hook.xposed.bridge.caller
import android.content.pm.ApplicationInfo
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources
import com.highcapable.yukihookapi.hook.xposed.bridge.type.HookEntryType
@@ -77,9 +77,9 @@ internal object YukiXposedModuleCaller {
) = YukiXposedModule.onPackageLoaded(type, packageName, processName, appClassLoader, appInfo, appResources)
/**
* 打印内部 E 级别的日志
* 打印 Error 级别 Log
* @param msg 日志打印的内容
* @param e 异常堆栈信息 - 默认空
*/
internal fun internalLoggerE(msg: String, e: Throwable? = null) = yLoggerE(msg = msg, e = e)
internal fun callLogError(msg: String, e: Throwable? = null) = YLog.innerE(msg, e)
}

View File

@@ -33,7 +33,7 @@ import android.content.res.Resources
import android.content.res.XResources
import android.graphics.drawable.Drawable
import android.view.View
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.xposed.bridge.resources.YukiResources.LayoutInflatedParam
import de.robv.android.xposed.callbacks.XC_LayoutInflated
@@ -153,7 +153,7 @@ class YukiResources private constructor(private val baseInstance: XResources) :
private inline fun runIfAnyErrors(name: String, initiate: () -> Unit) {
runCatching {
initiate()
}.onFailure { yLoggerE(msg = "Failed to execute method \"$name\", maybe your Hook Framework not support Resources Hook", it) }
}.onFailure { YLog.innerE("Failed to execute method \"$name\", maybe your Hook Framework not support Resources Hook", it) }
}
}

View File

@@ -45,10 +45,8 @@ import android.os.Parcelable
import android.os.TransactionTooLargeException
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.CauseProblemsApi
import com.highcapable.yukihookapi.hook.log.YukiHookLogger
import com.highcapable.yukihookapi.hook.log.YukiLoggerData
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.log.data.YLogData
import com.highcapable.yukihookapi.hook.utils.factory.RandomSeed
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
@@ -92,7 +90,7 @@ class YukiHookDataChannel private constructor() {
private const val GET_YUKI_LOGGER_INMEMORY_DATA = "yuki_logger_inmemory_data_get"
/** 调试日志数据结果标签 */
private val RESULT_YUKI_LOGGER_INMEMORY_DATA = ChannelData<ArrayList<YukiLoggerData>>("yuki_logger_inmemory_data_result")
private val RESULT_YUKI_LOGGER_INMEMORY_DATA = ChannelData<List<YLogData>>("yuki_logger_inmemory_data_result")
/** 仅监听结果键值 */
private const val VALUE_WAIT_FOR_LISTENER = "wait_for_listener_value"
@@ -167,7 +165,7 @@ class YukiHookDataChannel private constructor() {
destroyedCallbacks.takeIf { it.isNotEmpty() }?.forEach { remove(it) }
}
}
}.onFailure { yLoggerE(msg = "Received action \"$action\" failed", e = it) }
}.onFailure { YLog.innerE("Received action \"$action\" failed", it) }
}
}
}
@@ -191,7 +189,7 @@ class YukiHookDataChannel private constructor() {
context is Application || isXposedEnvironment || (((context ?: receiverContext)
?.getSystemService(ACTIVITY_SERVICE) as? ActivityManager?)
?.getRunningTasks(9999)?.filter { context?.javaClass?.name == it?.topActivity?.className }?.size ?: 0) > 0
}.getOrNull() ?: yLoggerW(msg = "Couldn't got current Activity status because a SecurityException blocked it").let { false }
}.getOrNull() ?: YLog.innerW("Couldn't got current Activity status because a SecurityException blocked it").let { false }
/**
* 获取宿主广播 Action 名称
@@ -237,7 +235,7 @@ class YukiHookDataChannel private constructor() {
}
/** 注册监听模块与宿主之间的调试日志数据 */
wait<String>(GET_YUKI_LOGGER_INMEMORY_DATA) { fromPackageName ->
nameSpace(context, fromPackageName).put(RESULT_YUKI_LOGGER_INMEMORY_DATA, YukiHookLogger.inMemoryData)
nameSpace(context, fromPackageName).put(RESULT_YUKI_LOGGER_INMEMORY_DATA, YLog.inMemoryData)
}
}
}
@@ -439,17 +437,17 @@ class YukiHookDataChannel private constructor() {
}
/**
* 获取模块与宿主之间的 [ArrayList]<[YukiLoggerData]> 数据
* 获取模块与宿主之间的 [List]<[YLogData]> 数据
*
* 由于模块与宿主处于不同的进程 - 我们可以使用数据通讯桥访问各自的调试日志数据
*
* - 模块与宿主必须启用 [YukiHookLogger.Configs.isRecord] 才能获取到调试日志数据
* - 模块与宿主必须启用 [YLog.Configs.isRecord] 才能获取到调试日志数据
*
* - 由于 Android 限制了数据传输大小的最大值 - 如果调试日志过多将会自动进行分段发送 - 数据越大速度越慢
* @param priority 响应优先级 - 默认不设置
* @param result 回调 [ArrayList]<[YukiLoggerData]>
* @param result 回调 [List]<[YLogData]>
*/
fun obtainLoggerInMemoryData(priority: ChannelPriority? = null, result: (ArrayList<YukiLoggerData>) -> Unit) {
fun obtainLoggerInMemoryData(priority: ChannelPriority? = null, result: (List<YLogData>) -> Unit) {
wait(RESULT_YUKI_LOGGER_INMEMORY_DATA, priority) { result(it) }
put(GET_YUKI_LOGGER_INMEMORY_DATA, packageName)
}
@@ -565,10 +563,10 @@ class YukiHookDataChannel private constructor() {
segmentsTempData.remove(wrapper.wrapperId)
}
}
else -> yLoggerE(msg = "Unsupported segments data key of \"${wrapper.instance.key}\"'s type")
else -> YLog.innerE("Unsupported segments data key of \"${wrapper.instance.key}\"'s type")
}
}.onFailure {
yLoggerE(msg = "YukiHookDataChannel cannot merge this segments data key of \"${wrapper.instance.key}\"", e = it)
YLog.innerE("YukiHookDataChannel cannot merge this segments data key of \"${wrapper.instance.key}\"", it)
} else wrapper.instance.value?.let { e -> result(e) }
}
@@ -583,7 +581,7 @@ class YukiHookDataChannel private constructor() {
/** 当前需要发送的数据字节大小 */
val dataByteSize = wrapper.instance.calDataByteSize()
if (dataByteSize < 0 && isAllowSendTooLargeData.not()) return yLoggerE(
if (dataByteSize < 0 && isAllowSendTooLargeData.not()) return YLog.innerE(
msg = "YukiHookDataChannel cannot calculate the byte size of the data key of \"${wrapper.instance.key}\" to be sent, " +
"so this data cannot be sent\n" +
"If you want to lift this restriction, use the allowSendTooLargeData function when calling, " +
@@ -595,7 +593,7 @@ class YukiHookDataChannel private constructor() {
* @param size 分段总大小 (长度)
*/
fun loggerForTooLargeData(name: String, size: Int) {
if (YukiHookAPI.Configs.isDebug) yLoggerW(
if (YukiHookAPI.Configs.isDebug) YLog.innerW(
msg = "This data key of \"${wrapper.instance.key}\" type $name is too large (total ${dataByteSize / 1024f} KB, " +
"limit ${receiverDataMaxByteSize / 1024f} KB), will be segmented to $size piece to send"
)
@@ -605,7 +603,7 @@ class YukiHookDataChannel private constructor() {
* 如果数据过大且无法分段打印错误信息
* @param suggestionMessage 建议内容 - 默认空
*/
fun loggerForUnprocessableData(suggestionMessage: String = "") = yLoggerE(
fun loggerForUnprocessableData(suggestionMessage: String = "") = YLog.innerE(
msg = "YukiHookDataChannel cannot send this data key of \"${wrapper.instance.key}\" type ${wrapper.instance.value?.javaClass}, " +
"because it is too large (total ${dataByteSize / 1024f} KB, " +
"limit ${receiverDataMaxByteSize / 1024f} KB) and cannot be segmented\n" +
@@ -710,7 +708,7 @@ class YukiHookDataChannel private constructor() {
if (packageName != AppParasitics.SYSTEM_FRAMEWORK_NAME)
setPackage(if (isXposedEnvironment) YukiXposedModule.modulePackageName else packageName)
putExtra(wrapper.instance.key + keyNonRepeatName, wrapper)
}) ?: yLoggerE(msg = "Failed to sendBroadcast like \"${wrapper.instance.key}\", because got null context in \"$packageName\"")
}) ?: YLog.innerE("Failed to sendBroadcast like \"${wrapper.instance.key}\", because got null context in \"$packageName\"")
}
}
}

View File

@@ -63,8 +63,7 @@ import com.highcapable.yukihookapi.hook.factory.hasMethod
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.factory.toClass
import com.highcapable.yukihookapi.hook.factory.toClassOrNull
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.android.ActivityManagerClass
import com.highcapable.yukihookapi.hook.type.android.ActivityManagerNativeClass
import com.highcapable.yukihookapi.hook.type.android.ActivityTaskManagerClass
@@ -199,7 +198,7 @@ internal object AppParasitics {
*/
internal fun hookClassLoader(loader: ClassLoader?, result: (Class<*>) -> Unit) {
if (loader == null) return
if (YukiXposedModule.isXposedEnvironment.not()) return yLoggerW(msg = "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
if (isClassLoaderHooked) return
runCatching {
@@ -211,7 +210,7 @@ internal object AppParasitics {
}
})
isClassLoaderHooked = true
}.onFailure { yLoggerW(msg = "Try to hook ClassLoader failed: $it") }
}.onFailure { YLog.innerW("Try to hook ClassLoader failed: $it") }
}
/**
@@ -270,7 +269,7 @@ internal object AppParasitics {
*/
fun YukiHookCallback.Param.throwToAppOrLogger(throwable: Throwable) {
if (AppLifecycleCallback.isOnFailureThrowToApp) this.throwable = throwable
else yLoggerE(msg = "An exception occurred during AppLifecycle event", e = throwable)
else YLog.innerE("An exception occurred during AppLifecycle event", e = throwable)
}
/** Hook [Application] 装载方法 */
runCatching {
@@ -378,14 +377,14 @@ internal object AppParasitics {
internal fun injectModuleAppResources(hostResources: Resources) {
if (YukiXposedModule.isXposedEnvironment) runCatching {
if (currentPackageName == YukiXposedModule.modulePackageName)
return yLoggerE(msg = "You cannot inject module resources into yourself")
return YLog.innerE("You cannot inject module resources into yourself")
hostResources.assets.current(ignored = true).method {
name = "addAssetPath"
param(StringClass)
}.call(YukiXposedModule.moduleAppFilePath)
}.onFailure {
yLoggerE(msg = "Failed to inject module resources into [$hostResources]", e = it)
} else yLoggerW(msg = "You can only inject module resources in Xposed Environment")
YLog.innerE("Failed to inject module resources into [$hostResources]", it)
} else YLog.innerW("You can only inject module resources in Xposed Environment")
}
/**
@@ -396,9 +395,9 @@ internal object AppParasitics {
@RequiresApi(Build.VERSION_CODES.N)
internal fun registerModuleAppActivities(context: Context, proxy: Any?) {
if (isActivityProxyRegistered) return
if (YukiXposedModule.isXposedEnvironment.not()) return yLoggerW(msg = "You can only register Activity Proxy in Xposed Environment")
if (context.packageName == YukiXposedModule.modulePackageName) return yLoggerE(msg = "You cannot register Activity Proxy into yourself")
if (Build.VERSION.SDK_INT < 24) return yLoggerE(msg = "Activity Proxy only support for Android 7.0 (API 24) or higher")
if (YukiXposedModule.isXposedEnvironment.not()) return YLog.innerW("You can only register Activity Proxy in Xposed Environment")
if (context.packageName == YukiXposedModule.modulePackageName) return YLog.innerE("You cannot register Activity Proxy into yourself")
if (Build.VERSION.SDK_INT < 24) return YLog.innerE("Activity Proxy only support for Android 7.0 (API 24) or higher")
runCatching {
ActivityProxyConfig.apply {
proxyIntentName = "${YukiXposedModule.modulePackageName}.ACTIVITY_PROXY_INTENT"
@@ -444,7 +443,7 @@ internal object AppParasitics {
}
}
isActivityProxyRegistered = true
}.onFailure { yLoggerE(msg = "Activity Proxy initialization failed because got an Exception", e = it) }
}.onFailure { YLog.innerE("Activity Proxy initialization failed because got an Exception", it) }
}
/**

View File

@@ -39,7 +39,7 @@ import android.os.Message
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.android.ActivityThreadClass
import com.highcapable.yukihookapi.hook.type.android.ClientTransactionClass
import com.highcapable.yukihookapi.hook.type.android.IBinderClass
@@ -76,7 +76,7 @@ internal object HandlerDelegateCaller {
set(intent.getParcelableExtra(ActivityProxyConfig.proxyIntentName))
}
}
}.onFailure { yLoggerE(msg = "Activity Proxy got an Exception in msg.what [$LAUNCH_ACTIVITY]", e = it) }
}.onFailure { YLog.innerE("Activity Proxy got an Exception in msg.what [$LAUNCH_ACTIVITY]", it) }
EXECUTE_TRANSACTION -> msg.obj?.runCatching client@{
ClientTransactionClass.method { name = "getCallbacks" }.ignored().get(this).list<Any?>().takeIf { it.isNotEmpty() }
?.forEach { item ->
@@ -100,7 +100,7 @@ internal object HandlerDelegateCaller {
}
}
}
}?.onFailure { yLoggerE(msg = "Activity Proxy got an Exception in msg.what [$EXECUTE_TRANSACTION]", e = it) }
}?.onFailure { YLog.innerE("Activity Proxy got an Exception in msg.what [$EXECUTE_TRANSACTION]", it) }
}
return baseInstance?.handleMessage(msg) ?: false
}

View File

@@ -37,8 +37,7 @@ import android.content.SharedPreferences
import android.util.ArrayMap
import androidx.preference.PreferenceFragmentCompat
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
import com.highcapable.yukihookapi.hook.xposed.bridge.delegate.XSharedPreferencesDelegate
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
@@ -150,7 +149,7 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu
makeWorldReadable()
reload()
}
}.onFailure { yLoggerE(msg = it.message ?: "Operating system not supported", e = it) }.getOrNull()
}.onFailure { YLog.innerE(it.message ?: "Operating system not supported", it) }.getOrNull()
?: error("Cannot load the XSharedPreferences, maybe is your Hook Framework not support it")
}
@@ -680,7 +679,7 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu
*/
private inline fun specifiedScope(callback: () -> Unit): Editor {
if (isXposedEnvironment.not() || isUsingNativeStorage) callback()
else yLoggerW(msg = "YukiHookPrefsBridge.Editor not allowed in Xposed Environment")
else YLog.innerW("YukiHookPrefsBridge.Editor not allowed in Xposed Environment")
return this
}
}

View File

@@ -30,7 +30,6 @@
package com.highcapable.yukihookapi.hook.xposed.proxy
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.log.yLoggerW
/**
* [YukiHookAPI] 的 Xposed 装载 API 调用接口
@@ -48,7 +47,7 @@ interface YukiHookXposedInitProxy {
* - 请将接口迁移到 [IYukiHookXposedInit]
*/
@Deprecated(message = "请将接口迁移到 IYukiHookXposedInit", level = DeprecationLevel.ERROR)
fun onInit() = yLoggerW(msg = "YukiHookXposedInitProxy was deprecated")
fun onInit() = Unit
/**
* - 此方法已过时
@@ -56,5 +55,5 @@ interface YukiHookXposedInitProxy {
* - 请将接口迁移到 [IYukiHookXposedInit]
*/
@Deprecated(message = "请将接口迁移到 IYukiHookXposedInit", level = DeprecationLevel.ERROR)
fun onHook() = yLoggerW(msg = "YukiHookXposedInitProxy was deprecated")
fun onHook() = Unit
}

View File

@@ -346,12 +346,12 @@ fun GenerateData.sources() = mapOf(
hookEntry.onXposedEvent()
hookEntry.onInit()
if (${ExternalCallerName.YukiXposedModuleCaller.second}.isXposedCallbackSetUp) {
${ExternalCallerName.YukiXposedModuleCaller.second}.internalLoggerE("You cannot load a hooker in \"onInit\" or \"onXposedEvent\" method! Aborted")
${ExternalCallerName.YukiXposedModuleCaller.second}.callLogError("You cannot load a hooker in \"onInit\" or \"onXposedEvent\" method! Aborted")
return
}
hookEntry.onHook()
${ExternalCallerName.YukiXposedModuleCaller.second}.callOnFinishLoadModule()
}.onFailure { ${ExternalCallerName.YukiXposedModuleCaller.second}.internalLoggerE("YukiHookAPI try to load hook entry class failed", it) }
}.onFailure { ${ExternalCallerName.YukiXposedModuleCaller.second}.callLogError("YukiHookAPI try to load hook entry class failed", it) }
${ExternalCallerName.YukiXposedModuleCaller.second}.callOnPackageLoaded(
type = when {
isZygoteLoaded -> HookEntryType.ZYGOTE
@@ -373,7 +373,7 @@ fun GenerateData.sources() = mapOf(
${ExternalCallerName.YukiXposedModuleCaller.second}.callOnStartLoadModule(modulePackageName, sparam.modulePath)
callOnXposedModuleLoaded(isZygoteLoaded = true)
isZygoteCalled = true
}.onFailure { ${ExternalCallerName.YukiXposedModuleCaller.second}.internalLoggerE("An exception occurred when YukiHookAPI loading Xposed Module", it) }
}.onFailure { ${ExternalCallerName.YukiXposedModuleCaller.second}.callLogError("An exception occurred when YukiHookAPI loading Xposed Module", it) }
}
fun callHandleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {