From 185e5586ab53e17e7ad703c28c0138a8f3a733e3 Mon Sep 17 00:00:00 2001 From: shifujun Date: Fri, 18 Nov 2022 14:18:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20BroadcastReceiver=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E4=BB=A5Wrapper=E6=96=B9=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shadow对于BroadcastReceiver所需的处理只涉及onReceive方法传回的参数。 由于插件的BroadcastReceiver是通过正常的Context对象直接注册到系统中的, 所以对于系统来说这些BroadcastReceiver和宿主的其他BroadcastReceiver 没有什么区别。系统回调onReceive方法时传回的Context和Intent也都是宿主的。 其中需要把Context换回ShadowContext,Intent中的ClassLoader换回插件的。 #865#issuecomment-1134246493指出:系统控件 ViewFlipper 在 onAttachedToWindow() 方法中使用 Contenxt.registerReceiverAsUser 方法进行广播注册;在 onDetachedFromWindow() 方法中使用 Context.unregisterReceiver 方法进行取消广播注册。 本地提交改为通过Transform修改插件中的onReceive方法,在BroadcastReceiver 内部进行参数转换。从而避免引入新的类型,同时也不依赖如何注册和反注册Receiver。 fix #865 close #1105 --- .../runtime/BroadcastReceiverWrapper.java | 23 --- .../core/runtime/ShadowApplication.java | 31 ---- .../shadow/core/runtime/ShadowContext.java | 38 ---- .../specific/ReceiverSupportTransform.kt | 173 ++++++++++++++++++ .../android/content/BroadcastReceiver.java | 5 + .../src/test/java/android/content/Intent.java | 8 + .../shadow/core/runtime/PluginPartInfo.java | 5 + .../core/runtime/PluginPartInfoManager.java | 7 + .../core/runtime/ShadowApplication.java | 2 +- .../shadow/core/runtime/ShadowContext.java | 6 + .../src/test/java/test/TestReceiver.java | 83 +++++++++ .../specific/ReceiverSupportTransformTest.kt | 114 ++++++++++++ 12 files changed, 402 insertions(+), 93 deletions(-) delete mode 100644 projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/BroadcastReceiverWrapper.java create mode 100644 projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransform.kt create mode 100644 projects/sdk/core/transform/src/test/java/android/content/BroadcastReceiver.java create mode 100644 projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfo.java create mode 100644 projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfoManager.java create mode 100644 projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowContext.java create mode 100644 projects/sdk/core/transform/src/test/java/test/TestReceiver.java create mode 100644 projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt diff --git a/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/BroadcastReceiverWrapper.java b/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/BroadcastReceiverWrapper.java deleted file mode 100644 index fc1d966b4..000000000 --- a/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/BroadcastReceiverWrapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.tencent.shadow.core.runtime; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class BroadcastReceiverWrapper extends BroadcastReceiver { - - final private BroadcastReceiver mRealBroadcastReceiver; - - final private ShadowContext mShadowContext; - - public BroadcastReceiverWrapper(BroadcastReceiver broadcastReceiver, ShadowContext shadowContext) { - mRealBroadcastReceiver = broadcastReceiver; - mShadowContext = shadowContext; - } - - @Override - public void onReceive(Context context, Intent intent) { - intent.setExtrasClassLoader(mShadowContext.mPluginClassLoader); - mRealBroadcastReceiver.onReceive(mShadowContext, intent); - } -} diff --git a/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowApplication.java b/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowApplication.java index 734714c69..ce362ddce 100644 --- a/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowApplication.java +++ b/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowApplication.java @@ -50,22 +50,6 @@ public class ShadowApplication extends ShadowContext { public boolean isCallOnCreate; - /** - * BroadcastReceiver到BroadcastReceiverWrapper对象到映射关系 - *

- * 采用WeakHashMap> - * 使key和value都采用弱引用持有,以保持原本BroadcastReceiver的GC回收时机。 - *

- * BroadcastReceiver由原有业务代码强持有(也可能不持有),BroadcastReceiver原本在registerReceiver - * 之后交由系统持有,现在由BroadcastReceiverWrapper代替它被系统强持有。 - * 所以BroadcastReceiverWrapper强引用持有BroadcastReceiver,保持了系统强引用BroadcastReceiver的关系。 - *

- * 如果业务原本没有持有BroadcastReceiver,也就不会再有unregisterReceiver调用来, - * 也就不需要Map中有wrapper对应关系,所以用弱引用持有此关系没有影响。 - */ - final private Map> - mReceiverWrapperMap = new WeakHashMap<>(); - @Override public Context getApplicationContext() { return this; @@ -196,19 +180,4 @@ public static String getProcessName() { return Application.getProcessName(); } - public BroadcastReceiverWrapper receiverToWrapper(BroadcastReceiver receiver) { - if (receiver == null) { - return null; - } - synchronized (mReceiverWrapperMap) { - WeakReference weakReference - = mReceiverWrapperMap.get(receiver); - BroadcastReceiverWrapper wrapper = weakReference == null ? null : weakReference.get(); - if (wrapper == null) { - wrapper = new BroadcastReceiverWrapper(receiver, this); - mReceiverWrapperMap.put(receiver, new WeakReference<>(wrapper)); - } - return wrapper; - } - } } diff --git a/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowContext.java b/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowContext.java index 5aa848787..7dfd7d024 100644 --- a/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowContext.java +++ b/projects/sdk/core/runtime/src/main/java/com/tencent/shadow/core/runtime/ShadowContext.java @@ -18,18 +18,15 @@ package com.tencent.shadow.core.runtime; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.text.TextUtils; import android.util.Pair; import android.view.LayoutInflater; @@ -234,44 +231,9 @@ public String getPackageName() { return mApplicationInfo.packageName; } - - @Override - public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { - return super.registerReceiver(receiverToWrapper(receiver), filter); - } - - @Override - public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) { - return super.registerReceiver(receiverToWrapper(receiver), filter, flags); - } - - @Override - public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { - return super.registerReceiver(receiverToWrapper(receiver), filter, broadcastPermission, scheduler); - } - - @Override - public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) { - return super.registerReceiver(receiverToWrapper(receiver), filter, broadcastPermission, scheduler, flags); - } - - @Override - public void unregisterReceiver(BroadcastReceiver receiver) { - BroadcastReceiverWrapper wrapper = receiverToWrapper(receiver); - if (wrapper != null) { - super.unregisterReceiver(wrapper); - } else { - super.unregisterReceiver(receiver); - } - } - @Override public String getPackageCodePath() { PluginPartInfo pluginInfo = PluginPartInfoManager.getPluginInfo(getClassLoader()); return pluginInfo.packageManager.getArchiveFilePath(); } - - private BroadcastReceiverWrapper receiverToWrapper(BroadcastReceiver receiver) { - return mShadowApplication.receiverToWrapper(receiver); - } } diff --git a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransform.kt b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransform.kt new file mode 100644 index 000000000..79bfbc2ea --- /dev/null +++ b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransform.kt @@ -0,0 +1,173 @@ +/* + * Tencent is pleased to support the open source community by making Tencent Shadow available. + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.tencent.shadow.core.transform.specific + +import com.tencent.shadow.core.transform_kit.SpecificTransform +import com.tencent.shadow.core.transform_kit.TransformStep +import javassist.* +import javassist.compiler.Javac.CtFieldWithInit + +/** + * 系统回调BroadcastReceiver的onReceive(Context context, Intent intent)方法时: + * 1. 传回的context是宿主的,需要修改为插件的。 + * 2. intent的ExtrasClassLoader是宿主的,需要改为插件的。 + * + * 如果是系统类的BroadcastReceiver,它也不会和插件的context或classloader有什么联系, + * 所以我们只需要修改插件代码中的BroadcastReceiver。 + * + * 把原本插件的onReceive方法改个名字,再统一添加一个onReceive方法。 + * 在新增的onReceive方法中修改收到的系统回调参数,再转调被改名了的原本插件的onReceive方法。 + */ +class ReceiverSupportTransform : SpecificTransform() { + + companion object { + const val AndroidBroadcastReceiverClassname = "android.content.BroadcastReceiver" + const val AndroidContextClassname = "android.content.Context" + const val AndroidIntentClassname = "android.content.Intent" + } + + private fun CtClass.isReceiver(): Boolean = isClassOf(AndroidBroadcastReceiverClassname) + + override fun setup(allInputClass: Set) { + mClassPool.importPackage("android.content") + mClassPool.importPackage("com.tencent.shadow.core.runtime") + + val androidContext = mClassPool[AndroidContextClassname] + val androidIntent = mClassPool[AndroidIntentClassname] + + /** + * 收集覆盖了onReceive方法的Receiver作为修改目标 + */ + val targetReceivers = mutableSetOf() + newStep(object : TransformStep { + override fun filter(allInputClass: Set) = allInputClass + .filter { it.isReceiver() } + .toSet() + + override fun transform(ctClass: CtClass) { + val onReceiveMethod: CtMethod? = + try { + ctClass.getDeclaredMethod( + "onReceive", + arrayOf(androidContext, androidIntent) + ) + } catch (e: NotFoundException) { + null + } + if (onReceiveMethod != null) { + targetReceivers.add(ctClass) + } + } + }) + + /** + * 对原本的onReceive方法改名,并添加新的onReceive方法。 + */ + newStep(object : TransformStep { + override fun filter(allInputClass: Set) = targetReceivers + + override fun transform(ctClass: CtClass) { + ctClass.defrost() + + // 改名 + val originalOnReceiveMethod: CtMethod = + ctClass.getDeclaredMethod( + "onReceive", + arrayOf(androidContext, androidIntent) + ) + originalOnReceiveMethod.name = "onReceiveShadowContext" + originalOnReceiveMethod.modifiers = + Modifier.setPrivate(originalOnReceiveMethod.modifiers) + + // 声明两个域变量保存onReceive收到的原始参数,供调用super方法时使用。 + // the compiler embedded in Javassist does not support generics + arrayOf( + CtFieldWithInit.make( + "ThreadLocal originalOnReceiveContext = new ThreadLocal();", + ctClass + ), + CtFieldWithInit.make( + "ThreadLocal originalOnReceiveIntent = new ThreadLocal();", + ctClass + ), + ).forEach { + ctClass.addField(it) + } + + // 添加新onReceive方法 + val newOnReceiveMethod = CtMethod.make( + """ + public void onReceive(Context context, Intent intent) { + try{ + //保存收到的参数 + originalOnReceiveContext.set(context); + originalOnReceiveIntent.set(intent); + + //通过当前插件类ClassLoader找到相关的插件Application + ClassLoader cl = this.getClass().getClassLoader(); + PluginPartInfo info = PluginPartInfoManager.getPluginInfo(cl); + Context shadowContext = info.application; + Intent intentCopy = new Intent(intent);//不修改原本的intent + intentCopy.setExtrasClassLoader(cl); + + //调用原本的onReceive方法 + onReceiveShadowContext(shadowContext, intentCopy); + }finally { + originalOnReceiveContext.remove(); + originalOnReceiveIntent.remove(); + } + } + """.trimIndent(), ctClass + ) + ctClass.addMethod(newOnReceiveMethod) + + // 定义superOnReceiveMethod方法 + val newSuperMethod = CtMethod.make( + """ + private void superOnReceive(Context context, Intent intent) { + context = (Context)originalOnReceiveContext.get(); + intent = (Intent)originalOnReceiveIntent.get(); + super.onReceive(context, intent); + } + """.trimIndent(), ctClass + ) + + //转调super.onReceive方法 + val superMethod: CtMethod = + ctClass.superclass.getMethod( + "onReceive", + "(Landroid/content/Context;Landroid/content/Intent;)V" + ) + + if (Modifier.isAbstract(superMethod.modifiers).not()) { + val codeConverter = CodeConverter() + codeConverter.redirectMethodCall(superMethod, newSuperMethod) + try { + ctClass.instrument(codeConverter) + } catch (e: Exception) { + System.err.println("处理" + ctClass.name + "时出错:" + e) + throw e + } + ctClass.addMethod(newSuperMethod) + } + } + + }) + } +} diff --git a/projects/sdk/core/transform/src/test/java/android/content/BroadcastReceiver.java b/projects/sdk/core/transform/src/test/java/android/content/BroadcastReceiver.java new file mode 100644 index 000000000..e014fa629 --- /dev/null +++ b/projects/sdk/core/transform/src/test/java/android/content/BroadcastReceiver.java @@ -0,0 +1,5 @@ +package android.content; + +public abstract class BroadcastReceiver { + public abstract void onReceive(Context context, Intent intent); +} diff --git a/projects/sdk/core/transform/src/test/java/android/content/Intent.java b/projects/sdk/core/transform/src/test/java/android/content/Intent.java index 459e57566..8b02f57f7 100644 --- a/projects/sdk/core/transform/src/test/java/android/content/Intent.java +++ b/projects/sdk/core/transform/src/test/java/android/content/Intent.java @@ -1,4 +1,12 @@ package android.content; public class Intent { + public Intent() { + } + + public Intent(Intent intent) { + } + + public void setExtrasClassLoader(ClassLoader loader) { + } } diff --git a/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfo.java b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfo.java new file mode 100644 index 000000000..e419411f7 --- /dev/null +++ b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfo.java @@ -0,0 +1,5 @@ +package com.tencent.shadow.core.runtime; + +public class PluginPartInfo { + public ShadowApplication application = new ShadowApplication(); +} diff --git a/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfoManager.java b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfoManager.java new file mode 100644 index 000000000..140ddcc0e --- /dev/null +++ b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/PluginPartInfoManager.java @@ -0,0 +1,7 @@ +package com.tencent.shadow.core.runtime; + +public class PluginPartInfoManager { + public static PluginPartInfo getPluginInfo(ClassLoader classLoader) { + return new PluginPartInfo(); + } +} diff --git a/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowApplication.java b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowApplication.java index 4cd7838d7..6034cda44 100644 --- a/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowApplication.java +++ b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowApplication.java @@ -1,6 +1,6 @@ package com.tencent.shadow.core.runtime; -public class ShadowApplication { +public class ShadowApplication extends ShadowContext { public void onCreate() { } diff --git a/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowContext.java b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowContext.java new file mode 100644 index 000000000..19e099c46 --- /dev/null +++ b/projects/sdk/core/transform/src/test/java/com/tencent/shadow/core/runtime/ShadowContext.java @@ -0,0 +1,6 @@ +package com.tencent.shadow.core.runtime; + +import android.content.Context; + +public class ShadowContext extends Context { +} diff --git a/projects/sdk/core/transform/src/test/java/test/TestReceiver.java b/projects/sdk/core/transform/src/test/java/test/TestReceiver.java new file mode 100644 index 000000000..6e40e2596 --- /dev/null +++ b/projects/sdk/core/transform/src/test/java/test/TestReceiver.java @@ -0,0 +1,83 @@ +package test; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import java.util.List; + +/** + * 直接继承 + *

+ * 需要修改,但不用管super调用。 + */ +class AceReceiver extends BroadcastReceiver { + List log; + + AceReceiver(List log) { + this.log = log; + } + + @Override + public void onReceive(Context context, Intent intent) { + log.add("AceReceiver onReceive"); + } +} + +/** + * 间接继承,不调用父类方法 + *

+ * 需要修改,和直接继承一样。 + */ +class BarReceiver extends AceReceiver { + List log; + + BarReceiver(List log) { + super(log); + this.log = log; + } + + @Override + public void onReceive(Context context, Intent intent) { + // do not call super + log.add("BarReceiver onReceive"); + } +} + +/** + * 间接继承,调用父类方法 + *

+ * 需要修改,和直接继承一样。 + *

+ * 额外的,需要把super调用改到superOnReceive上。不能直接调用super的被修改后的方法, + * 因为super可能是个系统类,我们不会修改它。所以让superOnReceive用原本的参数调用super原本的方法。 + */ +class CatReceiver extends BarReceiver { + List log; + + CatReceiver(List log) { + super(log); + this.log = log; + } + + @Override + public void onReceive(Context context, Intent intent) { + log.add("CatReceiver onReceive enter"); + super.onReceive(context, intent); + log.add("CatReceiver onReceive leave"); + } +} + +/** + * 间接继承,不覆盖方法 + *

+ * 这种不用修改,它不会自己处理onReceive收到的参数。 + */ +class DogReceiver extends CatReceiver { + List log; + + DogReceiver(List log) { + super(log); + this.log = log; + } +} diff --git a/projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt b/projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt new file mode 100644 index 000000000..9e15aa860 --- /dev/null +++ b/projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt @@ -0,0 +1,114 @@ +package com.tencent.shadow.core.transform.specific + +import android.content.Context +import android.content.Intent +import com.tencent.shadow.core.transform_kit.AbstractTransformTest +import javassist.ClassPool +import javassist.Loader +import org.junit.Assert +import org.junit.Before +import org.junit.Test + +class ReceiverSupportTransformTest : AbstractTransformTest() { + + @Before + fun setUp() { + val classPool = ClassPool(null) + classPool.appendSystemPath() + val allInputClass = classPool[ + arrayOf( + "test.AceReceiver", + "test.BarReceiver", + "test.CatReceiver", + "test.DogReceiver", + ) + ].toMutableSet() + + val applicationTransform = ReceiverSupportTransform() + applicationTransform.mClassPool = classPool + applicationTransform.setup(allInputClass) + + applicationTransform.list.forEach { transform -> + transform.filter(allInputClass).forEach { + transform.transform(it) + it.writeFile(WRITE_FILE_DIR) + } + } + } + + private fun commonTestLogic(testClassName: String, expectLog: Array) { + //加载修改后的类对象 + val ctClass = dLoader[testClassName] + val loader = Loader(this.javaClass.classLoader, dLoader) + loader.delegateLoadingOf("android.content.") + val clazz = ctClass.toClass(loader) + + //构造实例,调用onReceive方法,检查log记录的字符串List是否符合预期 + val constructor = clazz.getDeclaredConstructor(List::class.java) + constructor.trySetAccessible() + val onReceive = + clazz.getMethod("onReceive", Context::class.java, Intent::class.java) + onReceive.trySetAccessible() + val log = clazz.getDeclaredField("log") + log.trySetAccessible() + val receiver = constructor.newInstance(mutableListOf()) + val context = Context() + val intent = Intent() + onReceive.invoke(receiver, context, intent) + val logList: List = log.get(receiver) as List + Assert.assertArrayEquals(expectLog, logList.toTypedArray()) + } + + @Test + fun testAceReceiver() { + commonTestLogic( + "test.AceReceiver", + arrayOf( + "AceReceiver onReceive" + ) + ) + } + + @Test + fun testBarReceiver() { + commonTestLogic( + "test.BarReceiver", + arrayOf( + "BarReceiver onReceive" + ) + ) + } + + @Test + fun testCatReceiver() { + commonTestLogic( + "test.CatReceiver", + arrayOf( + "CatReceiver onReceive enter", + "BarReceiver onReceive", + "CatReceiver onReceive leave", + ) + ) + } + + @Test + fun testDogReceiver() { + commonTestLogic( + "test.DogReceiver", + arrayOf( + "CatReceiver onReceive enter", + "BarReceiver onReceive", + "CatReceiver onReceive leave", + ) + ) + } + + /** + * DogReceiver本来就没有override onReceive,我们也不应该给它添加。 + */ + @Test(expected = javassist.NotFoundException::class) + fun testDogReceiverDoNotHaveOnReceiveMethod() { + val ctClass = dLoader["test.DogReceiver"] + ctClass.getDeclaredMethod("onReceive") + } +}