mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-06 02:35:40 +08:00
Added Activity Proxy function
This commit is contained in:
@@ -24,6 +24,10 @@
|
|||||||
|
|
||||||
[filename](public/ModuleApplication.md ':include')
|
[filename](public/ModuleApplication.md ':include')
|
||||||
|
|
||||||
|
[filename](public/ModuleAppActivity.md ':include')
|
||||||
|
|
||||||
|
[filename](public/ModuleAppCompatActivity.md ':include')
|
||||||
|
|
||||||
[filename](public/YukiModuleResources.md ':include')
|
[filename](public/YukiModuleResources.md ':include')
|
||||||
|
|
||||||
[filename](public/YukiResources.md ':include')
|
[filename](public/YukiResources.md ':include')
|
||||||
|
17
docs/api/public/ModuleAppActivity.md
Normal file
17
docs/api/public/ModuleAppActivity.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
## ModuleAppActivity [class]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
open class ModuleAppActivity : Activity()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.93` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 代理 `Activity`。
|
||||||
|
|
||||||
|
继承于此类的 `Activity` 可以同时在宿主与模块中启动。
|
||||||
|
|
||||||
|
在 (Xposed) 宿主环境需要在宿主启动时调用 `Context.registerModuleAppActivities` 进行注册。
|
33
docs/api/public/ModuleAppCompatActivity.md
Normal file
33
docs/api/public/ModuleAppCompatActivity.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
## ModuleAppCompatActivity [class]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
open class ModuleAppCompatActivity : AppCompatActivity()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.93` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 代理 `AppCompatActivity`。
|
||||||
|
|
||||||
|
继承于此类的 `Activity` 可以同时在宿主与模块中启动。
|
||||||
|
|
||||||
|
在 (Xposed) 宿主环境需要在宿主启动时调用 `Context.registerModuleAppActivities` 进行注册。
|
||||||
|
|
||||||
|
在 (Xposed) 宿主环境需要重写 `moduleTheme` 设置 AppCompat 主题,否则会无法启动。
|
||||||
|
|
||||||
|
### moduleTheme [field]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
open val moduleTheme: Int
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.93` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 设置当前代理的 `Activity` 主题。
|
@@ -191,6 +191,150 @@ onAppLifecycle {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### registerModuleAppActivities [method]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun Context.registerModuleAppActivities(proxy: Any?)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.93` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 向 Hook APP (宿主) 注册当前 Xposed 模块的 `Activity`。
|
||||||
|
|
||||||
|
注册成功后,你就可以直接使用 `Context.startActivity` 来启动未在宿主中注册的 `Activity`。
|
||||||
|
|
||||||
|
你要将需要在宿主启动的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
||||||
|
|
||||||
|
为防止资源 ID 互相冲突,你需要在当前 Xposed 模块项目的 `build.gradle` 中修改资源 ID。
|
||||||
|
|
||||||
|
- Kotlin Gradle DSL
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
androidResources.additionalParameters("--allow-reserved-package-id", "--package-id", "0x64")
|
||||||
|
```
|
||||||
|
|
||||||
|
- Groovy
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
aaptOptions.additionalParameters '--allow-reserved-package-id', '--package-id', '0x64'
|
||||||
|
```
|
||||||
|
|
||||||
|
!> 提供的示例资源 ID 值仅供参考,为了防止当前宿主存在多个 Xposed 模块,建议自定义你自己的资源 ID。
|
||||||
|
|
||||||
|
!> 只能在 (Xposed) 宿主环境使用此功能,其它环境下使用将不生效且会打印警告信息。
|
||||||
|
|
||||||
|
**功能示例**
|
||||||
|
|
||||||
|
在 Hook 宿主之后,我们可以直接在 Hooker 中得到的 `Context` 注册当前模块的 `Activity` 代理。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
injectMember {
|
||||||
|
method {
|
||||||
|
name = "onCreate"
|
||||||
|
param(BundleClass)
|
||||||
|
}
|
||||||
|
afterHook {
|
||||||
|
instance<Activity>().registerModuleAppActivities()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
你还可以直接在 `AppLifecycle` 中注册当前模块的 `Activity` 代理。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
onAppLifecycle {
|
||||||
|
onCreate {
|
||||||
|
registerModuleAppActivities()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
如果没有填写 `proxy` 参数,API 将会根据当前 `Context` 自动获取当前宿主的启动入口 `Activity` 进行代理。
|
||||||
|
|
||||||
|
通常情况下,它是有效的,但是以上情况在一些 APP 中会失效,例如一些 `Activity` 会在注册清单上加入启动参数,那么我们就需要使用另一种解决方案。
|
||||||
|
|
||||||
|
若未注册的 `Activity` 不能被正确启动,我们可以手动拿到宿主的 `AndroidManifest.xml` 进行分析,来得到一个注册过的 `Activity` 标签,获取其中的 `name`。
|
||||||
|
|
||||||
|
你需要选择一个当前宿主可能用不到的、不需要的 `Activity` 作为一个“傀儡”将其进行代理,通常是有效的。
|
||||||
|
|
||||||
|
比如我们已经找到了能够被代理的合适 `Activity`。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<activity
|
||||||
|
android:name="com.demo.test.activity.TestActivity"
|
||||||
|
...>
|
||||||
|
```
|
||||||
|
|
||||||
|
根据其中的 `name`,我们只需要在方法中加入这个参数进行注册即可。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
registerModuleAppActivities(proxy = "com.demo.test.activity.TestActivity")
|
||||||
|
```
|
||||||
|
|
||||||
|
另一种情况,如果你对宿主的类编写了一个 `stub`,那么你可以直接通过 `Class` 对象来进行注册。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
registerModuleAppActivities(TestActivity::class.java)
|
||||||
|
```
|
||||||
|
|
||||||
|
注册完成后,请将你需要使用宿主启动的模块中的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
||||||
|
|
||||||
|
这些 `Activity` 现在无需注册即可无缝存活于宿主中。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class HostTestActivity : ModuleAppActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
// 模块资源已被自动注入,可以直接使用 xml 装载布局
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
若你需要继承于 `ModuleAppCompatActivity`,你需要手动设置 AppCompat 主题。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class HostTestActivity : ModuleAppCompatActivity() {
|
||||||
|
|
||||||
|
// 这里的主题名称仅供参考,请填写你模块中已有的主题名称
|
||||||
|
override val moduleTheme get() = R.style.Theme_AppCompat
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
// 模块资源已被自动注入,可以直接使用 xml 装载布局
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
以上步骤全部完成后,你就可以在 (Xposed) 宿主环境任意存在 `Context` 的地方愉快地调用 `startActivity` 了。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val context: Context = ... // 假设这就是你的 Context
|
||||||
|
context.startActivity(context, HostTestActivity::class.java)
|
||||||
|
```
|
||||||
|
|
||||||
### ~~isSupportResourcesHook [field]~~ <!-- {docsify-ignore} -->
|
### ~~isSupportResourcesHook [field]~~ <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
package com.highcapable.yukihookapi.hook.factory
|
package com.highcapable.yukihookapi.hook.factory
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
@@ -41,6 +42,8 @@ import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
|||||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppActivity
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppCompatActivity
|
||||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||||
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
@@ -126,6 +129,20 @@ fun Context.injectModuleAppResources() = resources?.injectModuleAppResources()
|
|||||||
*/
|
*/
|
||||||
fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResources(hostResources = this)
|
fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResources(hostResources = this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向 Hook APP (宿主) 注册当前 Xposed 模块的 [Activity]
|
||||||
|
*
|
||||||
|
* 注册成功后 - 你就可以直接使用 [Context.startActivity] 来启动未在宿主中注册的 [Activity]
|
||||||
|
*
|
||||||
|
* - 你要将需要在宿主启动的 [Activity] 继承于 [ModuleAppActivity] 或 [ModuleAppCompatActivity]
|
||||||
|
*
|
||||||
|
* 详情请参考 [registerModuleAppActivities](https://fankes.github.io/YukiHookAPI/#/api/document?id=registermoduleappactivities-method)
|
||||||
|
*
|
||||||
|
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
||||||
|
* @param proxy 代理的 [Activity] - 必须存在于宿主的 AndroidMainifest 清单中 - 不填使用默认 [Activity]
|
||||||
|
*/
|
||||||
|
fun Context.registerModuleAppActivities(proxy: Any? = null) = AppParasitics.registerModuleAppActivities(context = this, proxy)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 仅判断模块是否在太极、无极中激活
|
* 仅判断模块是否在太极、无极中激活
|
||||||
*
|
*
|
||||||
|
@@ -24,11 +24,16 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*
|
*
|
||||||
* This file is Created by fankes on 2022/8/14.
|
* This file is Created by fankes on 2022/8/14.
|
||||||
|
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||||
*/
|
*/
|
||||||
|
@file:Suppress("QueryPermissionsNeeded")
|
||||||
|
|
||||||
package com.highcapable.yukihookapi.hook.xposed.parasitic
|
package com.highcapable.yukihookapi.hook.xposed.parasitic
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.ActivityManager
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.app.Instrumentation
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -36,10 +41,9 @@ import android.content.IntentFilter
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.os.Handler
|
||||||
import com.highcapable.yukihookapi.YukiHookAPI
|
import com.highcapable.yukihookapi.YukiHookAPI
|
||||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
import com.highcapable.yukihookapi.hook.factory.*
|
||||||
import com.highcapable.yukihookapi.hook.factory.current
|
|
||||||
import com.highcapable.yukihookapi.hook.factory.method
|
|
||||||
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||||
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
||||||
@@ -53,6 +57,10 @@ import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
|||||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
|
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
|
||||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
|
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
|
||||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.HandlerDelegate
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.IActivityManagerProxy
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.InstrumentationDelegate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 这是一个管理 APP 寄生功能的控制类
|
* 这是一个管理 APP 寄生功能的控制类
|
||||||
@@ -69,6 +77,9 @@ internal object AppParasitics {
|
|||||||
/** [YukiHookDataChannel] 是否已经注册 */
|
/** [YukiHookDataChannel] 是否已经注册 */
|
||||||
private var isDataChannelRegister = false
|
private var isDataChannelRegister = false
|
||||||
|
|
||||||
|
/** [Activity] 代理是否已经注册 */
|
||||||
|
private var isActivityProxyRegister = false
|
||||||
|
|
||||||
/** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */
|
/** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */
|
||||||
private val injectedHostResourcesHashCodes = HashSet<Int>()
|
private val injectedHostResourcesHashCodes = HashSet<Int>()
|
||||||
|
|
||||||
@@ -237,6 +248,61 @@ internal object AppParasitics {
|
|||||||
dynamicModuleAppResources?.let { moduleAppResources = it }
|
dynamicModuleAppResources?.let { moduleAppResources = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向 Hook APP (宿主) 注册当前 Xposed 模块的 [Activity]
|
||||||
|
* @param context 当前 [Context]
|
||||||
|
* @param proxy 代理的 [Activity]
|
||||||
|
*/
|
||||||
|
internal fun registerModuleAppActivities(context: Context, proxy: Any?) {
|
||||||
|
if (isActivityProxyRegister) return
|
||||||
|
if (YukiHookBridge.hasXposedBridge.not()) return yLoggerW(msg = "You can only register Activity Proxy in Xposed Environment")
|
||||||
|
runCatching {
|
||||||
|
ActivityProxyConfig.apply {
|
||||||
|
proxyIntentName = "${YukiHookBridge.modulePackageName}.ACTIVITY_PROXY_INTENT"
|
||||||
|
proxyClassName = proxy?.let {
|
||||||
|
when (it) {
|
||||||
|
is String, is CharSequence -> it.toString()
|
||||||
|
is Class<*> -> it.name
|
||||||
|
else -> error("This proxy [$it] type is not allowed")
|
||||||
|
}
|
||||||
|
}?.takeIf { it.isNotBlank() } ?: context.packageManager?.runCatching {
|
||||||
|
queryIntentActivities(getLaunchIntentForPackage(context.packageName)!!, 0).first().activityInfo.name
|
||||||
|
}?.getOrNull() ?: ""
|
||||||
|
if ((proxyClassName.hasClass(context.classLoader) && classOf(proxyClassName, context.classLoader).hasMethod {
|
||||||
|
name = "setIntent"; param(IntentClass); superClass()
|
||||||
|
}).not()
|
||||||
|
) (if (proxyClassName.isBlank()) error("Cound not got launch intent for package \"${context.packageName}\"")
|
||||||
|
else error("Could not found \"$proxyClassName\" or Class is not a type of Activity"))
|
||||||
|
}
|
||||||
|
/** Patched [Instrumentation] */
|
||||||
|
ActivityThreadClass.field { name = "sCurrentActivityThread" }.ignored().get().any()?.current {
|
||||||
|
method { name = "getInstrumentation" }
|
||||||
|
.invoke<Instrumentation>()
|
||||||
|
?.also { field { name = "mInstrumentation" }.set(InstrumentationDelegate.wrapper(it)) }
|
||||||
|
HandlerClass.field { name = "mCallback" }.get(field { name = "mH" }.any()).apply {
|
||||||
|
cast<Handler.Callback?>()?.apply {
|
||||||
|
if (current().name != classOf<HandlerDelegate>().name) set(HandlerDelegate.wrapper(baseInstance = this))
|
||||||
|
} ?: set(HandlerDelegate.wrapper())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Patched [ActivityManager] */
|
||||||
|
runCatching {
|
||||||
|
runCatching {
|
||||||
|
ActivityManagerNativeClass.field { name = "gDefault" }.ignored().get().any()
|
||||||
|
}.getOrNull() ?: ActivityManagerClass.field { name = "IActivityManagerSingleton" }.ignored().get().any()
|
||||||
|
}.getOrNull()?.also { default ->
|
||||||
|
SingletonClass.field { name = "mInstance" }.ignored().result {
|
||||||
|
get(default).apply { any()?.also { set(IActivityManagerProxy.wrapper(IActivityManagerClass, it)) } }
|
||||||
|
ActivityTaskManagerClass.field { name = "IActivityTaskManagerSingleton" }.ignored().get().any().also { singleton ->
|
||||||
|
SingletonClass.method { name = "get" }.ignored().get(singleton).call()
|
||||||
|
get(singleton).apply { any()?.also { set(IActivityManagerProxy.wrapper(IActivityTaskManagerClass, it)) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isActivityProxyRegister = true
|
||||||
|
}.onFailure { yLoggerE(msg = "Activity Proxy initialization failed because got an Exception", e = it) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前 Hook APP (宿主) 的生命周期回调处理类
|
* 当前 Hook APP (宿主) 的生命周期回调处理类
|
||||||
*/
|
*/
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/8/8.
|
||||||
|
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||||
|
*/
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
|
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||||
|
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.ModuleClassLoader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理 [Activity]
|
||||||
|
*
|
||||||
|
* 继承于此类的 [Activity] 可以同时在宿主与模块中启动
|
||||||
|
*
|
||||||
|
* - 在 (Xposed) 宿主环境需要在宿主启动时调用 [Context.registerModuleAppActivities] 进行注册
|
||||||
|
*/
|
||||||
|
open class ModuleAppActivity : Activity() {
|
||||||
|
|
||||||
|
override fun getClassLoader(): ClassLoader? = ModuleClassLoader.instance()
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
|
savedInstanceState.getBundle("android:viewHierarchyState")?.classLoader = classOf<ModuleAppActivity>().classLoader
|
||||||
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/8/8.
|
||||||
|
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||||
|
*/
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||||
|
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.ModuleClassLoader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理 [AppCompatActivity]
|
||||||
|
*
|
||||||
|
* 继承于此类的 [Activity] 可以同时在宿主与模块中启动
|
||||||
|
*
|
||||||
|
* - 在 (Xposed) 宿主环境需要在宿主启动时调用 [Context.registerModuleAppActivities] 进行注册
|
||||||
|
*
|
||||||
|
* - 在 (Xposed) 宿主环境需要重写 [moduleTheme] 设置 AppCompat 主题 - 否则会无法启动
|
||||||
|
*/
|
||||||
|
open class ModuleAppCompatActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前代理的 [Activity] 主题
|
||||||
|
* @return [Int]
|
||||||
|
*/
|
||||||
|
open val moduleTheme get() = -1
|
||||||
|
|
||||||
|
override fun getClassLoader(): ClassLoader? = ModuleClassLoader.instance()
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
|
savedInstanceState.getBundle("android:viewHierarchyState")?.classLoader = classOf<ModuleAppActivity>().classLoader
|
||||||
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
if (YukiHookBridge.hasXposedBridge && moduleTheme != -1) setTheme(moduleTheme)
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/8/14.
|
||||||
|
*/
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前代理的 [Activity] 参数配置类
|
||||||
|
*/
|
||||||
|
internal object ActivityProxyConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于代理的 [Intent] 名称
|
||||||
|
*/
|
||||||
|
internal var proxyIntentName = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要代理的 [Activity] 类名
|
||||||
|
*/
|
||||||
|
internal var proxyClassName = ""
|
||||||
|
}
|
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/8/8.
|
||||||
|
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||||
|
*/
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
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.type.android.ActivityThreadClass
|
||||||
|
import com.highcapable.yukihookapi.hook.type.android.ClientTransactionClass
|
||||||
|
import com.highcapable.yukihookapi.hook.type.android.IBinderClass
|
||||||
|
import com.highcapable.yukihookapi.hook.type.android.IntentClass
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理当前 [Handler.Callback]
|
||||||
|
* @param baseInstance 原始实例
|
||||||
|
*/
|
||||||
|
internal class HandlerDelegate private constructor(private val baseInstance: Handler.Callback?) : Handler.Callback {
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
|
||||||
|
/** 启动 [Activity] */
|
||||||
|
private const val LAUNCH_ACTIVITY = 100
|
||||||
|
|
||||||
|
/** 执行事务处理 */
|
||||||
|
private const val EXECUTE_TRANSACTION = 159
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 [Handler.Callback] 创建 [HandlerDelegate] 实例
|
||||||
|
* @param baseInstance [Handler.Callback] 实例 - 可空
|
||||||
|
* @return [HandlerDelegate]
|
||||||
|
*/
|
||||||
|
internal fun wrapper(baseInstance: Handler.Callback? = null) = HandlerDelegate(baseInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleMessage(msg: Message): Boolean {
|
||||||
|
when (msg.what) {
|
||||||
|
LAUNCH_ACTIVITY -> runCatching {
|
||||||
|
msg.obj.current().field { name = "intent" }.apply {
|
||||||
|
cast<Intent?>()?.also { intent ->
|
||||||
|
IntentClass.field { name = "mExtras" }.ignored().get(intent).cast<Bundle?>()
|
||||||
|
?.classLoader = YukiHookAppHelper.currentApplication()?.classLoader
|
||||||
|
if (intent.hasExtra(ActivityProxyConfig.proxyIntentName))
|
||||||
|
set(intent.getParcelableExtra(ActivityProxyConfig.proxyIntentName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.onFailure { yLoggerE(msg = "Activity Proxy got an Exception in msg.what [$LAUNCH_ACTIVITY]", e = it) }
|
||||||
|
EXECUTE_TRANSACTION -> msg.obj?.runCatching client@{
|
||||||
|
ClientTransactionClass.method { name = "getCallbacks" }.ignored().get(this).list<Any?>().takeIf { it.isNotEmpty() }
|
||||||
|
?.forEach { item ->
|
||||||
|
item?.current()?.takeIf { it.name.contains(other = "LaunchActivityItem") }?.field { name = "mIntent" }?.apply {
|
||||||
|
cast<Intent?>()?.also { intent ->
|
||||||
|
IntentClass.field { name = "mExtras" }.ignored().get(intent).cast<Bundle?>()
|
||||||
|
?.classLoader = YukiHookAppHelper.currentApplication()?.classLoader
|
||||||
|
if (intent.hasExtra(ActivityProxyConfig.proxyIntentName))
|
||||||
|
intent.getParcelableExtra<Intent>(ActivityProxyConfig.proxyIntentName).also { subIntent ->
|
||||||
|
if (Build.VERSION.SDK_INT >= 31)
|
||||||
|
ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()
|
||||||
|
?.current()?.method {
|
||||||
|
name = "getLaunchingActivity"
|
||||||
|
param(IBinderClass)
|
||||||
|
}?.call(this@client.current().method { name = "getActivityToken" }.call())
|
||||||
|
?.current()?.field { name = "intent" }?.set(subIntent)
|
||||||
|
set(subIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}?.onFailure { yLoggerE(msg = "Activity Proxy got an Exception in msg.what [$EXECUTE_TRANSACTION]", e = it) }
|
||||||
|
}
|
||||||
|
return baseInstance?.handleMessage(msg) ?: false
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/8/8.
|
||||||
|
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||||
|
*/
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Intent
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig
|
||||||
|
import java.lang.reflect.InvocationHandler
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
import java.lang.reflect.Proxy
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理当前 [ActivityManager]
|
||||||
|
* @param baseInstance 原始实例
|
||||||
|
*/
|
||||||
|
internal class IActivityManagerProxy private constructor(private val baseInstance: Any) : InvocationHandler {
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 [IActivityManagerProxy] 代理
|
||||||
|
* @param clazz 代理的目标 [Class]
|
||||||
|
* @param instance 代理的目标实例
|
||||||
|
* @return [Any] 代理包装后的实例
|
||||||
|
*/
|
||||||
|
internal fun wrapper(clazz: Class<*>, instance: Any) =
|
||||||
|
Proxy.newProxyInstance(AppParasitics.baseClassLoader, arrayOf(clazz), IActivityManagerProxy(instance))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(proxy: Any?, method: Method?, args: Array<Any>?): Any? {
|
||||||
|
if (method?.name == "startActivity") args?.indexOfFirst { it is Intent }?.also { index ->
|
||||||
|
val argsInstance = (args[index] as? Intent) ?: return@also
|
||||||
|
val component = argsInstance.component
|
||||||
|
if (component != null &&
|
||||||
|
component.packageName == YukiHookAppHelper.currentPackageName() &&
|
||||||
|
component.className.startsWith(YukiHookBridge.modulePackageName)
|
||||||
|
) args[index] = Intent().apply {
|
||||||
|
setClassName(component.packageName, ActivityProxyConfig.proxyClassName)
|
||||||
|
putExtra(ActivityProxyConfig.proxyIntentName, argsInstance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return method?.invoke(baseInstance, *(args ?: emptyArray()))
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/8/8.
|
||||||
|
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||||
|
*/
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application
|
||||||
|
import android.app.Instrumentation
|
||||||
|
import android.app.UiAutomation
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
|
import android.os.*
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import com.highcapable.yukihookapi.hook.factory.*
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理当前 [Instrumentation]
|
||||||
|
* @param baseInstance 原始实例
|
||||||
|
*/
|
||||||
|
internal class InstrumentationDelegate private constructor(private val baseInstance: Instrumentation) : Instrumentation() {
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 [Instrumentation] 创建 [InstrumentationDelegate] 实例
|
||||||
|
* @param baseInstance [Instrumentation] 实例
|
||||||
|
* @return [InstrumentationDelegate]
|
||||||
|
*/
|
||||||
|
internal fun wrapper(baseInstance: Instrumentation) = InstrumentationDelegate(baseInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入当前 [Activity] 生命周期
|
||||||
|
* @param icicle [Bundle]
|
||||||
|
*/
|
||||||
|
private fun Activity.injectLifecycle(icicle: Bundle?) {
|
||||||
|
if (icicle != null && current().name.startsWith(YukiHookBridge.modulePackageName))
|
||||||
|
icicle.classLoader = AppParasitics.baseClassLoader
|
||||||
|
if (current().name.startsWith(YukiHookBridge.modulePackageName)) injectModuleAppResources()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newActivity(cl: ClassLoader?, className: String?, intent: Intent?): Activity? = try {
|
||||||
|
baseInstance.newActivity(cl, className, intent)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (className?.startsWith(YukiHookBridge.modulePackageName) == true)
|
||||||
|
classOf(className).buildOf<Activity>() ?: throw e
|
||||||
|
else throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(arguments: Bundle?) {
|
||||||
|
baseInstance.onCreate(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
baseInstance.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
baseInstance.onStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onException(obj: Any?, e: Throwable?) = baseInstance.onException(obj, e)
|
||||||
|
|
||||||
|
override fun sendStatus(resultCode: Int, results: Bundle?) {
|
||||||
|
baseInstance.sendStatus(resultCode, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addResults(results: Bundle?) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) baseInstance.addResults(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish(resultCode: Int, results: Bundle?) {
|
||||||
|
baseInstance.finish(resultCode, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAutomaticPerformanceSnapshots() {
|
||||||
|
baseInstance.setAutomaticPerformanceSnapshots()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startPerformanceSnapshot() {
|
||||||
|
baseInstance.startPerformanceSnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun endPerformanceSnapshot() {
|
||||||
|
baseInstance.endPerformanceSnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
baseInstance.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getContext(): Context? = baseInstance.context
|
||||||
|
|
||||||
|
override fun getComponentName(): ComponentName? = baseInstance.componentName
|
||||||
|
|
||||||
|
override fun getTargetContext(): Context? = baseInstance.targetContext
|
||||||
|
|
||||||
|
override fun getProcessName(): String? =
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) baseInstance.processName else AppParasitics.systemContext.processName
|
||||||
|
|
||||||
|
override fun isProfiling() = baseInstance.isProfiling
|
||||||
|
|
||||||
|
override fun startProfiling() {
|
||||||
|
baseInstance.startProfiling()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stopProfiling() {
|
||||||
|
baseInstance.stopProfiling()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setInTouchMode(inTouch: Boolean) {
|
||||||
|
baseInstance.setInTouchMode(inTouch)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun waitForIdle(recipient: Runnable?) {
|
||||||
|
baseInstance.waitForIdle(recipient)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun waitForIdleSync() {
|
||||||
|
baseInstance.waitForIdleSync()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun runOnMainSync(runner: Runnable?) {
|
||||||
|
baseInstance.runOnMainSync(runner)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startActivitySync(intent: Intent?): Activity? = baseInstance.startActivitySync(intent)
|
||||||
|
|
||||||
|
override fun startActivitySync(intent: Intent, options: Bundle?): Activity =
|
||||||
|
if (Build.VERSION.SDK_INT >= 28) baseInstance.startActivitySync(intent, options) else error("Operating system not supported")
|
||||||
|
|
||||||
|
override fun addMonitor(monitor: ActivityMonitor?) {
|
||||||
|
baseInstance.addMonitor(monitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addMonitor(cls: String?, result: ActivityResult?, block: Boolean): ActivityMonitor? =
|
||||||
|
baseInstance.addMonitor(cls, result, block)
|
||||||
|
|
||||||
|
override fun addMonitor(filter: IntentFilter?, result: ActivityResult?, block: Boolean): ActivityMonitor? =
|
||||||
|
baseInstance.addMonitor(filter, result, block)
|
||||||
|
|
||||||
|
override fun checkMonitorHit(monitor: ActivityMonitor?, minHits: Int) = baseInstance.checkMonitorHit(monitor, minHits)
|
||||||
|
|
||||||
|
override fun waitForMonitor(monitor: ActivityMonitor?): Activity? = baseInstance.waitForMonitor(monitor)
|
||||||
|
|
||||||
|
override fun waitForMonitorWithTimeout(monitor: ActivityMonitor?, timeOut: Long): Activity? =
|
||||||
|
baseInstance.waitForMonitorWithTimeout(monitor, timeOut)
|
||||||
|
|
||||||
|
override fun removeMonitor(monitor: ActivityMonitor?) {
|
||||||
|
baseInstance.removeMonitor(monitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invokeContextMenuAction(targetActivity: Activity?, id: Int, flag: Int) =
|
||||||
|
baseInstance.invokeContextMenuAction(targetActivity, id, flag)
|
||||||
|
|
||||||
|
override fun invokeMenuActionSync(targetActivity: Activity?, id: Int, flag: Int) =
|
||||||
|
baseInstance.invokeMenuActionSync(targetActivity, id, flag)
|
||||||
|
|
||||||
|
override fun sendCharacterSync(keyCode: Int) {
|
||||||
|
baseInstance.sendCharacterSync(keyCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendKeyDownUpSync(key: Int) {
|
||||||
|
baseInstance.sendKeyDownUpSync(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendKeySync(event: KeyEvent?) {
|
||||||
|
baseInstance.sendKeySync(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendPointerSync(event: MotionEvent?) {
|
||||||
|
baseInstance.sendPointerSync(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendStringSync(text: String?) {
|
||||||
|
baseInstance.sendStringSync(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendTrackballEventSync(event: MotionEvent?) {
|
||||||
|
baseInstance.sendTrackballEventSync(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application? =
|
||||||
|
baseInstance.newApplication(cl, className, context)
|
||||||
|
|
||||||
|
override fun callApplicationOnCreate(app: Application?) {
|
||||||
|
baseInstance.callApplicationOnCreate(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newActivity(
|
||||||
|
clazz: Class<*>?, context: Context?,
|
||||||
|
token: IBinder?, application: Application?,
|
||||||
|
intent: Intent?, info: ActivityInfo?,
|
||||||
|
title: CharSequence?, parent: Activity?,
|
||||||
|
id: String?, lastNonConfigurationInstance: Any?
|
||||||
|
): Activity? = baseInstance.newActivity(
|
||||||
|
clazz, context,
|
||||||
|
token, application,
|
||||||
|
intent, info, title,
|
||||||
|
parent, id, lastNonConfigurationInstance
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun callActivityOnCreate(activity: Activity, icicle: Bundle?, persistentState: PersistableBundle?) {
|
||||||
|
activity.injectLifecycle(icicle)
|
||||||
|
baseInstance.callActivityOnCreate(activity, icicle, persistentState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnCreate(activity: Activity, icicle: Bundle?) {
|
||||||
|
activity.injectLifecycle(icicle)
|
||||||
|
baseInstance.callActivityOnCreate(activity, icicle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnDestroy(activity: Activity?) {
|
||||||
|
baseInstance.callActivityOnDestroy(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnRestoreInstanceState(activity: Activity, savedInstanceState: Bundle) {
|
||||||
|
baseInstance.callActivityOnRestoreInstanceState(activity, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnRestoreInstanceState(activity: Activity, savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||||
|
baseInstance.callActivityOnRestoreInstanceState(activity, savedInstanceState, persistentState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnPostCreate(activity: Activity, savedInstanceState: Bundle?) {
|
||||||
|
baseInstance.callActivityOnPostCreate(activity, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnPostCreate(activity: Activity, savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||||
|
baseInstance.callActivityOnPostCreate(activity, savedInstanceState, persistentState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnNewIntent(activity: Activity?, intent: Intent?) {
|
||||||
|
baseInstance.callActivityOnNewIntent(activity, intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnStart(activity: Activity?) {
|
||||||
|
baseInstance.callActivityOnStart(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnRestart(activity: Activity?) {
|
||||||
|
baseInstance.callActivityOnRestart(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnPause(activity: Activity?) {
|
||||||
|
baseInstance.callActivityOnPause(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnResume(activity: Activity?) {
|
||||||
|
baseInstance.callActivityOnResume(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnStop(activity: Activity?) {
|
||||||
|
baseInstance.callActivityOnStop(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnUserLeaving(activity: Activity?) {
|
||||||
|
baseInstance.callActivityOnUserLeaving(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnSaveInstanceState(activity: Activity, outState: Bundle) {
|
||||||
|
baseInstance.callActivityOnSaveInstanceState(activity, outState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnSaveInstanceState(activity: Activity, outState: Bundle, outPersistentState: PersistableBundle) {
|
||||||
|
baseInstance.callActivityOnSaveInstanceState(activity, outState, outPersistentState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callActivityOnPictureInPictureRequested(activity: Activity) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) baseInstance.callActivityOnPictureInPictureRequested(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override fun startAllocCounting() {
|
||||||
|
baseInstance.startAllocCounting()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override fun stopAllocCounting() {
|
||||||
|
baseInstance.stopAllocCounting()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAllocCounts(): Bundle? = baseInstance.allocCounts
|
||||||
|
|
||||||
|
override fun getBinderCounts(): Bundle? = baseInstance.binderCounts
|
||||||
|
|
||||||
|
override fun getUiAutomation(): UiAutomation? = baseInstance.uiAutomation
|
||||||
|
|
||||||
|
override fun getUiAutomation(flags: Int): UiAutomation? =
|
||||||
|
if (Build.VERSION.SDK_INT >= 24) baseInstance.getUiAutomation(flags) else error("Operating system not supported")
|
||||||
|
|
||||||
|
override fun acquireLooperManager(looper: Looper?): TestLooperManager? =
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) baseInstance.acquireLooperManager(looper) else error("Operating system not supported")
|
||||||
|
}
|
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/8/8.
|
||||||
|
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||||
|
*/
|
||||||
|
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||||
|
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动处理 (Xposed) 宿主环境与模块环境的 [ClassLoader]
|
||||||
|
*/
|
||||||
|
internal class ModuleClassLoader private constructor() : ClassLoader(AppParasitics.baseClassLoader) {
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
|
||||||
|
/** 当前 [ModuleClassLoader] 单例 */
|
||||||
|
private var instance: ModuleClassLoader? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 [ModuleClassLoader] 单例
|
||||||
|
* @return [ModuleClassLoader]
|
||||||
|
*/
|
||||||
|
internal fun instance() = instance ?: ModuleClassLoader().apply { instance = this }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadClass(name: String, resolve: Boolean): Class<*> {
|
||||||
|
if (YukiHookBridge.hasXposedBridge.not()) return AppParasitics.baseClassLoader.loadClass(name)
|
||||||
|
return YukiHookAppHelper.currentApplication()?.classLoader?.let { loader ->
|
||||||
|
runCatching { return@let AppParasitics.baseClassLoader.loadClass(name) }
|
||||||
|
runCatching { if (name == "androidx.lifecycle.ReportFragment") return@let loader.loadClass(name) }
|
||||||
|
runCatching { AppParasitics.baseClassLoader.loadClass(name) }.getOrNull() ?: loader.loadClass(name)
|
||||||
|
} ?: super.loadClass(name, resolve)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user