主要思想
- host预先申请若干stubProvider
- hook ams,插件查询provider时,返回stubProVider
- 在 stubProvider 中操控插件的providr对象
Provider 查询
源码关键路径分析
getContext().getContentResolver().XXX最终会调用到ActivityThread的acquireProvider方法
1
2
3
4
5
6
7
8
9
10public final IContentProvider acquireProvider( Context c, String auth ...){
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
//查询AMS
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
return holder.provider;
}系统先到调用acquireExistingProvider方法查询,如果有返回。没有,查询AMS。很明显,acquireExistingProvider方法应该是查了缓存
1
2
3
4
5
6
7public final IContentProvider acquireExistingProvider(){
//果然只查了个缓存。
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) return null;
IContentProvider provider = pr.mProvider;
return provider;
}这里有两种hook方案,一种是DroidPlugin的方法,hook掉AMS的getContentProvider方法,另外是把插件的provider信息直接放到缓存mProviderMap中。
以hook掉AMS的getContentProvider方法为准hook AMS getContentProvider
在IActivityManagerHookHandle类中登记hook方法:
1
2
3protected void init() {
sHookedMethodHandlers.put("getContentProvider", new getContentProvider(mHostContext));
}看hook方案的实现
1
2
3
4
5
6
7
8
9protected boolean beforeInvoke(Object receiver, Method method, Object[] args){
String authority=(String) args[1];
//找到插件的provider记录
mTargetProvider = PluginManager.getInstance().resolveContentProvider(PluginProcessManager.getCurrentVUid(), name, 0);
//分配一个provider坑
mStubProvider = PluginManager.getInstance().selectStubProviderInfo(mTargetProvider, name);
//替换参数中的,让系统启动 stubProvider
args[index] = mStubProvider.authority;
}stubProvider 实现
onCreate
1
2
3
4
5
6
7
8
9
10先看增删改查的出处理
/** AbstractContentProviderStub **/
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY);
//拿到插件provider的对象
ContentProviderClient client = getContentProviderClient(targetAuthority);
//返回插件provider的执行结果
return client.query(buildNewUri(uri, targetAuthority), projection, selection, selectionArgs, sortOrder);
}继续看getContentProviderClient函数
1
2
3
4
5
6
7
8private synchronized ContentProviderClient getContentProviderClient(final String targetAuthority) {
ContentProviderClient client = sClients.get(targetAuthority);
if (client != null) {
return client;
}
//..
}sClient是一个map,很明显,这又是一个cache。cache中存储了插件provider的信息。继续看存储信息的时机。
加载provider信息
在PluginProcessManager类中,有个preLoadApk方法1
2
3
4private static void preLoadApk(Context hostContext, String packageName) {
Application application = applicationMap.get(packageName);
installContentProviders(hostContext, application, packageName);
}根据插件的packageName拿到插件的Application,继续看installContentProviders方法
1
2
3
4
5
6
7
8
9
10
11
12
13private static void installContentProviders(Context hostContext, Application app, String packageName) {
//获得stubProvider引用
AbstractContentProviderStub proxy = AbstractContentProviderStub.getProxyInstance();
//插件pkgInfo
PackageInfo pkgInfo = PluginManager.getInstance().getPackageInfo(packageName, PackageManager.GET_PROVIDERS);
for (ProviderInfo providerInfo : pkgInfo.providers) {
//获取满足条件的插件的providr
ContentProviderClient localProvider = app.getContentResolver().acquireContentProviderClient(providerInfo.authority);
//绑定stubProvider
proxy.installClient(providerInfo, localProvider);
}
}AbstractContentProviderStub.getProxyInstance()返回的是stubProvider的引用
1
2
3
4
5
6
7
8/** AbstractContentProviderStub **/
public boolean onCreate() {
sProxy = this;
}
public static AbstractContentProviderStub getProxyInstance() {
return sProxy;
}最后,看proxy.installClient方法,方法只将插件的provider放到缓存中
1
2
3public void installClient(ProviderInfo info, ContentProviderClient provider) {
sClients.put(info.authority, provider);
}至此,provider插件化分析完毕
小结
- 在插件加载时,程序缓存了插件pkg信息
- 插件查询provider时,返回一个预先申请好的stubProvider
- 将stubProvider与插件的provider绑定在一起,由stubProvider操控插件的provider
参考: