Skip to content

Commit 7c5a86a

Browse files
committed
adapt to android13
1 parent 32ecf10 commit 7c5a86a

File tree

10 files changed

+104
-45
lines changed

10 files changed

+104
-45
lines changed
2.1 MB
Binary file not shown.

app/src/main/java/com/malin/hook/MainActivity.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,18 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
7272
isApplicationContext = false
7373
)
7474
}
75+
7576
R.id.btn_start_host_unregister_appcompat_act -> {
7677
startActivity(
7778
startActType = Type.HOST_UNREGISTER_APPCOMPAT_ACTIVITY,
7879
isApplicationContext = false
7980
)
8081
}
82+
8183
R.id.btn_start_plugin_apk_activity -> {
8284
startActivity(startActType = Type.PLUGIN_ACTIVITY, isApplicationContext = false)
8385
}
86+
8487
R.id.btn_start_plugin_apk_appcompat_activity -> {
8588
startActivity(
8689
startActType = Type.PLUGIN_APPCOMPAT_ACTIVITY,
@@ -90,15 +93,11 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
9093
}
9194
}
9295

93-
/**
94-
* 每个条件分支都隐式地返回其最后一行的表达式的结果,因此无需使用 return 关键字。
95-
* 由于全部三个分支的结果都是 Intent 类型,因此 when 表达式的结果也是 Intent 类型。
96-
*/
96+
9797
private fun startActivity(startActType: Type, isApplicationContext: Boolean) {
98-
// 1.延时初始化
99-
lateinit var intent: Intent
10098

101-
// 2.条件表达式
99+
val intent: Intent
100+
102101
when (startActType) {
103102

104103
Type.HOST_EXIST_ACTIVITY -> {
@@ -131,14 +130,17 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
131130
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
132131
applicationContext.startActivity(intent)
133132
}
133+
134134
else -> {
135135
this.startActivity(intent)
136136
}
137137
}
138138
}
139139

140140
private companion object {
141-
private const val PLUGIN_PACKAGE_NAME = "com.malin.plugin"
141+
// 插件包名和宿主保持一致
142+
// https://juejin.cn/post/6844903875284058119
143+
private const val PLUGIN_PACKAGE_NAME = "com.malin.hook"
142144
private const val PLUGIN_ACTIVITY_NAME = "com.malin.plugin.PluginActivity"
143145
private const val PLUGIN_APPCOMPAT_ACTIVITY_NAME =
144146
"com.malin.plugin.PluginAppCompatActivity"

app/src/main/java/com/malin/hook/PluginResourceUtil.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.malin.hook
22

3+
import android.annotation.SuppressLint
34
import android.content.Context
45
import android.content.res.Resources
56
import android.graphics.drawable.Drawable
@@ -22,13 +23,15 @@ object PluginResourceUtil {
2223
1 -> {
2324
resId = getResId(pluginPackageName = pluginPackageName, resName = resourceName)
2425
}
26+
2527
2 -> {
2628
resId = getResId2(
2729
resources = resources,
2830
pluginPackageName = pluginPackageName,
2931
resName = resourceName
3032
)
3133
}
34+
3235
3 -> {
3336
resId = getResId3(
3437
context = context,
@@ -37,6 +40,7 @@ object PluginResourceUtil {
3740
resName = resourceName
3841
)
3942
}
43+
4044
4 -> {
4145
resId =
4246
getResId4(
@@ -60,6 +64,7 @@ object PluginResourceUtil {
6064
return 0
6165
}
6266

67+
@SuppressLint("DiscouragedApi")
6368
private fun getResId2(
6469
resources: Resources,
6570
pluginPackageName: String,

buildSrc/src/main/kotlin/KotlinConstants.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
object KotlinConstants {
22
const val gradle_version = "7.3.0"
33
const val proguard_gradle = "7.2.2"
4-
const val kotlin_version = "1.7.10"
4+
const val kotlin_version = "1.7.20"
55
}
66

77
object AppConfig {

clean.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ find . -name "build" -type d | xargs rm -rf
44
find . -name ".gradle" -type d | xargs rm -rf
55
find . -name ".idea" -type d | xargs rm -rf
66
find . -name "infer-out" -type d | xargs rm -rf
7+
find . -name "mapping.txt" -type f | xargs rm -rf
8+
find . -name "proguard-merge-config.txt" -type f | xargs rm -rf
79

810
find ./redex -name "*.txt" -type f | xargs rm -rf
911
find ./redex -name "*.idsig" -type f | xargs rm -rf
1012
find ./redex -name "*.json" -type f | xargs rm -rf
1113
find ./redex -name "*.apk" -type f | xargs rm -rf
12-

pluginapk/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ android {
1313
ndkVersion = AppConfig.ndkVersion
1414

1515
defaultConfig {
16-
applicationId = "com.malin.plugin"
16+
// 插件包名和宿主保持一致
17+
// https://juejin.cn/post/6844903875284058119
18+
applicationId = AppConfig.applicationId
1719
resourceConfigurations.addAll(arrayOf("zh", "en"))
1820
minSdk = AppConfig.minSdkVersion
1921
targetSdk = AppConfig.targetSdkVersion

pluginapk/src/main/java/com/malin/plugin/BaseActivity.java

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,45 @@ public class BaseActivity extends AppCompatActivity {
1414

1515
protected Context mContext;
1616

17+
protected boolean pluginInHostRunning = false;
18+
1719
@Override
1820
protected void onCreate(@Nullable Bundle savedInstanceState) {
1921
super.onCreate(savedInstanceState);
20-
Resources resource = PluginResourceUtil.getResource(getApplication());
21-
mContext = new ContextThemeWrapper(getBaseContext(), 0);
22-
Class<?> contextClazz = mContext.getClass();
22+
23+
Class<?> hostAppClazz = null;
2324
try {
24-
Field mResourcesField = contextClazz.getDeclaredField("mResources");
25-
mResourcesField.setAccessible(true);
26-
mResourcesField.set(mContext, resource);
27-
28-
Class<?> rClazz = Class.forName("com.google.android.material.R$style");
29-
Field themeField = rClazz.getDeclaredField("Theme_MaterialComponents_DayNight");
30-
themeField.setAccessible(true);
31-
Object themeObj = themeField.get(null);
32-
if (themeObj != null) {
33-
int theme = (int) themeObj;
34-
Field mThemeResourceField = contextClazz.getDeclaredField("mThemeResource");
35-
mThemeResourceField.setAccessible(true);
36-
mThemeResourceField.set(mContext, theme);
25+
hostAppClazz = Class.forName("com.malin.hook.MApplication");
26+
} catch (Throwable ignore) {
27+
}
28+
29+
pluginInHostRunning = hostAppClazz != null;
30+
if (pluginInHostRunning) {
31+
// 插件apk在宿主中运行
32+
Resources resource = PluginResourceUtil.getResource(getApplication());
33+
mContext = new ContextThemeWrapper(getBaseContext(), 0);
34+
Class<?> contextClazz = mContext.getClass();
35+
try {
36+
Field mResourcesField = contextClazz.getDeclaredField("mResources");
37+
mResourcesField.setAccessible(true);
38+
mResourcesField.set(mContext, resource);
39+
40+
Class<?> rClazz = Class.forName("com.google.android.material.R$style");
41+
Field themeField = rClazz.getDeclaredField("Theme_MaterialComponents_DayNight");
42+
themeField.setAccessible(true);
43+
Object themeObj = themeField.get(null);
44+
if (themeObj != null) {
45+
int theme = (int) themeObj;
46+
Field mThemeResourceField = contextClazz.getDeclaredField("mThemeResource");
47+
mThemeResourceField.setAccessible(true);
48+
mThemeResourceField.set(mContext, theme);
49+
}
50+
} catch (Throwable e) {
51+
e.printStackTrace();
3752
}
38-
} catch (Throwable e) {
39-
e.printStackTrace();
53+
} else {
54+
// 插件作为独立的apk运行
55+
mContext = null;
4056
}
4157
}
4258
}

pluginapk/src/main/java/com/malin/plugin/PluginAppCompatActivity.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,12 @@ protected void onCreate(Bundle savedInstanceState) {
1717
super.onCreate(savedInstanceState);
1818
Log.d(TAG, TAG + ":onCreate");
1919
View rootView;
20-
String packageName = getPackageName();
21-
// pluginapk/build.gradle.kts/android.defaultConfig.applicationId
22-
if (packageName.equals("com.malin.plugin")) {
23-
// 插件作为独立的apk运行
24-
rootView = LayoutInflater.from(this).inflate(R.layout.plugin_activity, null);
25-
} else {
20+
if (pluginInHostRunning) {
2621
// 插件apk在宿主中运行
2722
rootView = LayoutInflater.from(mContext).inflate(R.layout.plugin_activity, null);
23+
} else {
24+
// 插件作为独立的apk运行
25+
rootView = LayoutInflater.from(this).inflate(R.layout.plugin_activity, null);
2826
}
2927
rootView.setOnClickListener(new View.OnClickListener() {
3028
@Override

pluingImpl/src/main/java/com/malin/plugin/impl/HookActivity.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ object HookActivity {
2424
* 对IActivityManager接口中的startActivity方法进行动态代理,发生在app的进程中
2525
* [android.app.Activity.startActivity]
2626
* [android.app.Activity.startActivityForResult]
27+
* [android.app.Instrumentation]
2728
* android.app.Instrumentation#execStartActivity()
2829
* Activity#startActivityForResult-->Instrumentation#execStartActivity-->ActivityManager.getService().startActivity()-->
2930
* IActivityManager public int startActivity(android.app.IApplicationThread caller, java.lang.String callingPackage, android.content.Intent intent, java.lang.String resolvedType, android.os.IBinder resultTo, java.lang.String resultWho, int requestCode, int flags, android.app.ProfilerInfo profilerInfo, android.os.Bundle options) throws android.os.RemoteException;
@@ -338,7 +339,13 @@ object HookActivity {
338339
val safeIntent = safeIntentField[activityClientRecordObj] as? Intent ?: return
339340

340341
// 4.获取原始的Intent
341-
val originIntent = safeIntent.getParcelableExtra<Intent>(EXTRA_ORIGIN_INTENT) ?: return
342+
val originIntent: Intent? = if (Build.VERSION.SDK_INT >= 33) {
343+
safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT, Intent::class.java)
344+
} else {
345+
@Suppress("DEPRECATION")
346+
safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT)
347+
}
348+
if (originIntent == null) return
342349

343350
// 5.将安全的Intent,替换为原始的Intent,以启动我们要启动的未注册的Activity
344351
safeIntent.component = originIntent.component
@@ -450,7 +457,7 @@ object HookActivity {
450457
) : InvocationHandler {
451458
@Throws(InvocationTargetException::class, IllegalAccessException::class)
452459
override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
453-
if (method.name == "startActivity" && args != null && args.isNotEmpty()) {
460+
if (method.name == "startActivity" && !args.isNullOrEmpty()) {
454461
var intentIndex = 2
455462
for (i in args.indices) {
456463
if (args[i] is Intent) {
@@ -561,8 +568,14 @@ object HookActivity {
561568

562569
// 11.获取原始的Intent
563570
// 12.需要判断originIntent != null
564-
val originIntent =
565-
safeIntent.getParcelableExtra<Intent>(EXTRA_ORIGIN_INTENT) ?: return
571+
val originIntent: Intent? =
572+
if (Build.VERSION.SDK_INT >= 33) {
573+
safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT, Intent::class.java)
574+
} else {
575+
@Suppress("DEPRECATION")
576+
safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT)
577+
}
578+
if (originIntent == null) return
566579

567580
// 13.将原始的Intent,赋值给clientTransactionItem的mIntent属性
568581
safeIntent.component = originIntent.component
@@ -587,7 +600,7 @@ object HookActivity {
587600
@Throws(Throwable::class)
588601
override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
589602
// public android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName className, int flags, int userId)
590-
if ("getActivityInfo" == method.name && args != null && args.isNotEmpty()) {
603+
if ("getActivityInfo" == method.name && !args.isNullOrEmpty()) {
591604
var index = 0
592605
for (i in args.indices) {
593606
if (args[i] is ComponentName) {

pluingImpl/src/main/java/com/malin/plugin/impl/HookInstrumentation.kt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.app.Instrumentation
66
import android.content.Context
77
import android.content.Intent
88
import android.content.pm.PackageManager
9+
import android.content.pm.PackageManager.ResolveInfoFlags
910
import android.content.pm.ResolveInfo
1011
import android.os.Build
1112
import android.os.Bundle
@@ -99,12 +100,20 @@ object HookInstrumentation {
99100
if (Build.VERSION.SDK_INT >= 23) {
100101
flags = PackageManager.MATCH_ALL
101102
}
102-
resolveInfoList = mPackageManager.queryIntentActivities(intent, flags)
103+
resolveInfoList = if (Build.VERSION.SDK_INT >= 33) {
104+
mPackageManager.queryIntentActivities(
105+
intent,
106+
ResolveInfoFlags.of(flags.toLong())
107+
)
108+
} else {
109+
@Suppress("DEPRECATION")
110+
mPackageManager.queryIntentActivities(intent, flags)
111+
}
103112
} catch (throwable: Throwable) {
104113
throwable.printStackTrace()
105114
}
106115
var finalIntent = intent
107-
if (resolveInfoList == null || resolveInfoList.isEmpty()) {
116+
if (resolveInfoList.isNullOrEmpty()) {
108117
// 目标Activity没有在AndroidManifest.xml中注册的话,将目标Activity的ClassName保存到桩Intent中.
109118
finalIntent = Intent(who, mStubActivityClazz)
110119
// public class Intent implements Parcelable;
@@ -161,12 +170,20 @@ object HookInstrumentation {
161170
var resolveInfoList: List<ResolveInfo>? = null
162171
try {
163172
val flags = 0
164-
resolveInfoList = mPackageManager.queryIntentActivities(intent, flags)
173+
resolveInfoList = if (Build.VERSION.SDK_INT >= 33) {
174+
mPackageManager.queryIntentActivities(
175+
intent,
176+
ResolveInfoFlags.of(flags.toLong())
177+
)
178+
} else {
179+
@Suppress("DEPRECATION")
180+
mPackageManager.queryIntentActivities(intent, flags)
181+
}
165182
} catch (throwable: Throwable) {
166183
throwable.printStackTrace()
167184
}
168185
var finalIntent = intent
169-
if (resolveInfoList == null || resolveInfoList.isEmpty()) {
186+
if (resolveInfoList.isNullOrEmpty()) {
170187
// 目标Activity没有在AndroidManifest.xml中注册的话,将目标Activity的ClassName保存到桩Intent中.
171188
finalIntent = Intent(who, mStubActivityClazz)
172189
// public class Intent implements Parcelable;
@@ -215,7 +232,12 @@ object HookInstrumentation {
215232
className: String,
216233
intent: Intent,
217234
): Activity {
218-
val pluginIntent = intent.getParcelableExtra<Intent>(TARGET_INTENT_CLASS)
235+
val pluginIntent: Intent? = if (Build.VERSION.SDK_INT >= 33) {
236+
intent.getParcelableExtra(TARGET_INTENT_CLASS, Intent::class.java)
237+
} else {
238+
@Suppress("DEPRECATION")
239+
intent.getParcelableExtra(TARGET_INTENT_CLASS)
240+
}
219241
val pluginIntentClassNameExist = pluginIntent != null && !TextUtils.isEmpty(
220242
pluginIntent.component?.className
221243
)

0 commit comments

Comments
 (0)