├── .gitignore ├── LICENSE ├── PluginApi ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── reginald │ └── pluginm │ └── pluginapi │ ├── IInvokeCallback.java │ ├── IInvokeResult.java │ ├── IInvoker.java │ ├── IPluginLocalManager.java │ └── PluginHelper.java ├── PluginManager ├── .gitignore ├── build.gradle ├── consumer-proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── com │ │ └── reginald │ │ └── pluginm │ │ ├── IPluginClient.aidl │ │ ├── IPluginManager.aidl │ │ ├── PluginInfo.aidl │ │ ├── comm │ │ ├── IPluginComm.aidl │ │ └── invoker │ │ │ ├── InvokeCallback.aidl │ │ │ └── InvokeResult.aidl │ │ └── stub │ │ └── IPluginServiceStubBinder.aidl │ ├── java │ ├── android │ │ └── content │ │ │ └── IContentProvider.java │ └── com │ │ ├── android │ │ └── common │ │ │ ├── ActivityThreadCompat.java │ │ │ ├── ContentProviderCompat.java │ │ │ ├── ContextCompat.java │ │ │ ├── SystemPropertiesCompat.java │ │ │ └── UserHandleCompat.java │ │ └── reginald │ │ └── pluginm │ │ ├── PluginConfigs.java │ │ ├── PluginInfo.java │ │ ├── PluginM.java │ │ ├── PluginNotFoundException.java │ │ ├── comm │ │ ├── PluginCommClient.java │ │ ├── PluginCommService.java │ │ ├── PluginLocalManager.java │ │ └── invoker │ │ │ ├── HostInvokerManager.java │ │ │ ├── InvokeCallbackWrapper.java │ │ │ └── InvokeResult.java │ │ ├── core │ │ ├── HostContext.java │ │ ├── HostInstrumentation.java │ │ ├── PluginClientService.java │ │ ├── PluginContext.java │ │ ├── PluginDexClassLoader.java │ │ ├── PluginManager.java │ │ ├── PluginManagerService.java │ │ ├── PluginManagerServiceProvider.java │ │ ├── PluginPackageManager.java │ │ ├── PluginProcess.java │ │ └── ResourcesManager.java │ │ ├── hook │ │ ├── IActivityManagerServiceHook.java │ │ ├── ServiceHook.java │ │ └── SystemServiceHook.java │ │ ├── parser │ │ ├── ApkParser.java │ │ ├── ComponentNameComparator.java │ │ ├── IntentMatcher.java │ │ ├── PackageParser.java │ │ ├── PackageParserApi15.java │ │ ├── PackageParserApi16.java │ │ ├── PackageParserApi20.java │ │ ├── PackageParserApi21.java │ │ ├── PackageParserApi22.java │ │ ├── PackageParserApi22Preview1.java │ │ ├── PackageParserApi28.java │ │ └── PluginPackageParser.java │ │ ├── reflect │ │ ├── FieldUtils.java │ │ ├── MemberUtils.java │ │ ├── MethodUtils.java │ │ ├── Utils.java │ │ └── Validate.java │ │ ├── stub │ │ ├── PluginContentResolver.java │ │ ├── PluginServiceConnection.java │ │ ├── PluginStubLocalActivityManager.java │ │ ├── PluginStubMainProvider.java │ │ ├── PluginStubMainService.java │ │ ├── StubManager.java │ │ └── Stubs.java │ │ └── utils │ │ ├── BinderParcelable.java │ │ ├── CommonUtils.java │ │ ├── ConfigUtils.java │ │ ├── Logger.java │ │ ├── PackageUtils.java │ │ ├── ProcessHelper.java │ │ └── ThreadUtils.java │ └── res │ └── values │ └── styles.xml ├── README.md ├── _config.yml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── install-test ├── pluginm-test.keystore ├── pluginsharelib ├── .gitignore ├── build.gradle ├── consumer-proguard-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── reginald │ │ └── pluginm │ │ └── demo │ │ └── pluginsharelib │ │ └── ExampleInstrumentedTest.java │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── com │ │ └── reginald │ │ └── pluginm │ │ └── demo │ │ └── pluginsharelib │ │ ├── ITestPluginBinder.aidl │ │ ├── ITestServiceBinder.aidl │ │ └── PluginItem.aidl │ └── java │ └── com │ └── reginald │ └── pluginm │ └── demo │ └── pluginsharelib │ └── PluginItem.java ├── settings.gradle ├── testhost ├── .gitignore ├── build.gradle ├── libs │ └── nineoldandroids.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── testhost │ │ ├── DemoActivity.java │ │ ├── HostApplication.java │ │ ├── HostContentProvider.java │ │ ├── HostService.java │ │ ├── HostTestActivity.java │ │ ├── MyHostInvoker.java │ │ └── Prefs.java │ ├── jniLibs │ └── armeabi │ │ └── libtestjni.so │ └── res │ ├── layout │ ├── activity_host.xml │ ├── apk_list_item_layout.xml │ ├── config_layout.xml │ ├── demo_layout.xml │ └── plugin_list_item_layout.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── testplugin ├── .gitignore ├── build.gradle ├── libs │ └── nineoldandroids.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── testplugin │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── com │ │ └── example │ │ └── testplugin │ │ └── ITestBinder.aidl │ ├── java │ └── com │ │ └── example │ │ └── testplugin │ │ ├── CustomClassA.java │ │ ├── JniUtils.java │ │ ├── MyPluginInvoker.java │ │ ├── PluginActivityA.java │ │ ├── PluginActivityB.java │ │ ├── PluginApplication.java │ │ ├── PluginBackContentProvider.java │ │ ├── PluginContentProvider.java │ │ ├── PluginMainActivity.java │ │ ├── PluginObject.java │ │ ├── PluginService.java │ │ ├── PluginServiceA.java │ │ ├── PluginStaticBroadcastReceiver.java │ │ ├── PluginUtils.java │ │ └── TestUtils.java │ ├── jni │ ├── Android.mk │ ├── Application.mk │ ├── com_example_testplugin_JniUtils.h │ └── testjni.cpp │ ├── jniLibs │ └── armeabi │ │ └── liblocSDK6a.so │ ├── libs │ ├── arm64-v8a │ │ └── libtestjni.so │ ├── armeabi-v7a │ │ └── libtestjni.so │ ├── armeabi │ │ └── libtestjni.so │ ├── mips │ │ └── libtestjni.so │ ├── mips64 │ │ └── libtestjni.so │ ├── x86 │ │ └── libtestjni.so │ └── x86_64 │ │ └── libtestjni.so │ ├── obj │ └── local │ │ ├── arm64-v8a │ │ ├── libstdc++.a │ │ ├── libtestjni.so │ │ └── objs │ │ │ └── testjni │ │ │ ├── testjni.o │ │ │ └── testjni.o.d │ │ ├── armeabi-v7a │ │ ├── libstdc++.a │ │ ├── libtestjni.so │ │ └── objs │ │ │ └── testjni │ │ │ ├── testjni.o │ │ │ └── testjni.o.d │ │ ├── armeabi │ │ ├── libstdc++.a │ │ ├── libtestjni.so │ │ └── objs │ │ │ └── testjni │ │ │ ├── testjni.o │ │ │ └── testjni.o.d │ │ ├── mips │ │ ├── libstdc++.a │ │ ├── libtestjni.so │ │ └── objs │ │ │ └── testjni │ │ │ ├── testjni.o │ │ │ └── testjni.o.d │ │ ├── mips64 │ │ ├── libstdc++.a │ │ ├── libtestjni.so │ │ └── objs │ │ │ └── testjni │ │ │ ├── testjni.o │ │ │ └── testjni.o.d │ │ ├── x86 │ │ ├── libstdc++.a │ │ ├── libtestjni.so │ │ └── objs │ │ │ └── testjni │ │ │ ├── testjni.o │ │ │ └── testjni.o.d │ │ └── x86_64 │ │ ├── libstdc++.a │ │ ├── libtestjni.so │ │ └── objs │ │ └── testjni │ │ ├── testjni.o │ │ └── testjni.o.d │ └── res │ ├── drawable │ └── image.jpg │ ├── layout │ ├── activity_plugin_activity_a.xml │ ├── activity_plugin_activity_b.xml │ └── activity_plugin_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── testplugin2 ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── reginald │ └── pluginm │ └── demo │ └── plugintest2 │ └── DemoActivity.java └── res ├── layout ├── activity_demo.xml └── content_demo.xml ├── menu └── menu_demo.xml ├── mipmap-hdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-mdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xhdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xxhdpi ├── ic_launcher.png └── ic_launcher_round.png ├── mipmap-xxxhdpi ├── ic_launcher.png └── ic_launcher_round.png └── values ├── colors.xml ├── dimens.xml ├── strings.xml └── styles.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea/ 5 | .DS_Store 6 | /build 7 | /captures 8 | .idea 9 | apks/ 10 | -------------------------------------------------------------------------------- /PluginApi/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /PluginApi/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | minSdkVersion rootProject.ext.minSdkVersion 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled true 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | } 25 | -------------------------------------------------------------------------------- /PluginApi/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # pluginm api start 20 | -keep public class com.reginald.pluginm.pluginapi.** { 21 | public ; 22 | ; 23 | } 24 | # pluginm api end 25 | -------------------------------------------------------------------------------- /PluginApi/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PluginApi/src/main/java/com/reginald/pluginm/pluginapi/IInvokeCallback.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.pluginapi; 2 | 3 | /** 4 | * Created by lxy on 17-9-18. 5 | */ 6 | 7 | public interface IInvokeCallback { 8 | /** 9 | * 函数回调 10 | * @param params 回调参数 11 | * @return 回调结果 {@link com.reginald.pluginm.pluginapi.IInvokeResult} 12 | */ 13 | IInvokeResult onCallback(String params); 14 | } 15 | -------------------------------------------------------------------------------- /PluginApi/src/main/java/com/reginald/pluginm/pluginapi/IInvokeResult.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.pluginapi; 2 | 3 | /** 4 | * Created by lxy on 17-9-18. 5 | */ 6 | 7 | public interface IInvokeResult { 8 | /** 9 | * 函数调用结果成功 10 | */ 11 | int RESULT_OK = 1; 12 | /** 13 | * 函数调用结果失败,未找到目标IInvoker 14 | */ 15 | int RESULT_NOT_FOUND = -1; 16 | /** 17 | * 函数调用结果失败,远端服务进程死亡 18 | */ 19 | int RESULT_REMOTE_ERROR = -2; 20 | /** 21 | * 函数调用结果失败,非法或不支持的参数 22 | */ 23 | int RESULT_INVOKE_INVALID = -10; 24 | /** 25 | * 函数调用结果失败,内部错误 26 | */ 27 | int RESULT_INVOKE_ERROR = -11; 28 | 29 | /** 30 | * 获取调用返回码 31 | * @return 返回码 32 | */ 33 | int getResultCode(); 34 | 35 | /** 36 | * 获取调用结果,当{@link IInvokeResult#getResultCode()} 为 {@link IInvokeResult#RESULT_OK} 时才有意义。 37 | * @return 调用结果 38 | */ 39 | String getResult(); 40 | 41 | IInvokeResult INVOKERESULT_VOID_OK = new IInvokeResult() { 42 | @Override 43 | public int getResultCode() { 44 | return RESULT_OK; 45 | } 46 | 47 | @Override 48 | public String getResult() { 49 | return null; 50 | } 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /PluginApi/src/main/java/com/reginald/pluginm/pluginapi/IInvoker.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.pluginapi; 2 | 3 | import android.content.Context; 4 | import android.os.IBinder; 5 | 6 | /** 7 | * Created by lxy on 17-9-18. 8 | */ 9 | 10 | public interface IInvoker { 11 | /** 12 | * 提供此IInvoker对外提供的Binder服务 13 | * @param context 14 | * @return Binder服务 15 | */ 16 | IBinder onServiceCreate(Context context); 17 | 18 | /** 19 | * 处理此IInvoker上的函数调用 20 | * @param context 21 | * @param methodName 函数名称 22 | * @param params 函数参数(建议使用json等结构化数据格式) 23 | * @param callback 回调 {@link IInvokeCallback} 24 | * @return 结果 {@link IInvokeResult} 不要返回null。 25 | */ 26 | IInvokeResult onInvoke(Context context, String methodName, String params, IInvokeCallback callback); 27 | } 28 | -------------------------------------------------------------------------------- /PluginApi/src/main/java/com/reginald/pluginm/pluginapi/IPluginLocalManager.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.pluginapi; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.os.IBinder; 6 | 7 | /** 8 | * Created by lxy on 16-10-27. 9 | */ 10 | public interface IPluginLocalManager { 11 | Context getHostContext(); 12 | String getPluginPackageName(Context context); 13 | PackageInfo getPluginPackageInfo(String packageName, int flags); 14 | 15 | IInvokeResult invoke(String packageName, String serviceName, String methodName, String params, IInvokeCallback callback); 16 | IBinder fetchService(String packageName, String serviceName); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /PluginApi/src/main/java/com/reginald/pluginm/pluginapi/PluginHelper.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.pluginapi; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.IBinder; 7 | import android.text.TextUtils; 8 | 9 | /** 10 | * Created by lxy on 16-10-27. 11 | */ 12 | public class PluginHelper { 13 | private static final String TAG = "PluginHelper"; 14 | private static IPluginLocalManager sPluginLocalManager; 15 | private static String sHostPackageName; 16 | 17 | private static void init(Object iPluginLocalManager) { 18 | sPluginLocalManager = (IPluginLocalManager)iPluginLocalManager; 19 | } 20 | 21 | private PluginHelper() { 22 | } 23 | 24 | /** 25 | * 获取插件包名 26 | * @param context 27 | * @return 插件包名 28 | */ 29 | public static String getPluginPackageName(Context context) { 30 | if (sPluginLocalManager != null) { 31 | return sPluginLocalManager.getPluginPackageName(context); 32 | } 33 | 34 | //default 35 | return context.getPackageName(); 36 | } 37 | 38 | /** 39 | * 获取宿主包名 40 | * @param context 41 | * @return 宿主包名 42 | */ 43 | public static String getHostPackageName(Context context) { 44 | if (!TextUtils.isEmpty(sHostPackageName)) { 45 | return sHostPackageName; 46 | } 47 | 48 | sHostPackageName = getHostContext(context).getPackageName(); 49 | 50 | return sHostPackageName; 51 | } 52 | 53 | /** 54 | * 获取宿主Context 55 | * @param context 56 | * @return 宿主Context 57 | */ 58 | public static Context getHostContext(Context context) { 59 | if (sPluginLocalManager != null) { 60 | return sPluginLocalManager.getHostContext(); 61 | } 62 | 63 | return context; 64 | } 65 | 66 | /** 67 | * 获取插件package信息 68 | * @param context 69 | * @param packageName 插件包名 70 | * @param flags 71 | * @return 72 | */ 73 | public static PackageInfo getPluginPackageInfo(Context context, String packageName, int flags) { 74 | if (sPluginLocalManager != null) { 75 | return sPluginLocalManager.getPluginPackageInfo(packageName, flags); 76 | } 77 | 78 | try { 79 | return context.getPackageManager().getPackageInfo(packageName, flags); 80 | } catch (PackageManager.NameNotFoundException e) { 81 | // ignore 82 | } 83 | 84 | return null; 85 | } 86 | 87 | /** 88 | * IInvoker框架函数调用 89 | * @param packageName 插件或宿主包名 90 | * @param serviceName 服务名称 91 | * @param methodName 方法名称 92 | * @param params 方法参数 93 | * @param callback 方法回调 {@link IInvokeCallback} 94 | * @return 方法调用结果 {@link IInvokeResult} 95 | */ 96 | public static IInvokeResult invoke(String packageName, String serviceName, String methodName, String params, IInvokeCallback callback) { 97 | if (sPluginLocalManager != null) { 98 | return sPluginLocalManager.invoke(packageName, serviceName, methodName, params, callback); 99 | } 100 | 101 | return null; 102 | } 103 | 104 | /** 105 | * IInvoker框架Binder服务获取 106 | * @param packageName 插件或宿主包名 107 | * @param serviceName 服务名称 108 | * @return Binder服务 109 | */ 110 | public static IBinder fetchService(String packageName, String serviceName) { 111 | if (sPluginLocalManager != null) { 112 | return sPluginLocalManager.fetchService(packageName, serviceName); 113 | } 114 | 115 | return null; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /PluginManager/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /PluginManager/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | minSdkVersion rootProject.ext.minSdkVersion 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | buildConfigField("boolean", "DEBUG_LOG", "true") 14 | 15 | consumerProguardFiles 'consumer-proguard-rules.pro' 16 | } 17 | 18 | lintOptions { 19 | abortOnError false 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation 'com.android.support:support-annotations:25.3.1' 32 | api project(':PluginApi') 33 | } 34 | -------------------------------------------------------------------------------- /PluginManager/consumer-proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -dontpreverify 20 | -ignorewarnings 21 | -repackageclasses 'pluginm' 22 | 23 | #-keep class com.dianxinos.library.dxbase.**; 24 | -allowaccessmodification 25 | 26 | #-renamesourcefileattribute SourceFile 27 | -keepattributes SourceFile,LineNumberTable,Signature 28 | 29 | -dontwarn com.reginald.pluginm.** 30 | 31 | -keep class android.os.* { 32 | ; 33 | ; 34 | } 35 | -keep class android.util.* { 36 | ; 37 | ; 38 | } 39 | -keep class android.content.** { 40 | ; 41 | ; 42 | } 43 | 44 | ### Disable known warnings ### 45 | -dontwarn android.os.** 46 | -dontwarn android.util.** 47 | -dontwarn android.content.** 48 | -dontwarn com.android.internal.** 49 | -dontwarn android.app.** 50 | 51 | 52 | -keep class com.reginald.pluginm.core.HostContext { 53 | ; 54 | } 55 | 56 | -keep class com.reginald.pluginm.core.HostInstrumentation { 57 | ; 58 | } 59 | 60 | -keep class com.reginald.pluginm.stub.PluginContentResolver { 61 | ; 62 | } 63 | 64 | -keep class com.reginald.pluginm.core.PluginPackageManager { 65 | ; 66 | } 67 | 68 | -keep class com.reginald.pluginm.stub.Stubs$**{ 69 | *; 70 | } 71 | 72 | # pluginm api start 73 | -keep public class com.reginald.pluginm.pluginapi.** { 74 | public ; 75 | ; 76 | } 77 | # pluginm api end -------------------------------------------------------------------------------- /PluginManager/src/main/aidl/com/reginald/pluginm/IPluginClient.aidl: -------------------------------------------------------------------------------- 1 | // IPlugin.aidl 2 | package com.reginald.pluginm; 3 | 4 | import com.reginald.pluginm.PluginInfo; 5 | import com.reginald.pluginm.comm.invoker.InvokeResult; 6 | import com.reginald.pluginm.comm.invoker.InvokeCallback; 7 | 8 | interface IPluginClient { 9 | List getAllLoadedPlugins(); 10 | 11 | InvokeResult invokePlugin(String packageName, String serviceName, String methodName, String params, InvokeCallback callback); 12 | 13 | IBinder fetchPluginService(String packageName, String serviceName); 14 | } 15 | -------------------------------------------------------------------------------- /PluginManager/src/main/aidl/com/reginald/pluginm/IPluginManager.aidl: -------------------------------------------------------------------------------- 1 | // IPluginServiceStubBinder.aidl 2 | package com.reginald.pluginm; 3 | 4 | import android.content.Intent; 5 | import com.reginald.pluginm.PluginInfo; 6 | // Declare any non-default types here with import statements 7 | 8 | interface IPluginManager { 9 | PluginInfo install(in String pluginPackageName, boolean isInternal, boolean loadDex); 10 | PluginInfo uninstall(in String pluginPackageName); 11 | 12 | PluginInfo getInstalledPluginInfo(in String packageName); 13 | List getAllInstalledPlugins(); 14 | List getAllRunningPlugins(); 15 | boolean isPluginRunning(String pkgName); 16 | 17 | Intent getPluginActivityIntent(in Intent originIntent); 18 | Intent getPluginServiceIntent(in Intent originIntent); 19 | Bundle getPluginProviderUri(in String auth); 20 | String selectStubProcessName(in String processName, in String pkgName); 21 | 22 | ActivityInfo resolveActivityInfo(in Intent intent, int flags); 23 | ServiceInfo resolveServiceInfo(in Intent intent, int flags); 24 | ProviderInfo resolveProviderInfo(in String name); 25 | 26 | List queryIntentActivities(in Intent intent, int flags); 27 | List queryIntentServices(in Intent intent, int flags); 28 | List queryBroadcastReceivers(in Intent intent, int flags); 29 | List queryIntentContentProviders(in Intent intent, int flags); 30 | 31 | ActivityInfo getActivityInfo(in ComponentName componentName, int flags); 32 | ServiceInfo getServiceInfo(in ComponentName componentName, int flags); 33 | ActivityInfo getReceiverInfo(in ComponentName componentName, int flags); 34 | ProviderInfo getProviderInfo(in ComponentName componentName, int flags); 35 | PackageInfo getPackageInfo(in String packageName, int flags); 36 | 37 | void onPluginProcessAttached(in IBinder client); 38 | void onApplicationAttached(in ApplicationInfo targetInfo, String processName); 39 | void onActivityCreated(in ActivityInfo stubInfo,in ActivityInfo targetInfo); 40 | void onActivityDestory(in ActivityInfo stubInfo,in ActivityInfo targetInfo); 41 | void onServiceCreated(in ServiceInfo stubInfo,in ServiceInfo targetInfo); 42 | void onServiceDestory(in ServiceInfo stubInfo,in ServiceInfo targetInfo); 43 | void onProviderCreated(in ProviderInfo stubInfo,in ProviderInfo targetInfo); 44 | 45 | String getPluginProcessName(int pid); 46 | } -------------------------------------------------------------------------------- /PluginManager/src/main/aidl/com/reginald/pluginm/PluginInfo.aidl: -------------------------------------------------------------------------------- 1 | // PluginInfo.aidl 2 | package com.reginald.pluginm; 3 | 4 | // Declare any non-default types here with import statements 5 | 6 | parcelable PluginInfo; 7 | -------------------------------------------------------------------------------- /PluginManager/src/main/aidl/com/reginald/pluginm/comm/IPluginComm.aidl: -------------------------------------------------------------------------------- 1 | // IPluginComm.aidl 2 | package com.reginald.pluginm.comm; 3 | 4 | import com.reginald.pluginm.comm.invoker.InvokeResult; 5 | import com.reginald.pluginm.comm.invoker.InvokeCallback; 6 | 7 | interface IPluginComm { 8 | InvokeResult invoke(String packageName, String serviceName, String methodName, String params, InvokeCallback callback); 9 | IBinder fetchService(String packageName, String serviceName); 10 | } 11 | -------------------------------------------------------------------------------- /PluginManager/src/main/aidl/com/reginald/pluginm/comm/invoker/InvokeCallback.aidl: -------------------------------------------------------------------------------- 1 | // IInvokeCallback.aidl 2 | package com.reginald.pluginm.comm.invoker; 3 | 4 | import com.reginald.pluginm.comm.invoker.InvokeResult; 5 | // Declare any non-default types here with import statements 6 | 7 | interface InvokeCallback { 8 | InvokeResult onCallback(String params); 9 | } 10 | -------------------------------------------------------------------------------- /PluginManager/src/main/aidl/com/reginald/pluginm/comm/invoker/InvokeResult.aidl: -------------------------------------------------------------------------------- 1 | // IInvokeCallback.aidl 2 | package com.reginald.pluginm.comm.invoker; 3 | 4 | parcelable InvokeResult; 5 | -------------------------------------------------------------------------------- /PluginManager/src/main/aidl/com/reginald/pluginm/stub/IPluginServiceStubBinder.aidl: -------------------------------------------------------------------------------- 1 | // IPluginServiceStubBinder.aidl 2 | package com.reginald.pluginm.stub; 3 | 4 | // Declare any non-default types here with import statements 5 | 6 | interface IPluginServiceStubBinder { 7 | // boolean needConnect(); 8 | ComponentName getComponentName(); 9 | IBinder getBinder(); 10 | } 11 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/android/content/IContentProvider.java: -------------------------------------------------------------------------------- 1 | package android.content; 2 | 3 | import android.os.IInterface; 4 | 5 | public interface IContentProvider extends IInterface { 6 | } 7 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/android/common/ContentProviderCompat.java: -------------------------------------------------------------------------------- 1 | package com.android.common; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentProviderClient; 5 | import android.content.ContentResolver; 6 | import android.content.IContentProvider; 7 | import android.net.Uri; 8 | 9 | import com.reginald.pluginm.utils.Logger; 10 | 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | * Created by lxy on 16-10-26. 15 | */ 16 | public class ContentProviderCompat { 17 | private static final String TAG = "ContentProviderCompat"; 18 | 19 | private static Method sAcquireProviderMethod; 20 | private static Method sGetIContentProviderMethod; 21 | 22 | static { 23 | try { 24 | Class[] arrayOfClass = new Class[]{Uri.class}; 25 | sAcquireProviderMethod = ContentResolver.class.getMethod("acquireProvider", 26 | arrayOfClass); 27 | sGetIContentProviderMethod = ContentProvider.class.getMethod("getIContentProvider"); 28 | } catch (Exception e) { 29 | Logger.d(TAG, "can not find acquireProvider or getIContentProvider"); 30 | sAcquireProviderMethod = null; 31 | sGetIContentProviderMethod = null; 32 | } 33 | } 34 | 35 | public static boolean hasPorvider(ContentResolver cr, Uri uri) { 36 | if (sAcquireProviderMethod != null) { 37 | try { 38 | ContentProviderClient client = cr.acquireContentProviderClient(uri); 39 | if (client != null) { 40 | client.release(); 41 | return true; 42 | } 43 | return false; 44 | } catch (Exception localInvocationTargetException) { 45 | // ignore this, will to the final 46 | } 47 | } 48 | return false; 49 | } 50 | 51 | public static IContentProvider getIContentProvider(ContentProvider cp) { 52 | if (sGetIContentProviderMethod != null) { 53 | try { 54 | return (IContentProvider) sGetIContentProviderMethod.invoke(cp); 55 | } catch (Exception e) { 56 | Logger.e(TAG, "getIContentProvider() error!", e); 57 | } 58 | } 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/android/common/ContextCompat.java: -------------------------------------------------------------------------------- 1 | package com.android.common; 2 | 3 | import android.content.Context; 4 | import android.content.ContextWrapper; 5 | 6 | import com.reginald.pluginm.utils.Logger; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | @SuppressWarnings("NewApi") 11 | public class ContextCompat { 12 | private final static String TAG = "ContextCompat"; 13 | private final static boolean DEBUG = true; 14 | 15 | private static Class sClassContextImpl; 16 | private static Method sMethodSetOuterContext; 17 | 18 | static { 19 | ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 20 | try { 21 | sClassContextImpl = classLoader.loadClass("android.app.ContextImpl"); 22 | sMethodSetOuterContext = sClassContextImpl.getDeclaredMethod("setOuterContext", Context.class); 23 | sMethodSetOuterContext.setAccessible(true); 24 | } catch (Exception e) { 25 | sClassContextImpl = null; 26 | sMethodSetOuterContext = null; 27 | } 28 | } 29 | 30 | public static void setOuterContext(Context context, Context outerContext) { 31 | if (sMethodSetOuterContext != null) { 32 | while (!sClassContextImpl.isInstance(context)) { 33 | if (context instanceof ContextWrapper) { 34 | context = ((ContextWrapper) context).getBaseContext(); 35 | } else { 36 | Logger.e(TAG, "setOuterContext error context=" + context); 37 | return; 38 | } 39 | } 40 | 41 | try { 42 | sMethodSetOuterContext.invoke(context, outerContext); 43 | } catch (Exception e) { 44 | Logger.e(TAG, "setOuterContext fail context=" + context, e); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/android/common/SystemPropertiesCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.android.common; 24 | 25 | import com.reginald.pluginm.reflect.MethodUtils; 26 | 27 | import java.lang.reflect.InvocationTargetException; 28 | 29 | /** 30 | * Created by zhangyong on 15/5/1. 31 | */ 32 | public class SystemPropertiesCompat { 33 | 34 | private static Class sClass; 35 | 36 | private static Class getMyClass() throws ClassNotFoundException { 37 | if (sClass == null) { 38 | sClass = Class.forName("android.os.SystemProperties"); 39 | } 40 | return sClass; 41 | } 42 | 43 | private static String getInner(String key, String defaultValue) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { 44 | Class clazz = getMyClass(); 45 | return (String) MethodUtils.invokeStaticMethod(clazz, "get", key, defaultValue); 46 | } 47 | 48 | public static String get(String key, String defaultValue) { 49 | try { 50 | return getInner(key, defaultValue); 51 | } catch (Exception e) { 52 | return defaultValue; 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/android/common/UserHandleCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.android.common; 24 | 25 | import android.os.UserHandle; 26 | 27 | import com.reginald.pluginm.reflect.MethodUtils; 28 | 29 | /** 30 | * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/4/13. 31 | */ 32 | public class UserHandleCompat { 33 | 34 | // UserHandle.getCallingUserId() 35 | public static int getCallingUserId() { 36 | try { 37 | return (int) MethodUtils.invokeStaticMethod(UserHandle.class, "getCallingUserId"); 38 | } catch (Exception e) { 39 | } 40 | return 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/PluginInfo.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.pm.ApplicationInfo; 6 | import android.content.pm.PackageManager; 7 | import android.content.res.Resources; 8 | import android.os.Parcel; 9 | import android.os.Parcelable; 10 | 11 | import com.reginald.pluginm.parser.PluginPackageParser; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * Created by lxy on 16-6-21. 18 | */ 19 | public final class PluginInfo implements Parcelable { 20 | // install info 21 | public String packageName; 22 | public String apkPath; 23 | public String versionName; 24 | public int versionCode; 25 | public long fileSize; 26 | public long lastModified; 27 | public String dataDir; 28 | public String dexDir; 29 | public String nativeLibDir; 30 | public final Map> pluginInvokerClassMap = new HashMap<>(); 31 | public ApplicationInfo applicationInfo; 32 | 33 | // loaded info 34 | public PluginPackageParser pkgParser; 35 | public ClassLoader classLoader; 36 | public ClassLoader parentClassLoader; 37 | public Application application; 38 | public Context baseContext; 39 | public Resources resources; 40 | public PackageManager packageManager; 41 | 42 | public PluginInfo() { 43 | 44 | } 45 | 46 | protected PluginInfo(Parcel in) { 47 | packageName = in.readString(); 48 | apkPath = in.readString(); 49 | versionName = in.readString(); 50 | versionCode = in.readInt(); 51 | fileSize = in.readLong(); 52 | lastModified = in.readLong(); 53 | dataDir = in.readString(); 54 | dexDir = in.readString(); 55 | nativeLibDir = in.readString(); 56 | in.readMap(pluginInvokerClassMap, PluginInfo.class.getClassLoader()); 57 | 58 | applicationInfo = in.readParcelable(ApplicationInfo.class.getClassLoader()); 59 | } 60 | 61 | @Override 62 | public void writeToParcel(Parcel dest, int flags) { 63 | dest.writeString(packageName); 64 | dest.writeString(apkPath); 65 | dest.writeString(versionName); 66 | dest.writeInt(versionCode); 67 | dest.writeLong(fileSize); 68 | dest.writeLong(lastModified); 69 | dest.writeString(dataDir); 70 | dest.writeString(dexDir); 71 | dest.writeString(nativeLibDir); 72 | dest.writeMap(pluginInvokerClassMap); 73 | dest.writeParcelable(applicationInfo, flags); 74 | } 75 | 76 | @Override 77 | public int describeContents() { 78 | return 0; 79 | } 80 | 81 | public static final Creator CREATOR = new Creator() { 82 | @Override 83 | public PluginInfo createFromParcel(Parcel in) { 84 | return new PluginInfo(in); 85 | } 86 | 87 | @Override 88 | public PluginInfo[] newArray(int size) { 89 | return new PluginInfo[size]; 90 | } 91 | }; 92 | 93 | public String toString() { 94 | return String.format("PluginInfo[ packageName = %s, apkPath = %s, versionName = %s, versionCode = %d, fileSize = %d, " + 95 | "lastModified = %d, dataDir = %s, dexDir = %s, nativeLibDir = %s, pluginInvokerClassMap = %s]", 96 | packageName, apkPath, versionName, versionCode, fileSize, lastModified, 97 | dataDir, dexDir, nativeLibDir, pluginInvokerClassMap); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/PluginM.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm; 2 | 3 | import java.util.List; 4 | 5 | import com.reginald.pluginm.comm.PluginCommClient; 6 | import com.reginald.pluginm.comm.invoker.InvokeCallback; 7 | import com.reginald.pluginm.comm.invoker.InvokeCallbackWrapper; 8 | import com.reginald.pluginm.comm.invoker.InvokeResult; 9 | import com.reginald.pluginm.core.PluginManager; 10 | import com.reginald.pluginm.pluginapi.IInvokeCallback; 11 | import com.reginald.pluginm.pluginapi.IInvokeResult; 12 | import com.reginald.pluginm.stub.PluginContentResolver; 13 | 14 | import android.app.Activity; 15 | import android.app.Application; 16 | import android.content.ComponentName; 17 | import android.content.ContentResolver; 18 | import android.content.Context; 19 | import android.content.Intent; 20 | import android.content.ServiceConnection; 21 | import android.content.pm.PackageManager; 22 | import android.os.Bundle; 23 | import android.os.IBinder; 24 | 25 | /** 26 | * Created by lxy on 17-8-23. 27 | */ 28 | 29 | public class PluginM { 30 | private static Context sContext; 31 | private static PluginConfigs sConfigs; 32 | 33 | public static void onAttachBaseContext(Application app, PluginConfigs configs) { 34 | if (app == null || configs == null) { 35 | throw new IllegalStateException("app and configs MUST be provided!"); 36 | } 37 | sContext = app; 38 | sConfigs = configs; 39 | PluginManager.onAttachBaseContext(app); 40 | } 41 | 42 | public static PluginConfigs getConfigs() { 43 | return new PluginConfigs(sConfigs); 44 | } 45 | 46 | public static PluginInfo install(String apkPath) { 47 | return install(apkPath, true); 48 | } 49 | 50 | public static PluginInfo install(String apkPath, boolean loadDex) { 51 | return PluginManager.getInstance().installPlugin(apkPath, loadDex); 52 | } 53 | 54 | public static PluginInfo uninstall(String packageName) { 55 | return PluginManager.getInstance().uninstallPlugin(packageName); 56 | } 57 | 58 | public static PluginInfo getInstalledPlugin(String packageName) { 59 | return PluginManager.getInstance().getInstalledPluginInfo(packageName); 60 | } 61 | 62 | public static List getAllInstalledPlugins() { 63 | return PluginManager.getInstance().getAllInstalledPlugins(); 64 | } 65 | 66 | public static List getAllRunningPlugins() { 67 | return PluginManager.getInstance().getAllRunningPlugins(); 68 | } 69 | 70 | public static boolean isPluginRunning(String pkgName) { 71 | return PluginManager.getInstance().isPluginRunning(pkgName); 72 | } 73 | 74 | public static PackageManager getPluginPackageManager(Context context) { 75 | return PluginManager.getInstance().getPluginPackageManager(); 76 | } 77 | 78 | public static void startActivity(Context context, Intent intent) { 79 | PluginManager.getInstance().startActivity(context, intent); 80 | } 81 | 82 | public static void startActivity(Context context, Intent intent, Bundle options) { 83 | PluginManager.getInstance().startActivity(context, intent, options); 84 | } 85 | 86 | public static void startActivityForResult(Activity activity, Intent intent, int requestCode) { 87 | PluginManager.getInstance().startActivityForResult(activity, intent, requestCode); 88 | } 89 | 90 | public static void startActivityForResult(Activity activity, Intent intent, int requestCode, Bundle options) { 91 | PluginManager.getInstance().startActivityForResult(activity, intent, requestCode, options); 92 | } 93 | 94 | public static ComponentName startService(Context context, Intent intent) { 95 | return PluginManager.getInstance().startService(context, intent); 96 | } 97 | 98 | public static boolean stopService(Context context, Intent intent) { 99 | return PluginManager.getInstance().stopService(context, intent); 100 | } 101 | 102 | public static boolean bindService(Context context, Intent intent, ServiceConnection conn, 103 | int flags) { 104 | return PluginManager.getInstance().bindService(context, intent, conn, flags); 105 | } 106 | 107 | public static void unbindService(Context context, ServiceConnection conn) { 108 | PluginManager.getInstance().unbindService(context, conn); 109 | } 110 | 111 | public static ContentResolver getContentResolver(Context context) { 112 | return new PluginContentResolver(context, context.getContentResolver()); 113 | } 114 | 115 | public static IInvokeResult invoke(String packageName, String serviceName, String methodName, String params, IInvokeCallback callback) { 116 | InvokeCallback invokeCallback = InvokeCallbackWrapper.build(callback); 117 | 118 | final InvokeResult invokeResult = PluginCommClient.getInstance(sContext).invoke(packageName, serviceName, methodName, params, invokeCallback); 119 | 120 | return InvokeResult.newIInvokerResult(invokeResult); 121 | } 122 | 123 | public static IBinder fetchService(String packageName, String serviceName) { 124 | return PluginCommClient.getInstance(sContext).fetchService(packageName, serviceName); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/PluginNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm; 2 | 3 | /** 4 | * Created by lxy on 17-9-30. 5 | */ 6 | 7 | public class PluginNotFoundException extends RuntimeException { 8 | public PluginNotFoundException() { 9 | } 10 | 11 | public PluginNotFoundException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/comm/PluginCommClient.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.comm; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.os.IBinder; 7 | import android.os.RemoteException; 8 | 9 | import com.reginald.pluginm.comm.invoker.InvokeCallback; 10 | import com.reginald.pluginm.comm.invoker.InvokeResult; 11 | import com.reginald.pluginm.core.PluginManager; 12 | import com.reginald.pluginm.core.PluginManagerServiceProvider; 13 | import com.reginald.pluginm.pluginapi.IInvokeResult; 14 | import com.reginald.pluginm.utils.BinderParcelable; 15 | import com.reginald.pluginm.utils.Logger; 16 | 17 | /** 18 | * Created by lxy on 17-9-20. 19 | */ 20 | 21 | public class PluginCommClient { 22 | private static final String TAG = "PluginCommClient"; 23 | private static volatile PluginCommClient sInstance; 24 | 25 | private Context mContext; 26 | private volatile IPluginComm mService; 27 | 28 | public static synchronized PluginCommClient getInstance(Context hostContext) { 29 | if (sInstance == null) { 30 | sInstance = new PluginCommClient(hostContext); 31 | } 32 | 33 | return sInstance; 34 | } 35 | 36 | private PluginCommClient(Context hostContext) { 37 | Context appContext = hostContext.getApplicationContext(); 38 | mContext = appContext != null ? appContext : hostContext; 39 | initCommService(); 40 | } 41 | 42 | private void initCommService() { 43 | try { 44 | final ContentResolver contentResolver = mContext.getContentResolver(); 45 | final Bundle bundle = contentResolver.call(PluginManagerServiceProvider.getUri(mContext), 46 | PluginManagerServiceProvider.METHOD_GET_COMM_SERVICE, null, null); 47 | if (bundle != null) { 48 | bundle.setClassLoader(PluginManager.class.getClassLoader()); 49 | BinderParcelable bp = bundle.getParcelable(PluginManagerServiceProvider.KEY_SERVICE); 50 | if (bp != null) { 51 | IBinder iBinder = bp.iBinder; 52 | if (iBinder != null) { 53 | iBinder.linkToDeath(new IBinder.DeathRecipient() { 54 | @Override 55 | public void binderDied() { 56 | initCommService(); 57 | } 58 | }, 0); 59 | } 60 | mService = IPluginComm.Stub.asInterface(iBinder); 61 | Logger.d(TAG, "initCommService() success! mService = " + mService); 62 | } 63 | } 64 | } catch (Throwable e) { 65 | Logger.e(TAG, "initCommService() error!", e); 66 | } 67 | } 68 | 69 | public InvokeResult invoke(String packageName, String serviceName, 70 | String methodName, String params, InvokeCallback callback) { 71 | if (mService != null) { 72 | try { 73 | return mService.invoke(packageName, serviceName, methodName, params, callback); 74 | } catch (RemoteException e) { 75 | Logger.e(TAG, "invokePlugin() error!", e); 76 | } 77 | } 78 | return InvokeResult.buildErrorResult(IInvokeResult.RESULT_REMOTE_ERROR); 79 | } 80 | 81 | public IBinder fetchService(String packageName, String serviceName) { 82 | if (mService != null) { 83 | try { 84 | return mService.fetchService(packageName, serviceName); 85 | } catch (RemoteException e) { 86 | Logger.e(TAG, "fetchService() error!", e); 87 | } 88 | } 89 | return null; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/comm/PluginCommService.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.comm; 2 | 3 | import android.content.Context; 4 | import android.os.IBinder; 5 | import android.os.RemoteException; 6 | import android.text.TextUtils; 7 | 8 | import com.reginald.pluginm.IPluginClient; 9 | import com.reginald.pluginm.PluginInfo; 10 | import com.reginald.pluginm.comm.invoker.HostInvokerManager; 11 | import com.reginald.pluginm.comm.invoker.InvokeCallback; 12 | import com.reginald.pluginm.comm.invoker.InvokeResult; 13 | import com.reginald.pluginm.core.PluginManager; 14 | import com.reginald.pluginm.core.PluginManagerService; 15 | import com.reginald.pluginm.pluginapi.IInvokeResult; 16 | import com.reginald.pluginm.stub.StubManager; 17 | import com.reginald.pluginm.utils.ConfigUtils; 18 | import com.reginald.pluginm.utils.Logger; 19 | 20 | import java.util.Map; 21 | 22 | /** 23 | * Created by lxy on 17-8-22. 24 | */ 25 | 26 | public class PluginCommService extends IPluginComm.Stub { 27 | private static final String TAG = "PluginCommService"; 28 | private static volatile PluginCommService sInstance; 29 | 30 | private Context mContext; 31 | private PluginManager mPluginManager; 32 | private HostInvokerManager mHostInvokerManager; 33 | 34 | public static synchronized PluginCommService getInstance(Context hostContext) { 35 | if (sInstance == null) { 36 | sInstance = new PluginCommService(hostContext); 37 | } 38 | 39 | return sInstance; 40 | } 41 | 42 | private PluginCommService(Context hostContext) { 43 | Context appContext = hostContext.getApplicationContext(); 44 | mContext = appContext != null ? appContext : hostContext; 45 | mPluginManager = PluginManager.getInstance(); 46 | mHostInvokerManager = HostInvokerManager.getInstance(mContext); 47 | } 48 | 49 | @Override 50 | public InvokeResult invoke(String packageName, String serviceName, String methodName, String params, InvokeCallback callback) throws RemoteException { 51 | if (mContext.getPackageName().equals(packageName)) { 52 | return invokeHost(serviceName, methodName, params, callback); 53 | } else { 54 | return invokePlugin(packageName, serviceName, methodName, params, callback); 55 | } 56 | } 57 | 58 | @Override 59 | public IBinder fetchService(String packageName, String serviceName) throws RemoteException { 60 | if (mContext.getPackageName().equals(packageName)) { 61 | return mHostInvokerManager.fetchHostServiceBinder(serviceName); 62 | } else { 63 | return fetchPluginServiceBinder(packageName, serviceName); 64 | } 65 | } 66 | 67 | 68 | private InvokeResult invokeHost(String serviceName, String methodName, String params, InvokeCallback callback) throws RemoteException { 69 | return mHostInvokerManager.invokeHost(serviceName, methodName, params, callback); 70 | } 71 | 72 | private InvokeResult invokePlugin(String packageName, String serviceName, String methodName, String params, InvokeCallback callback) throws RemoteException { 73 | IPluginClient pluginClient = fetchPluginClient(packageName, serviceName); 74 | if (pluginClient != null) { 75 | return pluginClient.invokePlugin(packageName, serviceName, methodName, params, callback); 76 | } 77 | 78 | return InvokeResult.buildErrorResult(IInvokeResult.RESULT_NOT_FOUND); 79 | } 80 | 81 | 82 | // TODO cache binders 83 | private IBinder fetchPluginServiceBinder(String packageName, String serviceName) throws RemoteException { 84 | IPluginClient pluginClient = fetchPluginClient(packageName, serviceName); 85 | if (pluginClient != null) { 86 | return pluginClient.fetchPluginService(packageName, serviceName); 87 | } 88 | 89 | return null; 90 | } 91 | 92 | private IPluginClient fetchPluginClient(String packageName, String serviceName) { 93 | PluginInfo pluginInfo = mPluginManager.getInstalledPluginInfo(packageName); 94 | if (pluginInfo == null) { 95 | Logger.e(TAG, String.format("fetchPluginClient() plugin %s not installed!", packageName)); 96 | return null; 97 | } 98 | 99 | Map serviceConfig = pluginInfo.pluginInvokerClassMap.get(serviceName); 100 | 101 | if (serviceConfig == null) { 102 | Logger.e(TAG, String.format("fetchPluginClient() service config for %s@%s not found!", serviceName, packageName)); 103 | return null; 104 | } 105 | 106 | String targetProcessName = serviceConfig.get(ConfigUtils.KEY_INVOKER_PROCESS); 107 | StubManager.ProcessInfo processInfo = StubManager.getInstance(mContext).selectStubProcess(targetProcessName, packageName); 108 | Logger.d(TAG, String.format("fetchPluginClient() packageName = %s, serviceName = %s -> processInfo = %s", 109 | packageName, serviceName, processInfo)); 110 | 111 | if (processInfo != null) { 112 | return PluginManagerService.getInstance(mContext).fetchPluginClient(processInfo.processName, true); 113 | } 114 | 115 | return null; 116 | } 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/comm/PluginLocalManager.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.comm; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.os.IBinder; 6 | 7 | import com.reginald.pluginm.PluginInfo; 8 | import com.reginald.pluginm.comm.invoker.InvokeCallback; 9 | import com.reginald.pluginm.comm.invoker.InvokeCallbackWrapper; 10 | import com.reginald.pluginm.comm.invoker.InvokeResult; 11 | import com.reginald.pluginm.core.PluginManager; 12 | import com.reginald.pluginm.pluginapi.IInvokeCallback; 13 | import com.reginald.pluginm.pluginapi.IInvokeResult; 14 | import com.reginald.pluginm.pluginapi.IPluginLocalManager; 15 | 16 | /** 17 | * Created by lxy on 17-9-20. 18 | */ 19 | 20 | public class PluginLocalManager implements IPluginLocalManager { 21 | 22 | private static final String TAG = "PluginLocalManager"; 23 | private static volatile PluginLocalManager sInstance; 24 | 25 | private Context mContext; 26 | 27 | public static synchronized PluginLocalManager getInstance(Context hostContext) { 28 | if (sInstance == null) { 29 | sInstance = new PluginLocalManager(hostContext); 30 | } 31 | 32 | return sInstance; 33 | } 34 | 35 | private PluginLocalManager(Context context) { 36 | mContext = context.getApplicationContext(); 37 | } 38 | 39 | @Override 40 | public Context getHostContext() { 41 | return mContext; 42 | } 43 | 44 | @Override 45 | public String getPluginPackageName(Context context) { 46 | PluginInfo pluginInfo = getPluginInfo(context); 47 | if (pluginInfo != null) { 48 | return pluginInfo.packageName; 49 | } 50 | return null; 51 | } 52 | 53 | @Override 54 | public PackageInfo getPluginPackageInfo(String packageName, int flags) { 55 | return PluginManager.getInstance().getPackageInfo(packageName, flags); 56 | } 57 | 58 | @Override 59 | public IInvokeResult invoke(String packageName, String serviceName, String methodName, String params, IInvokeCallback callback) { 60 | InvokeCallback invokeCallback = InvokeCallbackWrapper.build(callback); 61 | 62 | final InvokeResult invokeResult = PluginCommClient.getInstance(mContext).invoke(packageName, serviceName, methodName, params, invokeCallback); 63 | 64 | return InvokeResult.newIInvokerResult(invokeResult); 65 | } 66 | 67 | @Override 68 | public IBinder fetchService(String packageName, String serviceName) { 69 | return PluginCommClient.getInstance(mContext).fetchService(packageName, serviceName); 70 | } 71 | 72 | private PluginInfo getPluginInfo(Context pluginContext) { 73 | if (pluginContext == null) { 74 | return null; 75 | } 76 | return PluginManager.getInstance().getPluginInfoByClassLoader(pluginContext.getClassLoader()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/comm/invoker/InvokeCallbackWrapper.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.comm.invoker; 2 | 3 | import android.os.RemoteException; 4 | 5 | import com.reginald.pluginm.pluginapi.IInvokeCallback; 6 | import com.reginald.pluginm.pluginapi.IInvokeResult; 7 | import com.reginald.pluginm.utils.Logger; 8 | 9 | /** 10 | * Created by lxy on 17-9-21. 11 | */ 12 | 13 | public abstract class InvokeCallbackWrapper extends InvokeCallback.Stub { 14 | private static final String TAG = "InvokeCallbackStub"; 15 | 16 | public static InvokeCallbackWrapper build(final IInvokeCallback iInvokeCallback) { 17 | if (iInvokeCallback == null) { 18 | return null; 19 | } 20 | 21 | return new InvokeCallbackWrapper() { 22 | @Override 23 | public InvokeResult onCallback(String params) { 24 | IInvokeResult iInvokeResult = iInvokeCallback.onCallback(params); 25 | return InvokeResult.build(iInvokeResult); 26 | } 27 | }; 28 | } 29 | 30 | public static IInvokeCallback build(final InvokeCallback callback) { 31 | return callback != null ? new IInvokeCallback() { 32 | @Override 33 | public IInvokeResult onCallback(String params) { 34 | final InvokeResult invokeResult; 35 | try { 36 | invokeResult = callback.onCallback(params); 37 | } catch (RemoteException e) { 38 | Logger.e(TAG, "build IInvokeCallback. remote died!"); 39 | return new IInvokeResult() { 40 | @Override 41 | public int getResultCode() { 42 | return IInvokeResult.RESULT_REMOTE_ERROR; 43 | } 44 | 45 | @Override 46 | public String getResult() { 47 | return null; 48 | } 49 | }; 50 | } 51 | 52 | return InvokeResult.newIInvokerResult(invokeResult); 53 | } 54 | } : null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/comm/invoker/InvokeResult.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.comm.invoker; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.reginald.pluginm.pluginapi.IInvokeResult; 7 | 8 | /** 9 | * Created by lxy on 17-9-19. 10 | */ 11 | 12 | public class InvokeResult implements Parcelable { 13 | private int mResultCode; 14 | private String mResult; 15 | 16 | public InvokeResult(int resultCode, String result) { 17 | mResultCode = resultCode; 18 | mResult = result; 19 | } 20 | 21 | private InvokeResult(IInvokeResult iInvokeResult) { 22 | mResultCode = iInvokeResult.getResultCode(); 23 | mResult = iInvokeResult.getResult(); 24 | } 25 | 26 | protected InvokeResult(Parcel in) { 27 | mResultCode = in.readInt(); 28 | mResult = in.readString(); 29 | } 30 | 31 | public int getResultCode() { 32 | return mResultCode; 33 | } 34 | 35 | public String getResult() { 36 | return mResult; 37 | } 38 | 39 | public static final Creator CREATOR = new Creator() { 40 | @Override 41 | public InvokeResult createFromParcel(Parcel in) { 42 | return new InvokeResult(in); 43 | } 44 | 45 | @Override 46 | public InvokeResult[] newArray(int size) { 47 | return new InvokeResult[size]; 48 | } 49 | }; 50 | 51 | @Override 52 | public int describeContents() { 53 | return 0; 54 | } 55 | 56 | @Override 57 | public void writeToParcel(Parcel dest, int flags) { 58 | dest.writeInt(mResultCode); 59 | dest.writeString(mResult); 60 | } 61 | 62 | public static InvokeResult build(IInvokeResult iInvokeResult) { 63 | if (iInvokeResult != null) { 64 | return new InvokeResult(iInvokeResult); 65 | } 66 | return null; 67 | } 68 | 69 | public static IInvokeResult newIInvokerResult(final InvokeResult invokeResult) { 70 | return invokeResult != null ? new IInvokeResult() { 71 | @Override 72 | public int getResultCode() { 73 | return invokeResult.getResultCode(); 74 | } 75 | 76 | @Override 77 | public String getResult() { 78 | return invokeResult.getResult(); 79 | } 80 | } : IInvokeResult.INVOKERESULT_VOID_OK; 81 | } 82 | 83 | // TODO make error result static final 84 | public static InvokeResult buildErrorResult(int errorCode) { 85 | return new InvokeResult(errorCode, null); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/core/PluginContext.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.core; 2 | 3 | import com.reginald.pluginm.PluginInfo; 4 | import com.reginald.pluginm.PluginNotFoundException; 5 | import com.reginald.pluginm.stub.PluginContentResolver; 6 | import com.reginald.pluginm.stub.StubManager; 7 | import com.reginald.pluginm.utils.Logger; 8 | 9 | import android.content.ComponentName; 10 | import android.content.ContentResolver; 11 | import android.content.Context; 12 | import android.content.Intent; 13 | import android.content.ServiceConnection; 14 | import android.content.pm.ApplicationInfo; 15 | import android.content.pm.PackageManager; 16 | import android.content.res.AssetManager; 17 | import android.content.res.Resources; 18 | import android.view.ContextThemeWrapper; 19 | 20 | /** 21 | * Created by lxy on 16-6-28. 22 | */ 23 | public class PluginContext extends ContextThemeWrapper { 24 | 25 | private static final String TAG = "PluginContext"; 26 | 27 | private Context mBaseContext; 28 | private PluginInfo mPluginInfo; 29 | private String mApkPath; 30 | private AssetManager mAssetManager; 31 | private PackageManager mPackageManager; 32 | private Resources mResources; 33 | private ClassLoader mClassLoader; 34 | private ContentResolver mContentResolver; 35 | private PluginManager mPluginManager; 36 | 37 | public PluginContext(PluginInfo pluginInfo, Context baseContext) { 38 | super(baseContext, android.R.style.Theme); 39 | Logger.d(TAG, "PluginActivityContext() pluginInfo = " + pluginInfo); 40 | mBaseContext = baseContext; 41 | mPluginInfo = pluginInfo; 42 | mApkPath = pluginInfo.apkPath; 43 | mPackageManager = pluginInfo.packageManager; 44 | mAssetManager = pluginInfo.resources.getAssets(); 45 | mResources = pluginInfo.resources; 46 | mClassLoader = pluginInfo.classLoader; 47 | mContentResolver = new PluginContentResolver(mBaseContext, super.getContentResolver()); 48 | mPluginManager = PluginManager.getInstance(); 49 | } 50 | 51 | public String getPackageResourcePath() { 52 | return mApkPath; 53 | } 54 | 55 | public String getPackageCodePath() { 56 | return mApkPath; 57 | } 58 | 59 | public String getPackageName() { 60 | return PluginManager.getPackageNameCompat(mPluginInfo.packageName, super.getPackageName()); 61 | } 62 | 63 | public PackageManager getPackageManager() { 64 | return mPackageManager; 65 | } 66 | 67 | public ClassLoader getClassLoader() { 68 | return mClassLoader != null ? mClassLoader : ClassLoader.getSystemClassLoader(); 69 | } 70 | 71 | public AssetManager getAssets() { 72 | return mAssetManager; 73 | } 74 | 75 | public Resources getResources() { 76 | return mResources; 77 | } 78 | 79 | @Override 80 | public Resources.Theme getTheme() { 81 | return super.getTheme(); 82 | } 83 | 84 | public Context getApplicationContext() { 85 | return mPluginInfo.application; 86 | } 87 | 88 | public ApplicationInfo getApplicationInfo() { 89 | return mPluginInfo.applicationInfo; 90 | } 91 | 92 | @Override 93 | public ComponentName startService(Intent intent) { 94 | if (!StubManager.isStubIntent(mBaseContext, intent)) { 95 | try { 96 | return mPluginManager.startService(this, intent); 97 | } catch (PluginNotFoundException e) { 98 | } 99 | } 100 | 101 | return super.startService(intent); 102 | } 103 | 104 | @Override 105 | public boolean stopService(Intent intent) { 106 | if (!StubManager.isStubIntent(mBaseContext, intent)) { 107 | try { 108 | return mPluginManager.stopService(this, intent); 109 | } catch (PluginNotFoundException e) { 110 | } 111 | } 112 | 113 | return super.stopService(intent); 114 | } 115 | 116 | @Override 117 | public boolean bindService(Intent intent, ServiceConnection conn, 118 | int flags) { 119 | if (!StubManager.isStubIntent(mBaseContext, intent)) { 120 | try { 121 | return mPluginManager.bindService(this, intent, conn, flags); 122 | } catch (PluginNotFoundException e) { 123 | } 124 | } 125 | 126 | return super.bindService(intent, conn, flags); 127 | } 128 | 129 | @Override 130 | public void unbindService(ServiceConnection conn) { 131 | try { 132 | mPluginManager.unbindService(this, conn); 133 | return; 134 | } catch (PluginNotFoundException e) { 135 | } 136 | 137 | super.unbindService(conn); 138 | } 139 | 140 | @Override 141 | public Object getSystemService(String name) { 142 | return PluginManager.getSystemServiceCompat(mBaseContext, this, name); 143 | } 144 | 145 | @Override 146 | public ContentResolver getContentResolver() { 147 | return mContentResolver; 148 | } 149 | 150 | PluginInfo getPluginInfo() { 151 | return mPluginInfo; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/core/PluginDexClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.core; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.reginald.pluginm.BuildConfig; 6 | import com.reginald.pluginm.PluginM; 7 | import com.reginald.pluginm.pluginapi.PluginHelper; 8 | import com.reginald.pluginm.utils.Logger; 9 | 10 | import dalvik.system.DexClassLoader; 11 | 12 | /** 13 | * Created by lxy on 16-6-24. 14 | */ 15 | public class PluginDexClassLoader extends DexClassLoader { 16 | 17 | private static final String TAG = "PluginDexClassLoader"; 18 | private static final boolean LOADER_DEBUG = BuildConfig.DEBUG_LOG && false; 19 | private static final String[] HOST_LOAD_LIST = new String[]{ 20 | PluginHelper.class.getPackage().getName() 21 | }; 22 | 23 | private final ClassLoader mHost; 24 | 25 | public PluginDexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent, ClassLoader extra) { 26 | super(dexPath, optimizedDirectory, libraryPath, parent); 27 | Logger.d(TAG, "PluginDexClassLoader() " + this + ", parent = " + parent); 28 | mHost = extra; 29 | } 30 | 31 | protected Class loadClass(String className, boolean resolve) throws ClassNotFoundException { 32 | if (LOADER_DEBUG) { 33 | Logger.d(TAG, "loadClass() classname = " + className + " , resolve = " + resolve); 34 | } 35 | 36 | Class hostClazz = tryLoadFromHostList(className, resolve); 37 | if (hostClazz != null) { 38 | if (LOADER_DEBUG) { 39 | Logger.d(TAG, "loadClass() from host list: classname = %s ok!", 40 | HOST_LOAD_LIST, className); 41 | } 42 | return hostClazz; 43 | } 44 | 45 | try { 46 | Class clazz = super.loadClass(className, resolve); 47 | if (LOADER_DEBUG) { 48 | Logger.d(TAG, "loadClass() plugin loader: classname = " + className + " ok!"); 49 | } 50 | return clazz; 51 | } catch (ClassNotFoundException pluginException) { 52 | if (LOADER_DEBUG) { 53 | Logger.e(TAG, "loadClass() plugin loader: classname = " + className + " fail!"); 54 | } 55 | 56 | if (canUseHostLoader(className)) { 57 | try { 58 | Class clazz = mHost.loadClass(className); 59 | if (LOADER_DEBUG) { 60 | Logger.d(TAG, "loadClass() host loader: classname = " + className + " ok!"); 61 | } 62 | return clazz; 63 | } catch (ClassNotFoundException hostException) { 64 | if (LOADER_DEBUG) { 65 | Logger.e(TAG, "loadClass() host loader: classname = " + className + " host load fail!"); 66 | } 67 | throw new ClassNotFoundException(String.format("plugin class {%s} is NOT found both in PLUGIN " 68 | + "CLASSLOADER {%s} and HOST CLASSLOADER {%s}", className, this, mHost)); 69 | } 70 | } else { 71 | throw new ClassNotFoundException(String.format("plugin class {%s} is NOT found in PLUGIN CLASSLOADER " 72 | + "{%s}", className, this)); 73 | } 74 | } 75 | } 76 | 77 | private Class tryLoadFromHostList(String className, boolean resolve) { 78 | for (String prefix : HOST_LOAD_LIST) { 79 | if (className.startsWith(prefix)) { 80 | try { 81 | Class clazz = mHost.loadClass(className); 82 | if (LOADER_DEBUG) { 83 | Logger.d(TAG, "tryLoadFromHostList() host loader: classname = " + className + " ok!"); 84 | } 85 | return clazz; 86 | } catch (ClassNotFoundException hostException) { 87 | if (LOADER_DEBUG) { 88 | Logger.e(TAG, "tryLoadFromHostList() host loader: classname = " + className + " host load fail!"); 89 | } 90 | } 91 | } 92 | } 93 | 94 | return null; 95 | } 96 | 97 | private boolean canUseHostLoader(String className) { 98 | boolean configUseHost = PluginM.getConfigs().isUseHostLoader(); 99 | return configUseHost || isPluginApiClass(className); 100 | } 101 | 102 | private boolean isPluginApiClass(String className) { 103 | if (!TextUtils.isEmpty(className)) { 104 | return className.startsWith(PluginHelper.class.getPackage().getName()); 105 | } 106 | return false; 107 | } 108 | 109 | public String toString() { 110 | return "@" + hashCode() + " " + super.toString(); 111 | } 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/core/PluginManagerServiceProvider.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.core; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.os.SystemClock; 10 | import android.support.annotation.NonNull; 11 | import android.support.annotation.Nullable; 12 | import android.text.TextUtils; 13 | 14 | import com.reginald.pluginm.comm.PluginCommService; 15 | import com.reginald.pluginm.utils.BinderParcelable; 16 | import com.reginald.pluginm.utils.Logger; 17 | 18 | /** 19 | * Created by lxy on 17-8-23. 20 | */ 21 | 22 | public class PluginManagerServiceProvider extends ContentProvider { 23 | public static final String METHOD_GET_CORE_SERVICE = "method.get_core_service"; 24 | public static final String METHOD_GET_COMM_SERVICE = "method.get_comm_service"; 25 | public static final String KEY_SERVICE = "key.service"; 26 | private static final String TAG = "PluginManagerServiceProvider"; 27 | private static final String AUTH_SUFFIX = ".pluginm.provider.core"; 28 | 29 | private static volatile Uri sUri; 30 | private static final Object sUriLock = new Object(); 31 | 32 | public static Uri getUri(Context hostContext) { 33 | if (sUri == null) { 34 | synchronized (sUriLock) { 35 | if (sUri == null) { 36 | sUri = Uri.parse("content://" + hostContext.getPackageName() + AUTH_SUFFIX); 37 | } 38 | } 39 | } 40 | 41 | return sUri; 42 | } 43 | 44 | @Override 45 | public boolean onCreate() { 46 | Logger.d(TAG, "onCreate()"); 47 | initCoreService(); 48 | return true; 49 | } 50 | 51 | private void initCoreService() { 52 | new Thread(new Runnable() { 53 | @Override 54 | public void run() { 55 | Logger.d(TAG, "initCoreService ..."); 56 | long startTime = SystemClock.elapsedRealtime(); 57 | PluginManagerService.getInstance(getContext()); 58 | Logger.d(TAG, String.format("initCoreService finished! cost %d ms", 59 | SystemClock.elapsedRealtime() - startTime)); 60 | } 61 | }).start(); 62 | } 63 | 64 | public 65 | @Nullable 66 | Bundle call(@NonNull String method, @Nullable String arg, 67 | @Nullable Bundle extras) { 68 | Logger.d(TAG, String.format("call method = %s, arg = %s, extras = %s", method, arg, extras)); 69 | 70 | if (!TextUtils.isEmpty(method)) { 71 | if (method.equals(METHOD_GET_CORE_SERVICE)) { 72 | // delayed init core service 73 | PluginManagerService coreService = PluginManagerService.getInstance(getContext()); 74 | BinderParcelable binderParcelable = new BinderParcelable(coreService); 75 | Bundle bundle = new Bundle(); 76 | bundle.putParcelable(KEY_SERVICE, binderParcelable); 77 | return bundle; 78 | } else if (method.equals(METHOD_GET_COMM_SERVICE)) { 79 | BinderParcelable binderParcelable = new BinderParcelable(PluginCommService.getInstance(getContext())); 80 | Bundle bundle = new Bundle(); 81 | bundle.putParcelable(KEY_SERVICE, binderParcelable); 82 | return bundle; 83 | } else { 84 | Logger.w(TAG, "call method " + method + " NOT supported!"); 85 | } 86 | } 87 | return null; 88 | } 89 | 90 | 91 | @Nullable 92 | @Override 93 | public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { 94 | return null; 95 | } 96 | 97 | @Nullable 98 | @Override 99 | public String getType(@NonNull Uri uri) { 100 | return null; 101 | } 102 | 103 | @Nullable 104 | @Override 105 | public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { 106 | return null; 107 | } 108 | 109 | @Override 110 | public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { 111 | return 0; 112 | } 113 | 114 | @Override 115 | public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { 116 | return 0; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/core/ResourcesManager.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.core; 2 | 3 | import java.util.List; 4 | 5 | import com.reginald.pluginm.PluginInfo; 6 | import com.reginald.pluginm.reflect.MethodUtils; 7 | import com.reginald.pluginm.utils.Logger; 8 | 9 | import android.content.Context; 10 | import android.content.res.AssetManager; 11 | import android.content.res.Configuration; 12 | import android.content.res.Resources; 13 | import android.util.DisplayMetrics; 14 | 15 | /** 16 | * Created by lxy on 16-6-2. 17 | */ 18 | public class ResourcesManager { 19 | 20 | private static final String TAG = "ResourcesManager"; 21 | 22 | public static Resources createResources(Context hostContext, AssetManager assetManager) { 23 | try { 24 | if (assetManager != null) { 25 | return new PluginResources(assetManager, hostContext); 26 | } 27 | } catch (Exception e) { 28 | Logger.e(TAG, "createResources() error!", e); 29 | } 30 | return null; 31 | } 32 | 33 | public static AssetManager getCombinedAssetManager(Context hostContext, List pluginInfos) { 34 | try { 35 | 36 | /* 5.0以上这种方式会有问题 37 | AssetManager assetManager = AssetManager.class.newInstance(); 38 | AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(assetManager, 39 | hostContext.getApplicationInfo().sourceDir); 40 | */ 41 | // TODO 在插件进程中,宿主资源与插件资源混合,如果没有对插件包进行资源id隔离,有产生资源冲突的风险。 42 | AssetManager assetManager = hostContext.getAssets(); 43 | for (PluginInfo pluginInfo : pluginInfos) { 44 | MethodUtils.invokeMethod(assetManager, "addAssetPath", pluginInfo.apkPath); 45 | } 46 | return assetManager; 47 | } catch (Exception e) { 48 | Logger.e(TAG, "getCombinedAssetManager() error!", e); 49 | } 50 | 51 | return null; 52 | } 53 | 54 | public static AssetManager createAssetManager(String apkPath) { 55 | try { 56 | AssetManager assetManager = AssetManager.class.newInstance(); 57 | MethodUtils.invokeMethod(assetManager, "addAssetPath", apkPath); 58 | return assetManager; 59 | } catch (Exception e) { 60 | Logger.e(TAG, "createAssetManager() error!", e); 61 | } 62 | 63 | return null; 64 | } 65 | 66 | private static class PluginResources extends Resources { 67 | private final Context mHostContext; 68 | private final AssetManager mAssetManager; 69 | 70 | public PluginResources(AssetManager assets, Context hostContext) { 71 | super(assets, hostContext.getResources().getDisplayMetrics(), 72 | hostContext.getResources().getConfiguration()); 73 | mHostContext = hostContext; 74 | mAssetManager = assets; 75 | } 76 | 77 | @Override 78 | public Configuration getConfiguration() { 79 | return mHostContext.getResources().getConfiguration(); 80 | } 81 | 82 | @Override 83 | public DisplayMetrics getDisplayMetrics() { 84 | return mHostContext.getResources().getDisplayMetrics(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/hook/ServiceHook.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.hook; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import com.reginald.pluginm.utils.Logger; 11 | 12 | import android.os.SystemClock; 13 | 14 | /** 15 | * Created by lxy on 17-8-16. 16 | */ 17 | 18 | public abstract class ServiceHook implements InvocationHandler { 19 | private static final String TAG = "ServiceHook"; 20 | private static final boolean HOOK_LOG = true; 21 | 22 | protected Object mBase; 23 | protected final Map mMethodHandlers = new HashMap(2); 24 | protected MethodHandler mAllMethodHandler; 25 | 26 | /** 27 | * 1. replace the host object(mBase) with the new one(Hook) 28 | * 2. init MethodHandlers 29 | * @return 30 | */ 31 | public abstract boolean install(); 32 | 33 | protected void addMethodHandler(MethodHandler methodHandler) { 34 | Logger.d(TAG, "addMethodHandler " + methodHandler); 35 | mMethodHandlers.put(methodHandler.getName(), methodHandler); 36 | } 37 | 38 | protected void setAllMethodHandler(MethodHandler methodHandler) { 39 | Logger.d(TAG, "setAllMethodHandler " + methodHandler); 40 | mAllMethodHandler = methodHandler; 41 | } 42 | 43 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 44 | if (HOOK_LOG) { 45 | Logger.d(TAG, String.format("invoke %s.%s(%s)", 46 | method.getDeclaringClass().getName(), method.getName(), 47 | args != null ? Arrays.asList(args) : "NULL")); 48 | } 49 | 50 | try { 51 | if (method != null) { 52 | MethodHandler methodHandler = mMethodHandlers.get(method.getName()); 53 | if (methodHandler != null) { 54 | return methodHandler.invoke(mBase, method, args); 55 | } 56 | 57 | if (mAllMethodHandler != null) { 58 | return mAllMethodHandler.invoke(mBase, method, args); 59 | } 60 | } 61 | return method.invoke(mBase, args); 62 | } catch (InvocationTargetException e) { 63 | throw e; 64 | } catch (IllegalArgumentException e) { 65 | throw e; 66 | } 67 | } 68 | 69 | 70 | public static class MethodHandler { 71 | public static final Object NONE_RETURN = new Object(); 72 | 73 | public Object invoke(Object receiver, Method method, Object[] args) throws Throwable { 74 | long startTime = SystemClock.elapsedRealtime(); 75 | 76 | try { 77 | Object invokeResult = onStartInvoke(receiver, method, args); 78 | if (invokeResult == NONE_RETURN) { 79 | invokeResult = method.invoke(receiver, args); 80 | } 81 | invokeResult = onEndInvoke(receiver, method, args, invokeResult); 82 | 83 | return invokeResult; 84 | } finally { 85 | long endTime = SystemClock.elapsedRealtime(); 86 | if (HOOK_LOG) { 87 | Logger.d(TAG, String.format("MethodHandler.invoke method %s.%s costs %d ms", 88 | method.getDeclaringClass().getName(), method.getName(), endTime - startTime)); 89 | } 90 | } 91 | 92 | } 93 | 94 | /** 95 | * 子类需要覆盖此方法提供hook的方法名 96 | * @return 97 | */ 98 | public String getName() { 99 | return getClass().getSimpleName(); 100 | } 101 | 102 | /** 103 | * 调用IActivityManager接口之前的操作 104 | * @param receiver 105 | * @param method 106 | * @param args 107 | * @return 是否需要实际调用IActivityManager的接口 108 | */ 109 | public Object onStartInvoke(Object receiver, Method method, Object[] args) { 110 | return NONE_RETURN; 111 | } 112 | 113 | /** 114 | * 调用IActivityManager接口之后的操作 115 | * @param receiver 116 | * @param method 117 | * @param args 118 | * @param invokeResult 原始返回的结果 119 | * @return 实际要处理返回的结果,默认不做任何处理,返回 invokeResult 120 | */ 121 | public Object onEndInvoke(Object receiver, Method method, Object[] args, Object invokeResult) { 122 | return invokeResult; 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/parser/ComponentNameComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.reginald.pluginm.parser; 24 | 25 | import android.content.ComponentName; 26 | import android.text.TextUtils; 27 | 28 | import java.util.Comparator; 29 | 30 | public class ComponentNameComparator implements Comparator { 31 | 32 | @Override 33 | public int compare(ComponentName lhs, ComponentName rhs) { 34 | if (lhs == null && rhs == null) { 35 | return 0; 36 | } else if (lhs != null && rhs == null) { 37 | return 1; 38 | } else if (lhs == null && rhs != null) { 39 | return -1; 40 | } else { 41 | if (TextUtils.equals(lhs.getPackageName(), rhs.getPackageName()) && TextUtils.equals(lhs.getShortClassName(), rhs.getShortClassName())) { 42 | return 0; 43 | } else { 44 | return lhs.compareTo(rhs); 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/parser/PackageParserApi20.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.reginald.pluginm.parser; 24 | 25 | import android.annotation.TargetApi; 26 | import android.content.Context; 27 | import android.os.Build; 28 | import android.util.DisplayMetrics; 29 | 30 | import com.reginald.pluginm.reflect.MethodUtils; 31 | 32 | import java.io.File; 33 | 34 | /** 35 | * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/2/13. 36 | */ 37 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 38 | class PackageParserApi20 extends PackageParserApi21 { 39 | 40 | 41 | public PackageParserApi20(Context context) throws Exception { 42 | super(context); 43 | } 44 | 45 | @Override 46 | public void parsePackage(File sourceFile, int flags) throws Exception { 47 | /* public Package parsePackage(File sourceFile, String destCodePath, 48 | DisplayMetrics metrics, int flags)*/ 49 | DisplayMetrics metrics = new DisplayMetrics(); 50 | metrics.setToDefaults(); 51 | String destCodePath = sourceFile.getPath(); 52 | mPackageParser = MethodUtils.invokeConstructor(sPackageParserClass, destCodePath); 53 | mPackage = MethodUtils.invokeMethod(mPackageParser, "parsePackage", sourceFile, destCodePath, metrics, flags); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/parser/PackageParserApi22.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.reginald.pluginm.parser; 24 | 25 | import android.annotation.TargetApi; 26 | import android.content.Context; 27 | import android.content.pm.PackageInfo; 28 | import android.os.Build; 29 | 30 | import com.reginald.pluginm.reflect.MethodUtils; 31 | import com.reginald.pluginm.utils.Logger; 32 | 33 | import java.lang.reflect.Constructor; 34 | import java.lang.reflect.Method; 35 | import java.util.Collection; 36 | import java.util.HashSet; 37 | 38 | /** 39 | * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/5/29. 40 | */ 41 | @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1) 42 | class PackageParserApi22 extends PackageParserApi21 { 43 | private static final String TAG = PackageParserApi22Preview1.class.getSimpleName(); 44 | 45 | PackageParserApi22(Context context) throws Exception { 46 | super(context); 47 | } 48 | 49 | @Override 50 | public PackageInfo generatePackageInfo( 51 | int gids[], int flags, long firstInstallTime, long lastUpdateTime, 52 | HashSet grantedPermissions) throws Exception { 53 | /*public static PackageInfo generatePackageInfo(PackageParser.Package p, 54 | int gids[], int flags, long firstInstallTime, long lastUpdateTime, 55 | HashSet grantedPermissions, PackageUserState state, int userId) */ 56 | try { 57 | return super.generatePackageInfo(gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions); 58 | } catch (Exception e) { 59 | Logger.w(TAG, "generatePackageInfo fail", e); 60 | } 61 | 62 | Method method = MethodUtils.getAccessibleMethod(sPackageParserClass, "generatePackageInfo", 63 | mPackage.getClass(), 64 | int[].class, int.class, long.class, long.class, sArraySetClass, sPackageUserStateClass, int.class); 65 | Object grantedPermissionsArray = null; 66 | try { 67 | Constructor constructor = sArraySetClass.getConstructor(Collection.class); 68 | grantedPermissionsArray = constructor.newInstance(constructor, grantedPermissions); 69 | } catch (Exception e) { 70 | Logger.e(TAG, "generatePackageInfo error!", e); 71 | } 72 | if (grantedPermissionsArray == null) { 73 | grantedPermissionsArray = grantedPermissions; 74 | } 75 | return (PackageInfo) method.invoke(null, mPackage, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissionsArray, mDefaultPackageUserState, mUserId); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/parser/PackageParserApi22Preview1.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.reginald.pluginm.parser; 24 | 25 | import android.content.Context; 26 | import android.content.pm.PackageInfo; 27 | 28 | import com.reginald.pluginm.reflect.MethodUtils; 29 | import com.reginald.pluginm.utils.Logger; 30 | 31 | import java.lang.reflect.Method; 32 | import java.util.HashSet; 33 | import java.util.Set; 34 | 35 | /** 36 | * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/5/29. 37 | */ 38 | //for Android M 39 | class PackageParserApi22Preview1 extends PackageParserApi21 { 40 | 41 | 42 | private static final String TAG = PackageParserApi22Preview1.class.getSimpleName(); 43 | 44 | PackageParserApi22Preview1(Context context) throws Exception { 45 | super(context); 46 | } 47 | 48 | @Override 49 | public PackageInfo generatePackageInfo( 50 | int gids[], int flags, long firstInstallTime, long lastUpdateTime, 51 | HashSet grantedPermissions) throws Exception { 52 | /*public static PackageInfo generatePackageInfo(PackageParser.Package p, 53 | int gids[], int flags, long firstInstallTime, long lastUpdateTime, 54 | HashSet grantedPermissions, PackageUserState state, int userId) */ 55 | try { 56 | return super.generatePackageInfo(gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions); 57 | } catch (Exception e) { 58 | Logger.w(TAG, "generatePackageInfo fail", e); 59 | } 60 | Method method = MethodUtils.getAccessibleMethod(sPackageParserClass, "generatePackageInfo", 61 | mPackage.getClass(), 62 | int[].class, int.class, long.class, long.class, Set.class, sPackageUserStateClass, int.class); 63 | return (PackageInfo) method.invoke(null, mPackage, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions, mDefaultPackageUserState, mUserId); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/parser/PackageParserApi28.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.reginald.pluginm.parser; 24 | 25 | import android.content.Context; 26 | import android.content.pm.PackageInfo; 27 | import android.content.pm.Signature; 28 | 29 | import com.reginald.pluginm.reflect.FieldUtils; 30 | import com.reginald.pluginm.reflect.MethodUtils; 31 | import com.reginald.pluginm.utils.Logger; 32 | 33 | import java.lang.reflect.Method; 34 | import java.util.HashSet; 35 | import java.util.Set; 36 | 37 | /** 38 | * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/5/29. 39 | */ 40 | //for Android M 41 | class PackageParserApi28 extends PackageParserApi22 { 42 | 43 | 44 | private static final String TAG = PackageParserApi28.class.getSimpleName(); 45 | 46 | PackageParserApi28(Context context) throws Exception { 47 | super(context); 48 | } 49 | 50 | @Override 51 | public void collectCertificates(int flags) throws Exception { 52 | // public void collectCertificates(Package pkg, int flags) throws PackageParserException 53 | Method method = MethodUtils.getAccessibleMethod(sPackageParserClass, "collectCertificates", 54 | mPackage.getClass(), boolean.class); 55 | method.invoke(mPackageParser, mPackage, true); 56 | } 57 | 58 | @Override 59 | public void writeSignature(Signature[] signatures) throws Exception { 60 | // TODO write signatures on Android P 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/reflect/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.reginald.pluginm.reflect; 24 | 25 | import java.util.ArrayList; 26 | import java.util.HashSet; 27 | import java.util.LinkedHashSet; 28 | import java.util.List; 29 | 30 | /** 31 | * Created by Andy Zhang(zhangyong232@gmail.com)ClassUtils on 2015/3/26. 32 | */ 33 | public class Utils { 34 | 35 | static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; 36 | 37 | static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; 38 | 39 | static boolean isSameLength(final Object[] array1, final Object[] array2) { 40 | if ((array1 == null && array2 != null && array2.length > 0) || 41 | (array2 == null && array1 != null && array1.length > 0) || 42 | (array1 != null && array2 != null && array1.length != array2.length)) { 43 | return false; 44 | } 45 | return true; 46 | } 47 | 48 | static Class[] toClass(final Object... array) { 49 | if (array == null) { 50 | return null; 51 | } else if (array.length == 0) { 52 | return EMPTY_CLASS_ARRAY; 53 | } 54 | final Class[] classes = new Class[array.length]; 55 | for (int i = 0; i < array.length; i++) { 56 | classes[i] = array[i] == null ? null : array[i].getClass(); 57 | } 58 | return classes; 59 | } 60 | 61 | static Class[] nullToEmpty(final Class[] array) { 62 | if (array == null || array.length == 0) { 63 | return EMPTY_CLASS_ARRAY; 64 | } 65 | return array; 66 | } 67 | 68 | static Object[] nullToEmpty(final Object[] array) { 69 | if (array == null || array.length == 0) { 70 | return EMPTY_OBJECT_ARRAY; 71 | } 72 | return array; 73 | } 74 | 75 | public static List> getAllInterfaces(final Class cls) { 76 | if (cls == null) { 77 | return null; 78 | } 79 | final LinkedHashSet> interfacesFound = new LinkedHashSet>(); 80 | getAllInterfaces(cls, interfacesFound); 81 | return new ArrayList>(interfacesFound); 82 | } 83 | 84 | private static void getAllInterfaces(Class cls, final HashSet> interfacesFound) { 85 | while (cls != null) { 86 | final Class[] interfaces = cls.getInterfaces(); 87 | 88 | for (final Class i : interfaces) { 89 | if (interfacesFound.add(i)) { 90 | getAllInterfaces(i, interfacesFound); 91 | } 92 | } 93 | 94 | cls = cls.getSuperclass(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/reflect/Validate.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DroidPlugin Project 3 | ** 4 | ** Copyright(c) 2015 Andy Zhang 5 | ** 6 | ** This file is part of DroidPlugin. 7 | ** 8 | ** DroidPlugin is free software: you can redistribute it and/or 9 | ** modify it under the terms of the GNU Lesser General Public 10 | ** License as published by the Free Software Foundation, either 11 | ** version 3 of the License, or (at your option) any later version. 12 | ** 13 | ** DroidPlugin is distributed in the hope that it will be useful, 14 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | ** Lesser General Public License for more details. 17 | ** 18 | ** You should have received a copy of the GNU Lesser General Public 19 | ** License along with DroidPlugin. If not, see 20 | ** 21 | **/ 22 | 23 | package com.reginald.pluginm.reflect; 24 | 25 | /** 26 | * Created by Andy Zhang(zhangyong232@gmail.com)ClassUtils on 2015/3/26. 27 | */ 28 | class Validate { 29 | 30 | static void isTrue(final boolean expression, final String message, final Object... values) { 31 | if (expression == false) { 32 | throw new IllegalArgumentException(String.format(message, values)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/stub/PluginStubLocalActivityManager.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.stub; 2 | 3 | import com.reginald.pluginm.core.PluginManager; 4 | import com.reginald.pluginm.reflect.FieldUtils; 5 | import com.reginald.pluginm.utils.Logger; 6 | 7 | import android.app.Activity; 8 | import android.app.LocalActivityManager; 9 | import android.content.Intent; 10 | import android.view.Window; 11 | 12 | /** 13 | * Created by lxy on 18-6-20. 14 | */ 15 | 16 | public class PluginStubLocalActivityManager extends LocalActivityManager { 17 | private static final String TAG = "PluginStubLocalActivityManager"; 18 | private final PluginManager mPluginManager; 19 | 20 | /** 21 | * Create a new LocalActivityManager for holding activities running within 22 | * the given parent. 23 | * 24 | * @param parent the host of the embedded activities 25 | * @param singleMode True if the LocalActivityManger should keep a maximum 26 | */ 27 | public PluginStubLocalActivityManager(Activity parent, boolean singleMode) { 28 | super(parent, singleMode); 29 | mPluginManager = PluginManager.getInstance(); 30 | } 31 | 32 | public PluginStubLocalActivityManager(LocalActivityManager base) throws IllegalAccessException { 33 | this((Activity)FieldUtils.readField(base, "mParent"), (Boolean) FieldUtils.readField(base, "mSingleMode")); 34 | } 35 | 36 | @Override 37 | public Window startActivity(String id, Intent intent) { 38 | Logger.d(TAG, "startActivity " + intent); 39 | Intent resolvedIntent = mPluginManager.getPluginActivityIntent(intent); 40 | 41 | if (resolvedIntent == null) { 42 | resolvedIntent = intent; 43 | } 44 | 45 | return super.startActivity(id, resolvedIntent); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/stub/Stubs.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.stub; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | /** 7 | * 所有的非Activity的Stub组件,需要与Manifest中的Stub组建同步! 8 | * Created by lxy on 17-8-18. 9 | */ 10 | 11 | public class Stubs { 12 | 13 | public static class Activity { 14 | public static class Fake extends android.app.Activity { 15 | @Override 16 | protected void onCreate(@Nullable Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | finish(); 19 | } 20 | } 21 | } 22 | 23 | public static class Service { 24 | public static class P0 { 25 | public static class Service0 extends PluginStubMainService { 26 | } 27 | } 28 | 29 | public static class P1 { 30 | public static class Service0 extends PluginStubMainService { 31 | } 32 | } 33 | 34 | public static class P2 { 35 | public static class Service0 extends PluginStubMainService { 36 | } 37 | } 38 | 39 | public static class P3 { 40 | public static class Service0 extends PluginStubMainService { 41 | } 42 | } 43 | 44 | public static class P4 { 45 | public static class Service0 extends PluginStubMainService { 46 | } 47 | } 48 | 49 | public static class P5 { 50 | public static class Service0 extends PluginStubMainService { 51 | } 52 | } 53 | 54 | public static class P6 { 55 | public static class Service0 extends PluginStubMainService { 56 | } 57 | } 58 | 59 | public static class P7 { 60 | public static class Service0 extends PluginStubMainService { 61 | } 62 | } 63 | 64 | public static class P8 { 65 | public static class Service0 extends PluginStubMainService { 66 | } 67 | } 68 | 69 | public static class P9 { 70 | public static class Service0 extends PluginStubMainService { 71 | } 72 | } 73 | } 74 | 75 | public static class Provider { 76 | public static class P0 { 77 | public static class Provider0 extends PluginStubMainProvider { 78 | } 79 | } 80 | 81 | public static class P1 { 82 | public static class Provider0 extends PluginStubMainProvider { 83 | } 84 | } 85 | 86 | public static class P2 { 87 | public static class Provider0 extends PluginStubMainProvider { 88 | } 89 | } 90 | 91 | public static class P3 { 92 | public static class Provider0 extends PluginStubMainProvider { 93 | } 94 | } 95 | 96 | public static class P4 { 97 | public static class Provider0 extends PluginStubMainProvider { 98 | } 99 | } 100 | 101 | public static class P5 { 102 | public static class Provider0 extends PluginStubMainProvider { 103 | } 104 | } 105 | 106 | public static class P6 { 107 | public static class Provider0 extends PluginStubMainProvider { 108 | } 109 | } 110 | 111 | public static class P7 { 112 | public static class Provider0 extends PluginStubMainProvider { 113 | } 114 | } 115 | 116 | public static class P8 { 117 | public static class Provider0 extends PluginStubMainProvider { 118 | } 119 | } 120 | 121 | public static class P9 { 122 | public static class Provider0 extends PluginStubMainProvider { 123 | } 124 | } 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/utils/BinderParcelable.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.utils; 2 | 3 | import android.os.IBinder; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | import android.support.annotation.NonNull; 7 | 8 | /** 9 | * Created by lxy on 16-10-26. 10 | */ 11 | public class BinderParcelable implements Parcelable { 12 | 13 | public IBinder iBinder; 14 | 15 | public static final Creator CREATOR = new Creator() { 16 | @Override 17 | public BinderParcelable createFromParcel(Parcel parcel) { 18 | return new BinderParcelable(parcel); 19 | } 20 | 21 | @Override 22 | public BinderParcelable[] newArray(int i) { 23 | return new BinderParcelable[0]; 24 | } 25 | }; 26 | 27 | public BinderParcelable(@NonNull IBinder iBinder) { 28 | this.iBinder = iBinder; 29 | } 30 | 31 | public BinderParcelable(Parcel parcel) { 32 | iBinder = parcel.readStrongBinder(); 33 | } 34 | 35 | @Override 36 | public int describeContents() { 37 | return 0; 38 | } 39 | 40 | @Override 41 | public void writeToParcel(Parcel parcel, int i) { 42 | parcel.writeStrongBinder(iBinder); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/utils/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.utils; 2 | 3 | import android.app.Service; 4 | import android.content.ComponentName; 5 | import android.content.ContentProvider; 6 | import android.content.pm.ComponentInfo; 7 | import android.content.pm.PackageManager; 8 | import android.content.pm.ProviderInfo; 9 | import android.content.pm.ResolveInfo; 10 | import android.content.pm.ServiceInfo; 11 | import android.text.TextUtils; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collection; 15 | import java.util.Iterator; 16 | import java.util.List; 17 | 18 | /** 19 | * Created by lxy on 17-8-22. 20 | */ 21 | 22 | public class CommonUtils { 23 | 24 | public static boolean isComponentInfoMatch(ComponentInfo a, ComponentInfo b) { 25 | if (a == null && b == null) { 26 | return true; 27 | } else if (a != null && b != null) { 28 | return TextUtils.equals(a.name, b.name) && TextUtils.equals(a.packageName, b.packageName); 29 | } else { 30 | return false; 31 | } 32 | } 33 | 34 | public static boolean containsComponent(Collection collection, ComponentInfo componentInfo) { 35 | Iterator iterator = collection.iterator(); 36 | while (iterator.hasNext()) { 37 | if (isComponentInfoMatch(iterator.next(), componentInfo)) { 38 | return true; 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | public static boolean removeComponent(Collection collection, ComponentInfo componentInfo) { 45 | Iterator iterator = collection.iterator(); 46 | while (iterator.hasNext()) { 47 | if (isComponentInfoMatch(iterator.next(), componentInfo)) { 48 | iterator.remove(); 49 | return true; 50 | } 51 | } 52 | return false; 53 | } 54 | 55 | public static ServiceInfo getServiceInfo(Service service) { 56 | PackageManager pm = service.getPackageManager(); 57 | if (pm != null) { 58 | ComponentName componentName = new ComponentName(service, service.getClass()); 59 | try { 60 | return pm.getServiceInfo(componentName, 0); 61 | } catch (PackageManager.NameNotFoundException e) { 62 | } 63 | } 64 | 65 | return null; 66 | } 67 | 68 | public static ProviderInfo getProviderInfo(ContentProvider provider) { 69 | PackageManager pm = provider.getContext().getPackageManager(); 70 | if (pm != null) { 71 | ComponentName componentName = new ComponentName(provider.getContext(), provider.getClass()); 72 | try { 73 | return pm.getProviderInfo(componentName, 0); 74 | } catch (PackageManager.NameNotFoundException e) { 75 | } 76 | } 77 | 78 | return null; 79 | } 80 | 81 | public static List combineList(List a, List b) { 82 | if (a == null && b == null) { 83 | return null; 84 | } 85 | 86 | List results = new ArrayList<>(); 87 | if (a != null) { 88 | results.addAll(a); 89 | } 90 | 91 | if (b != null) { 92 | results.addAll(b); 93 | } 94 | 95 | return results; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/utils/ConfigUtils.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.utils; 2 | 3 | import android.os.Bundle; 4 | import android.text.TextUtils; 5 | 6 | import org.json.JSONArray; 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | 10 | import java.util.Collections; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | * Created by lxy on 17-9-22. 16 | */ 17 | 18 | public class ConfigUtils { 19 | public static final String KEY_INVOKER = "pluginm_invoker"; 20 | public static final String KEY_INVOKER_SERVICE = "service"; 21 | public static final String KEY_INVOKER_CLASS = "class"; 22 | public static final String KEY_INVOKER_PROCESS = "process"; 23 | 24 | public static Map> parseInvokerConfig(Bundle metaData) { 25 | if (metaData != null) { 26 | String configJson = metaData.getString(KEY_INVOKER); 27 | if (!TextUtils.isEmpty(configJson)) { 28 | JSONArray jsonArray = null; 29 | try { 30 | jsonArray = new JSONArray(configJson); 31 | int length = jsonArray.length(); 32 | Map> map = new HashMap<>(1); 33 | for (int i = 0; i < length; i++) { 34 | JSONObject item = jsonArray.optJSONObject(i); 35 | if (item != null) { 36 | String serviceName = item.optString(KEY_INVOKER_SERVICE); 37 | String className = item.optString(KEY_INVOKER_CLASS); 38 | String processName = item.optString(KEY_INVOKER_PROCESS); 39 | if (TextUtils.isEmpty(serviceName) || TextUtils.isEmpty(className)) { 40 | throw new IllegalStateException(String.format("Error config %s. 'service' and 'class' MUST be provided!", 41 | configJson)); 42 | } 43 | Map config = new HashMap<>(2); 44 | config.put(KEY_INVOKER_CLASS, className); 45 | config.put(KEY_INVOKER_PROCESS, processName); 46 | 47 | if (map.containsKey(serviceName)) { 48 | throw new IllegalStateException(String.format("Error config %s. duplicated service %s!", 49 | configJson, serviceName)); 50 | } 51 | 52 | map.put(serviceName, config); 53 | } 54 | } 55 | return map; 56 | } catch (JSONException e) { 57 | throw new IllegalStateException(String.format("Error config json %s!", 58 | configJson)); 59 | } 60 | } 61 | } 62 | 63 | return Collections.EMPTY_MAP; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/utils/Logger.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.utils; 2 | 3 | import android.util.Log; 4 | 5 | import com.reginald.pluginm.BuildConfig; 6 | 7 | import java.io.PrintWriter; 8 | import java.io.StringWriter; 9 | 10 | /** 11 | * Created by lxy on 17-8-21. 12 | */ 13 | 14 | public class Logger { 15 | public static final String TAG = "PluginM"; 16 | public static boolean LOG_ENABLED = BuildConfig.DEBUG_LOG; 17 | public static boolean ALWAYS_SHOW_ERROR = true; 18 | 19 | public static void d(String tag, String msg) { 20 | if (LOG_ENABLED) { 21 | Log.d(TAG, getLogMsg(tag, msg)); 22 | } 23 | } 24 | 25 | public static void d(String tag, String formatMsg, Object... args) { 26 | if (LOG_ENABLED) { 27 | d(tag, String.format(formatMsg, args)); 28 | } 29 | } 30 | 31 | public static void i(String tag, String msg) { 32 | if (LOG_ENABLED) { 33 | Log.i(TAG, getLogMsg(tag, msg)); 34 | } 35 | } 36 | 37 | public static void i(String tag, String formatMsg, Object... args) { 38 | if (LOG_ENABLED) { 39 | i(tag, String.format(formatMsg, args)); 40 | } 41 | } 42 | 43 | public static void w(String tag, String msg) { 44 | if (LOG_ENABLED) { 45 | Log.w(TAG, getLogMsg(tag, msg)); 46 | } 47 | } 48 | 49 | public static void w(String tag, String formatMsg, Object... args) { 50 | if (LOG_ENABLED) { 51 | w(tag, String.format(formatMsg, args)); 52 | } 53 | } 54 | 55 | public static void w(String tag, String msg, Throwable e) { 56 | if (LOG_ENABLED) { 57 | Log.w(TAG, getLogMsg(tag, msg + " Exception: " + getExceptionMsg(e))); 58 | } 59 | } 60 | 61 | public static void w(String tag, Throwable e, String formatMsg, Object... args) { 62 | if (LOG_ENABLED) { 63 | w(tag, String.format(formatMsg, args), e); 64 | } 65 | } 66 | 67 | public static void e(String tag, String msg) { 68 | if (LOG_ENABLED || ALWAYS_SHOW_ERROR) { 69 | Log.e(TAG, getLogMsg(tag, msg)); 70 | } 71 | } 72 | 73 | public static void e(String tag, String formatMsg, Object... args) { 74 | if (LOG_ENABLED || ALWAYS_SHOW_ERROR) { 75 | e(tag, String.format(formatMsg, args)); 76 | } 77 | } 78 | 79 | public static void e(String tag, String msg, Throwable e) { 80 | if (LOG_ENABLED || ALWAYS_SHOW_ERROR) { 81 | Log.e(TAG, getLogMsg(tag, msg + " Exception: " + getExceptionMsg(e))); 82 | } 83 | } 84 | 85 | public static void e(String tag, Throwable e, String formatMsg, Object... args) { 86 | if (LOG_ENABLED || ALWAYS_SHOW_ERROR) { 87 | e(tag, String.format(formatMsg, args), e); 88 | } 89 | } 90 | 91 | private static String getLogMsg(String subTag, String msg) { 92 | return "[" + subTag + "] " + msg; 93 | } 94 | 95 | private static String getExceptionMsg(Throwable e) { 96 | StringWriter sw = new StringWriter(1024); 97 | PrintWriter pw = new PrintWriter(sw); 98 | e.printStackTrace(pw); 99 | pw.close(); 100 | return sw.toString(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/utils/ProcessHelper.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.utils; 2 | 3 | import android.app.ActivityManager; 4 | import android.app.Application; 5 | import android.content.Context; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.os.Process; 9 | import android.text.TextUtils; 10 | 11 | import com.reginald.pluginm.reflect.MethodUtils; 12 | import com.reginald.pluginm.stub.StubManager; 13 | 14 | import java.lang.reflect.InvocationTargetException; 15 | import java.util.List; 16 | 17 | /** 18 | * Created by lxy on 17-9-28. 19 | */ 20 | 21 | public class ProcessHelper { 22 | 23 | public static int sPid; 24 | 25 | public static String sProcessName; 26 | 27 | public static Application sApp; 28 | 29 | private static Handler sHandler; 30 | 31 | public static void init(Application context) { 32 | sApp = context; 33 | sHandler = new Handler(Looper.getMainLooper()); 34 | sPid = Process.myPid(); 35 | sProcessName = getProcessName(context, sPid); 36 | if (TextUtils.isEmpty(sProcessName)) { 37 | throw new IllegalStateException("CAN NOT get processName for pid " + sPid); 38 | } 39 | } 40 | 41 | public static Context getHostContext() { 42 | return sApp; 43 | } 44 | 45 | public static void post(Runnable runnable) { 46 | sHandler.post(runnable); 47 | } 48 | 49 | public static void postDelayed(Runnable runnable, long delayMillis) { 50 | sHandler.postDelayed(runnable, delayMillis); 51 | } 52 | 53 | public static String getProcessName(Context context, int pid) { 54 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 55 | List raps = am.getRunningAppProcesses(); 56 | for (ActivityManager.RunningAppProcessInfo rap : raps) { 57 | if (rap != null && rap.pid == pid) { 58 | return rap.processName; 59 | } 60 | } 61 | return null; 62 | } 63 | 64 | public static boolean isPluginProcess(Context context) { 65 | StubManager.ProcessInfo processInfo = StubManager.getInstance(context).getProcessInfo(sProcessName); 66 | if (processInfo != null) { 67 | return true; 68 | } 69 | 70 | return false; 71 | } 72 | 73 | public static final void setArgV0(String name) { 74 | try { 75 | MethodUtils.invokeStaticMethod(Class.forName("android.os.Process"), "setArgV0", name); 76 | } catch (NoSuchMethodException e) { 77 | e.printStackTrace(); 78 | } catch (IllegalAccessException e) { 79 | e.printStackTrace(); 80 | } catch (InvocationTargetException e) { 81 | e.printStackTrace(); 82 | } catch (ClassNotFoundException e) { 83 | e.printStackTrace(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /PluginManager/src/main/java/com/reginald/pluginm/utils/ThreadUtils.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.utils; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | 8 | 9 | /** 10 | * Created by lxy on 17-8-18. 11 | */ 12 | 13 | public class ThreadUtils { 14 | public static final Handler sMainHandler = new Handler(Looper.getMainLooper()); 15 | 16 | public static void ensureRunOnMainThread(final Runnable runnable) { 17 | if (Looper.myLooper() == Looper.getMainLooper()) { 18 | runnable.run(); 19 | } else { 20 | final Object lock = new Object(); 21 | final AtomicBoolean mIsRunning = new AtomicBoolean(false); 22 | sMainHandler.post(new Runnable() { 23 | @Override 24 | public void run() { 25 | try { 26 | runnable.run(); 27 | } finally { 28 | mIsRunning.set(true); 29 | synchronized (lock) { 30 | lock.notifyAll(); 31 | } 32 | } 33 | 34 | } 35 | }); 36 | if (!mIsRunning.get()) { 37 | synchronized (lock) { 38 | try { 39 | lock.wait(); 40 | } catch (InterruptedException e) { 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /PluginManager/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.1' 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | ext { 16 | compileSdkVersion = 28 17 | buildToolsVersion = "28.0.3" 18 | minSdkVersion = 11 19 | 20 | DEBUG_LOG = "true" 21 | targetSharedLibDir = rootDir.path + '/build/shared_lib/' 22 | } 23 | 24 | allprojects { 25 | repositories { 26 | jcenter() 27 | google() 28 | } 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Wed Jul 13 13:57:17 CST 2016 16 | android.useDeprecatedNdk=true 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xyxyLiu/PluginM/c560db054915b42b810b742b510237aafbfbf761/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Sep 21 11:01:36 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /install-test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ########################################### 4 | ### Demo编译脚本,可以根据自己的需要进行修改 ### 5 | ########################################### 6 | 7 | host_pkg=com.example.testhost 8 | plugin_pkg1=com.example.testplugin 9 | plugin_pkg2=com.example.testplugin2 10 | buildType=assembleDebug 11 | apk_suffix=debug 12 | plugin_apk_dir=/sdcard/ 13 | only_host_build=0 14 | 15 | # command checking 16 | while getopts rh option 17 | do 18 | case "$option" in 19 | r) 20 | buildType=assembleRelease 21 | apk_suffix=release 22 | echo "option: build release" 23 | ;; 24 | h) 25 | only_host_build=1 26 | echo "option: only build host" 27 | ;; 28 | \?) 29 | echo "Usage: args [-r] [-h]" 30 | echo "-r means make release build" 31 | echo "-h means only build host demo" 32 | exit 2 33 | ;; 34 | esac 35 | done 36 | 37 | echo "host pkg = $host_pkg" 38 | echo "plugin_pkg1 = $plugin_pkg1" 39 | echo "plugin_pkg2 = $plugin_pkg2" 40 | 41 | 42 | # 编译插件的共享代码库,生成相应jar包,以provided形式供插件进行依赖,不要编译到最终的插件apk中! 43 | ./gradlew pluginsharelib:clean pluginsharelib:makeJar && \ 44 | 45 | # 是否需要编译两个插件demo 46 | if [ $only_host_build -eq 0 ] 47 | then 48 | adb shell mkdir $plugin_apk_dir 49 | # 编译testplugin2 demo 50 | ./gradlew testplugin2:clean testplugin2:$buildType && \ 51 | adb push testplugin2/build/outputs/apk/$apk_suffix/testplugin2-$apk_suffix.apk $plugin_apk_dir && \ 52 | # 编译testplugin demo 53 | ./gradlew testplugin:clean testplugin:$buildType && \ 54 | adb push testplugin/build/outputs/apk/$apk_suffix/testplugin-$apk_suffix.apk $plugin_apk_dir 55 | fi && \ 56 | 57 | # 编译宿主demo 58 | ./gradlew testhost:clean testhost:$buildType && \ 59 | adb push testhost/build/outputs/apk/$apk_suffix/testhost-$apk_suffix.apk /data/local/tmp/com.example.testhost && \ 60 | 61 | # 启动宿主demo 62 | adb shell pm install -r "/data/local/tmp/$host_pkg" && \ 63 | adb shell am start -n "$host_pkg/com.example.testhost.DemoActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -------------------------------------------------------------------------------- /pluginm-test.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xyxyLiu/PluginM/c560db054915b42b810b742b510237aafbfbf761/pluginm-test.keystore -------------------------------------------------------------------------------- /pluginsharelib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /pluginsharelib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | minSdkVersion rootProject.ext.minSdkVersion 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | consumerProguardFiles 'consumer-proguard-rules.pro' 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | lintOptions { 22 | abortOnError false 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | } 30 | 31 | task clearJar(type: Delete) { 32 | delete rootProject.ext.targetSharedLibDir 33 | } 34 | 35 | task makeJar(type: Copy) { 36 | from('build/intermediates/intermediate-jars/release/') 37 | into(rootProject.ext.targetSharedLibDir) 38 | include('classes.jar') 39 | rename ('classes.jar', 'pluginsharelib-v' + android.defaultConfig.versionName + 40 | '_' + android.defaultConfig.versionCode + '.jar') 41 | } 42 | 43 | makeJar.dependsOn(clearJar, build) 44 | -------------------------------------------------------------------------------- /pluginsharelib/consumer-proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -keep public class com.reginald.pluginm.demo.pluginsharelib.** { 19 | public ; 20 | public ; 21 | } -------------------------------------------------------------------------------- /pluginsharelib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /pluginsharelib/src/androidTest/java/com/reginald/pluginm/demo/pluginsharelib/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.demo.pluginsharelib; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * @see Testing documentation 15 | */ 16 | @RunWith(AndroidJUnit4.class) 17 | public class ExampleInstrumentedTest { 18 | @Test 19 | public void useAppContext() throws Exception { 20 | // Context of the app under test. 21 | Context appContext = InstrumentationRegistry.getTargetContext(); 22 | 23 | assertEquals("com.reginald.pluginm.demo.pluginsharelib.test", appContext.getPackageName()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pluginsharelib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pluginsharelib/src/main/aidl/com/reginald/pluginm/demo/pluginsharelib/ITestPluginBinder.aidl: -------------------------------------------------------------------------------- 1 | // ITestPluginBinder.aidl 2 | package com.reginald.pluginm.demo.pluginsharelib; 3 | 4 | // Declare any non-default types here with import statements 5 | import com.reginald.pluginm.demo.pluginsharelib.PluginItem; 6 | 7 | interface ITestPluginBinder { 8 | /** 9 | * Demonstrates some basic types that you can use as parameters 10 | * and return values in AIDL. 11 | */ 12 | String basicTypes(in PluginItem pluginItem); 13 | } 14 | -------------------------------------------------------------------------------- /pluginsharelib/src/main/aidl/com/reginald/pluginm/demo/pluginsharelib/ITestServiceBinder.aidl: -------------------------------------------------------------------------------- 1 | // ITestServiceBinder.aidl 2 | package com.reginald.pluginm.demo.pluginsharelib; 3 | 4 | interface ITestServiceBinder { 5 | void test(String url); 6 | } 7 | -------------------------------------------------------------------------------- /pluginsharelib/src/main/aidl/com/reginald/pluginm/demo/pluginsharelib/PluginItem.aidl: -------------------------------------------------------------------------------- 1 | // ITestServiceBinder.aidl 2 | package com.reginald.pluginm.demo.pluginsharelib; 3 | 4 | parcelable PluginItem; 5 | -------------------------------------------------------------------------------- /pluginsharelib/src/main/java/com/reginald/pluginm/demo/pluginsharelib/PluginItem.java: -------------------------------------------------------------------------------- 1 | package com.reginald.pluginm.demo.pluginsharelib; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by lxy on 17-9-26. 8 | */ 9 | 10 | public class PluginItem implements Parcelable{ 11 | public String pluginName; 12 | public int id; 13 | 14 | public PluginItem(String name, int id) { 15 | this.pluginName = name; 16 | this.id = id; 17 | } 18 | 19 | protected PluginItem(Parcel in) { 20 | pluginName = in.readString(); 21 | id = in.readInt(); 22 | } 23 | 24 | public static final Creator CREATOR = new Creator() { 25 | @Override 26 | public PluginItem createFromParcel(Parcel in) { 27 | return new PluginItem(in); 28 | } 29 | 30 | @Override 31 | public PluginItem[] newArray(int size) { 32 | return new PluginItem[size]; 33 | } 34 | }; 35 | 36 | public String toString() { 37 | return String.format("PluginItem[ pluginName = %s, id = %d ]", pluginName, id); 38 | } 39 | 40 | @Override 41 | public int describeContents() { 42 | return 0; 43 | } 44 | 45 | @Override 46 | public void writeToParcel(Parcel dest, int flags) { 47 | dest.writeString(pluginName); 48 | dest.writeInt(id); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':PluginApi', ':PluginManager', ':testplugin', ':testhost', ':testplugin2', ':pluginsharelib' 2 | -------------------------------------------------------------------------------- /testhost/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /testhost/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId "com.example.testhost" 9 | minSdkVersion rootProject.ext.minSdkVersion 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | buildConfigField("boolean", "DEBUG_LOG", rootProject.ext.DEBUG_LOG) 15 | } 16 | 17 | lintOptions { 18 | abortOnError false 19 | } 20 | 21 | signingConfigs { 22 | release { 23 | storeFile file("../pluginm-test.keystore") 24 | storePassword "pluginm" 25 | keyAlias "pluginm" 26 | keyPassword "pluginm" 27 | } 28 | } 29 | 30 | buildTypes { 31 | debug { 32 | minifyEnabled false 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | } 35 | release { 36 | minifyEnabled true 37 | shrinkResources true 38 | signingConfig signingConfigs.release 39 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 40 | } 41 | } 42 | } 43 | 44 | dependencies { 45 | implementation fileTree(dir: 'libs', include: ['*.jar']) 46 | implementation 'com.android.support:appcompat-v7:25.3.1' 47 | implementation project(':PluginManager') 48 | implementation project(':pluginsharelib') 49 | } 50 | -------------------------------------------------------------------------------- /testhost/libs/nineoldandroids.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xyxyLiu/PluginM/c560db054915b42b810b742b510237aafbfbf761/testhost/libs/nineoldandroids.jar -------------------------------------------------------------------------------- /testhost/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -keep public class * implements com.reginald.pluginm.pluginapi.IInvoker 20 | -------------------------------------------------------------------------------- /testhost/src/main/java/com/example/testhost/HostApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.testhost; 2 | 3 | import com.reginald.pluginm.PluginConfigs; 4 | import com.reginald.pluginm.PluginM; 5 | 6 | import android.app.Application; 7 | import android.content.Context; 8 | 9 | /** 10 | * Created by lxy on 16-6-22. 11 | */ 12 | public class HostApplication extends Application { 13 | 14 | @Override 15 | protected void attachBaseContext(Context base) { 16 | super.attachBaseContext(base); 17 | 18 | PluginM.onAttachBaseContext(this, new PluginConfigs() 19 | // 插件进程模式, 默认为 PluginConfigs.PROCESS_TYPE_INDEPENDENT。 20 | .setProcessType(Prefs.Config.getProcessType(this, PluginConfigs.PROCESS_TYPE_COMPLETE)) 21 | // 如果插件中使用插件ClassLoader加载未成功时,是否允许宿主尝试加载。默认为true 22 | .setUseHostLoader(Prefs.Config.getUseHostClassloader(this, true)) 23 | // 是否在插件进程中对宿主的Context进行hook。默认为true。(部分功能需要开启此选项,例如在webview中加载asset资源...) 24 | .setHostContextHook(Prefs.Config.getHookHostContext(this, true)) 25 | // 是否在插件进程中对所有系统binder服务进行hook。默认为false。(由于系统不知道插件的存在,所以要在所有插件与系统服务交互 26 | // 的过程中替换插件的信息为宿主信息,例如包名信息,component信息等。。。) 27 | .setSystemServicesHook(Prefs.Config.getHookSystemService(this, true)) 28 | // 是否开启签名检测,默认为false 29 | //.setSignatureCheckEnabled(true) 30 | // 如果开启签名检测,请填写合法的签名信息. 默认为空 31 | //.addSignatures(MaybeYourHostSignatures) 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /testhost/src/main/java/com/example/testhost/HostContentProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.testhost; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.database.Cursor; 6 | import android.database.MatrixCursor; 7 | import android.net.Uri; 8 | import android.util.Log; 9 | 10 | public class HostContentProvider extends ContentProvider { 11 | 12 | private static final String TAG = "HostContentProvider"; 13 | 14 | public HostContentProvider() { 15 | } 16 | 17 | 18 | @Override 19 | public boolean onCreate() { 20 | Log.d(TAG,"onCreate() "); 21 | return true; 22 | } 23 | 24 | 25 | @Override 26 | public int delete(Uri uri, String selection, String[] selectionArgs) { 27 | // Implement this to handle requests to delete one or more rows. 28 | throw new UnsupportedOperationException("Not yet implemented"); 29 | } 30 | 31 | @Override 32 | public String getType(Uri uri) { 33 | // TODO: Implement this to handle requests for the MIME type of the data 34 | // at the given URI. 35 | throw new UnsupportedOperationException("Not yet implemented"); 36 | } 37 | 38 | @Override 39 | public Uri insert(Uri uri, ContentValues values) { 40 | // TODO: Implement this to handle requests to insert a new row. 41 | throw new UnsupportedOperationException("Not yet implemented"); 42 | } 43 | 44 | 45 | 46 | @Override 47 | public Cursor query(Uri uri, String[] projection, String selection, 48 | String[] selectionArgs, String sortOrder) { 49 | Log.d(TAG, String.format("query() uri = %s", uri)); 50 | MatrixCursor cursor = new MatrixCursor(new String[]{"column1"}); 51 | cursor.addRow(new Object[]{"row1.column1.host_value"}); 52 | return cursor; 53 | } 54 | 55 | @Override 56 | public int update(Uri uri, ContentValues values, String selection, 57 | String[] selectionArgs) { 58 | // TODO: Implement this to handle requests to update one or more rows. 59 | throw new UnsupportedOperationException("Not yet implemented"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /testhost/src/main/java/com/example/testhost/HostService.java: -------------------------------------------------------------------------------- 1 | package com.example.testhost; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.Binder; 6 | import android.os.IBinder; 7 | import android.util.Log; 8 | import android.widget.Toast; 9 | 10 | public class HostService extends Service { 11 | 12 | private static final String TAG = "HostService"; 13 | 14 | private Binder myBinder = new Binder(); 15 | 16 | 17 | public HostService() { 18 | Log.d(TAG,"HostService()"); 19 | } 20 | 21 | public void onCreate() { 22 | super.onCreate(); 23 | Toast.makeText(this, "service created!", Toast.LENGTH_LONG).show(); 24 | Log.d(TAG,"onCreate()"); 25 | } 26 | 27 | public int onStartCommand(Intent intent, int flags, int startId) { 28 | Log.d(TAG,"onStartCommand()"); 29 | if (intent != null && "stopself".equals(intent.getAction())) { 30 | stopSelf(); 31 | } 32 | 33 | return super.onStartCommand(intent,flags,startId); 34 | } 35 | 36 | public void onDestroy() { 37 | super.onDestroy(); 38 | Log.d(TAG,"onDestroy()"); 39 | } 40 | 41 | @Override 42 | public IBinder onBind(Intent intent) { 43 | Log.d(TAG,"onBind()"); 44 | showAction(intent); 45 | return new Binder(); 46 | } 47 | 48 | @Override 49 | public boolean onUnbind(Intent intent) { 50 | boolean res = false; 51 | Log.d(TAG,"onUnbind() return " + res); 52 | showAction(intent); 53 | return res; 54 | } 55 | 56 | @Override 57 | public void onRebind(Intent intent) { 58 | Log.d(TAG,"onRebind()"); 59 | showAction(intent); 60 | } 61 | 62 | public void showAction(Intent intent) { 63 | Log.d(TAG, "ACTION = " + intent.getAction()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /testhost/src/main/java/com/example/testhost/MyHostInvoker.java: -------------------------------------------------------------------------------- 1 | package com.example.testhost; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import android.os.RemoteException; 7 | import android.util.Log; 8 | 9 | import com.reginald.pluginm.demo.pluginsharelib.ITestServiceBinder; 10 | import com.reginald.pluginm.pluginapi.IInvokeCallback; 11 | import com.reginald.pluginm.pluginapi.IInvokeResult; 12 | import com.reginald.pluginm.pluginapi.IInvoker; 13 | 14 | /** 15 | * Created by lxy on 17-9-21. 16 | */ 17 | 18 | public class MyHostInvoker implements IInvoker { 19 | 20 | private static final String TAG = "MyHostInvoker"; 21 | 22 | private static final String METHOD_START_MAIN = "start_host_main"; 23 | 24 | @Override 25 | public IBinder onServiceCreate(final Context context) { 26 | Log.d(TAG, "onServiceCreate()"); 27 | return new ITestServiceBinder.Stub() { 28 | @Override 29 | public void test(String url) throws RemoteException { 30 | Log.d(TAG, "BINDER test() url = " + url); 31 | } 32 | }; 33 | } 34 | 35 | @Override 36 | public IInvokeResult onInvoke(Context context, String methodName, String params, IInvokeCallback callback) { 37 | Log.d(TAG, String.format("onInvoke methodName = %s, params = %s, callback = %s", 38 | methodName, params, callback)); 39 | 40 | if (METHOD_START_MAIN.equals(methodName)) { 41 | Intent intent = new Intent(context, DemoActivity.class); 42 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 43 | context.startActivity(intent); 44 | return new IInvokeResult() { 45 | @Override 46 | public int getResultCode() { 47 | return RESULT_OK; 48 | } 49 | 50 | @Override 51 | public String getResult() { 52 | return "{'activity_name':'" + DemoActivity.class.getName() + "'}"; 53 | } 54 | }; 55 | } 56 | 57 | return new IInvokeResult() { 58 | @Override 59 | public int getResultCode() { 60 | return IInvokeResult.RESULT_INVOKE_INVALID; 61 | } 62 | 63 | @Override 64 | public String getResult() { 65 | return null; 66 | } 67 | }; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /testhost/src/main/jniLibs/armeabi/libtestjni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xyxyLiu/PluginM/c560db054915b42b810b742b510237aafbfbf761/testhost/src/main/jniLibs/armeabi/libtestjni.so -------------------------------------------------------------------------------- /testhost/src/main/res/layout/activity_host.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 |