6.2.3 AMS的installSystemProviders函数分析
还记得Settings数据库吗?system_server中很多Service都需要向它查询配置信息。为此,Android提供了一个SettingsProvider来帮助开发者。该Provider在SettingsProvider.apk中,installSystemProviders就会加载该APK并把SettingsProvider放到system_server进程中来运行。
此时的system_server进程已经加载了framework-res.apk,现在又要加载另外一个APK文件,这就是多个APK运行在同一进程的典型案例。另外,通过installSystemProviders函数还能见识到ContentProvider的安装过程。下面就来分析installSystemProviders函数,其实现代码如下:
提示 读者在定制自己的Android系统时,千万不可去掉/system/app/SettingsProvider.apk,否则系统将无法正常启动。
[—>ActivityManagerService.java:installSystemProviders]
public static final void installSystemProviders(){
List<ProviderInfo>providers;
synchronized(mSelf){
/*
从mProcessNames找到进程名为“system”且uid为SYSTEM_UID的ProcessRecord,
返回值就是前面在installSystemApplication中创建的那个ProcessRecord,它代表
SystemServer进程
*/
ProcessRecord app=mSelf.mProcessNames.get("system",Process.SYSTEM_UID);
//①关键调用,见下文分析
providers=mSelf.generateApplicationProvidersLocked(app);
if(providers!=null){
……//将非系统APK(即未设ApplicationInfo.FLAG_SYSTEM标志)提供的Provider
//从providers列表中去掉
}
if(providers!=null){//②为SystemServer进程安装Provider
mSystemThread.installSystemProviders(providers);
}
//监视Settings数据库中Secure表的变化,目前只关注long_press_timeout配置的变化
mSelf.mCoreSettingsObserver=new CoreSettingsObserver(mSelf);
//UsageStatsService的工作,以后再讨论
mSelf.mUsageStatsService.monitorPackages();
}
上述代码中列出了两个关键调用,分别是:
调用generateApplicationProvidersLocked函数,该函数返回一个ProviderInfo List。
调用ActivityThread的installSystemProviders函数。ActivityThread可以看做是进程的Android运行环境,那么installSystemProviders表示为该进程安装ContentProvider。
注意 此处不再区分系统进程还是应用进程。由于只和ActivityThread交互,因此它运行在什么进程中无关紧要。
下面来看第一个关键点generateApplicationProvidersLocked函数。
1.AMS的generateApplicationProvidersLocked函数分析
generateApplicationProvidersLocked函数的实现代码如下:
[—>ActivityManagerService.java:generateApplicationProvidersLocked]
private final List<ProviderInfo>generateApplicationProvidersLocked(
ProcessRecord app){
List<ProviderInfo>providers=null;
try{
//①向PKMS查询满足要求的ProviderInfo,最重要的查询条件包括:进程名和进程uid
providers=AppGlobals.getPackageManager().
queryContentProviders(app.processName, app.info.uid,
STOCK_PM_FLAGS|PackageManager.GET_URI_PERMISSION_PATTERNS);
}……
if(providers!=null){
final int N=providers.size();
for(int i=0;i<N;i++){
//②AMS对ContentProvider的管理,见下文解释
ProviderInfo cpi=(ProviderInfo)providers.get(i);
ComponentName comp=new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr=mProvidersByClass.get(comp);
if(cpr==null){
cpr=new ContentProviderRecord(cpi, app.info, comp);
//ContentProvider在AMS中用ContentProviderRecord来表示
mProvidersByClass.put(comp, cpr);//保存到AMS的mProvidersByClass中
}
//将信息保存到ProcessRecord中
app.pubProviders.put(cpi.name, cpr);
//保存PackageName到ProcessRecord中
app.addPackage(cpi.applicationInfo.packageName);
//对该APK进行dex优化
ensurePackageDexOpt(cpi.applicationInfo.packageName);
}
}
return providers;
}
由以上代码可知:generateApplicationProvidersLocked先从PKMS那里查询满足条件的ProviderInfo信息,而后将它们分别保存到AMS和ProcessRecord中的对应的数据结构中。
下面先来看查询函数queryContentProviders。
(1)PMS中queryContentProviders函数分析queryContentProviders函数的实现代码如下:
[—>PackageManagerService.java:queryContentProviders]
public List<ProviderInfo>queryContentProviders(String processName,
int uid, int flags){
ArrayList<ProviderInfo>finalList=null;
synchronized(mPackages){
//还记得mProvidersByComponent的作用吗?它以ComponentName为key,保存了
//PKMS扫描APK得到的PackageParser.Provider信息。读者可参考图4-9
final Iterator<PackageParser.Provider>i=
mProvidersByComponent.values().iterator();
while(i.hasNext()){
final PackageParser.Provider p=i.next();
//下面的if语句将从这些Provider中搜索本例设置的processName为system,
//uid为SYSTEM_UID, flags为FLAG_SYSTEM的Provider
if(p.info.authority!=null
&&(processName==null
||(p.info.processName.equals(processName)
&&p.info.applicationInfo.uid==uid))
&&mSettings.isEnabledLPr(p.info, flags)
&&(!mSafeMode||(p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM)!=0)){
if(finalList==null){
finalList=new ArrayList<ProviderInfo>(3);
}
//由PackageParser.Provider得到ProviderInfo,并添加到finalList中
//关于Provider类及ProviderInfo类,可参考图4-6
finalList.add(PackageParser.generateProviderInfo(p, flags));
}
}
}
if(finalList!=null)
//最终结果按provider的initOrder排序,该值用于表示初始化ContentProvider的顺序
Collections.sort(finalList, mProviderInitOrderSorter);
return finalList;//返回最终结果
}
queryContentProviders函数很简单,就是从PKMS那里查找满足条件的Provider,然后生成AMS使用的ProviderInfo信息。为何偏偏能找到SettingsProvider呢?来看它的AndroidManifest.xml文件,如图6-7所示。
图 6-7 SettingsProvider的AndroidManifest.xml文件示意
由图6-7可知,SettingsProvider设置其uid为android.uid.system,同时在application中设置了process名为system。而在framework-res.apk中也做了相同的设置。所以,现在可以确认SettingsProvider将和framework-res.apk运行在同一个进程,即system_server中。
提示 从运行效率角度来说,这样做也是合情合理的。因为system_server进程中的很多Service都依赖Settings数据库,把它们放在同一个进程中,可以降低由于进程间通信带来的效率损失。
(2)关于ContentProvider的介绍
前面介绍的从PKMS那里查询到的ProviderInfo还属于公有财产,现在我们要将它与AMS及ProcessRecord联系起来。
AMS保存ProviderInfo的原因是它要管理ContentProvider。
ProcessRecord保存ProviderInfo的原因是ContentProvider最终要落实到一个进程中。其实也是为了方便AMS管理,例如该进程一旦退出,AMS需要把其中的ContentProvider信息从系统中去除。
AMS及ProcessRecord均使用了一个新的数据结构ContentProviderRecord来管理ContentProvider信息。图6-8展示了ContentProviderRecord相应的数据结构。
图 6-8 ContentProvicerRecord及相应的“管理团队”
由图6-8可知:
ContentProviderRecord从ContentProviderHolder派生,内部保存了ProviderInfo、该Provider所驻留的进程ProcessRecord,以及使用该ContentProvider的客户端进程ProcessRecord(即clients成员变量)。
AMS的mProviderByClass成员变量及ProcessRecord的pubProviders成员变量均以ComponentName为key来保存对应的ContentProviderRecord对象。
至此,Provider信息已经保存到AMS及ProcessRecord中了。那么,下一步的工作是什么呢?
2.ActivityThread的installSystemProviders函数分析
在AMS和ProcessRecord中都保存了Provider信息,但这些都仅是一些信息,并不是ContentProvider,因此下面要创建一个ContentProvider实例(即SettingsProvider对象)。该工作由ActivityThread的installSystemProviders函数来完成,其实现代码如下:
[—>ActivityThread.java:installSystemProviders]
public final void installSystemProviders(List<ProviderInfo>providers){
if(providers!=null)
//调用installContentProviders,第一个参数真实类型是Application
installContentProviders(mInitialApplication, providers);
}
installContentProviders这个函数是所有ContentProvider产生的必经之路,其实现代码如下:
[—>ActivityThread.java:installContentProviders]
private void installContentProviders(
Context context, List<ProviderInfo>providers){
final ArrayList<IActivityManager.ContentProviderHolder>results=
new ArrayList<IActivityManager.ContentProviderHolder>();
Iterator<ProviderInfo>i=providers.iterator();
while(i.hasNext()){
ProviderInfo cpi=i.next();
//①调用installProvider函数,得到一个IContentProvider对象
IContentProvider cp=installProvider(context, null, cpi, false);
if(cp!=null){
IActivityManager.ContentProviderHolder cph=
new IActivityManager.ContentProviderHolder(cpi);
cph.provider=cp;
//将返回的cp保存到results数组中
results.add(cph);
synchronized(mProviderMap){
//mProviderRefCountMap,类型为HashMap<IBinder, ProviderRefCount>,
//主要通过ProviderRefCount对ContentProvider进行引用计数控制,一旦引用计数
//降为零,则表示系统中没有地方使用该ContentProvider,要考虑从系统中注销它
mProviderRefCountMap.put(cp.asBinder(),new ProviderRefCount(10000));
}
}
}
try{
//②调用AMS的publishContentProviders注册这些ContentProvider,第一个参数
//为ApplicationThread
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(),results);
}
……
}
installContentProviders实际上是标准的ContentProvider安装时调用的程序。安装ContentProvider包括两方面的工作:
先在ActivityThread中通过installProvider得到一个ContentProvider实例。
向AMS发布这个ContentProvider实例。如此这般,一个APK中声明的Content-Provider才能发挥其该有的作用。
提示 上述工作其实和Binder Service类似,一个Binder Service也需要先创建,然后注册到ServiceManager中。
下面来看ActivityThread的installProvider函数。
(1)ActivityThread的installProvider函数分析
[—>ActivityThread.java:installProvider]
private IContentProvider installProvider(Context context,
IContentProvider provider, ProviderInfo info, boolean noisy){
//注意本例所传的参数:context为mInitialApplication, provider为null, info不为null,
//noisy为false
ContentProvider localProvider=null;
if(provider==null){
Context c=null;
ApplicationInfo ai=info.applicationInfo;
/*
下面这个if判断的作用就是为该ContentProvider找到对应的Application。
在AndroidManifest.xml中,ContentProvider是Application的子标签,所以
ContentProvider和Application有一种对应关系。在本例中,传入的context(
其实是mInitialApplication)代表的是framework-res.apk,而Provider代表的
是SettingsProvider。而SettingsProvider.apk所对应的Application还未创建,
所以下面的判断语句最终会进入最后的else分支
*/
if(context.getPackageName().equals(ai.packageName)){
c=context;
}else if(mInitialApplication!=null&&
mInitialApplication.getPackageName().equals(ai.packageName)){
c=mInitialApplication;
}else{
try{
//ai.packageName应该是SettingsProvider.apk的Package,
//名为com.android.providers.settings
//下面将创建一个Context,指向该APK
c=context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
}
}//if(context.getPackageName().equals(ai.packageName))判断结束
if(c==null)return null;
try{
/*
为什么一定要找到对应的Context呢?除了ContentProvider和Application的
对应关系外,还有一个决定性原因:即只有对应的Context才能加载对应APK的Java字节码,
从而可通过反射机制生成ContentProvider实例
*/
final java.lang.ClassLoader cl=c.getClassLoader();
//通过Java反射机制得到真正的ContentProvider,
//此处将得到一个SettingsProvider对象
localProvider=(ContentProvider)cl.loadClass(info.name).newInstance();
//从ContentProvider中取出其mTransport成员(见下文分析)
provider=localProvider.getIContentProvider();
if(provider==null)return null;
//初始化该ContentProvider,内部会调用其onCreate函数
localProvider.attachInfo(c, info);
}……
}//if(provider==null)判断结束
synchronized(mProviderMap){
/*
ContentProvider必须指明一个或多个authority,在第4章曾经提到过,
在URL中host:port的组合表示一个authority。这个单词不太好理解,可简单
认为它用于指定ContentProvider的位置(类似网站的域名)
*/
String names[]=PATTERN_SEMICOLON.split(info.authority);
for(int i=0;i<names.length;i++){
ProviderClientRecord pr=new ProviderClientRecord(names[i],
provider, localProvider);
try{
//下面这条语句对linkToDeath的调用颇让人费解,见下文分析
provider.asBinder().linkToDeath(pr,0);
mProviderMap.put(names[i],pr);
}……
}//for循环结束
if(localProvider!=null){
//mLocalProviders用于存储由本进程创建的ContentProvider信息
mLocalProviders.put(provider.asBinder(),
new ProviderClientRecord(null, provider, localProvider));
}
}//synchronized结束
return provider;
}
以上代码不算复杂,但是涉及一些数据结构和一条令人费解的语句,即对inkToDeath函数的调用语句。先来说说那句令人费解的调用。
在本例中,provider变量并非通过函数参数传入,而是在本进程内部创建的。provider在本例中是Bn端(后面分析ContentProvider的getIContentProvider时即可知道),Bn端进程为Bn端设置死亡通知本身就比较奇怪。如果Bn端进程死亡,它设置的死亡通知也无法发送给自己。幸好源代码中有句注释:“Cache the pointer for the remote provider”。意思是如果provider参数是通过installProvider传递过来的(即该Provider代表远端进程的ContentProvider,此时它应为Bp端),那么这种处理是合适的。不管怎样,这仅仅是为了保存pointer,所以也无关紧要。
至于代码中涉及的数据结构如图6-9所示。
图 6-9 ActivityThread中ContentProvider涉及的数据结构
由图6-9可知:
ContentProvider类本身只是一个容器,而跨进程调用的支持是通过内部类Transport实现的。Transport从ContentProviderNative派生,而ContentProvider的成员变量mTransport指向该Transport对象。ContentProvider的getIContentProvider函数即返回mTransport成员变量。
ContentProviderNative从Binder派生,并实现了IContentProvider接口。其内部类ContentProviderProxy是供客户端使用的。
ProviderClientRecord是ActivityThread提供的用于保存ContentProvider信息的一个数据结构。它的mLocalProvider用于保存ContentProvider对象,mProvider用于保存IContentProvider对象。另外一个成员mName用于保存该ContentProvider的一个authority。注意,ContentProvider可以定义多个authority,就好像一个网站有多个域名一样。
至此,本例中的SettingProvider已经创建完毕,接下来的工作就是把它推向历史舞台—即发布该Provider了。
(2)AMS的publishContentProviders分析
publishContentProviders函数用于向AMS注册ContentProviders,其实现代码如下:
[—>ActivityManagerService.java:publishContentProviders]
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder>providers){
……
synchronized(this){
//找到调用者所在的ProcessRecord对象
final ProcessRecord r=getRecordForAppLocked(caller);
……
final long origId=Binder.clearCallingIdentity();
final int N=providers.size();
for(int i=0;i<N;i++){
ContentProviderHolder src=providers.get(i);
……
//①注意:先从该ProcessRecord中找对应的ContentProviderRecord
ContentProviderRecord dst=r.pubProviders.get(src.info.name);
if(dst!=null){
ComponentName comp=new ComponentName(dst.info.packageName,
dst.info.name);
//以ComponentName为key,保存到mProvidersByClass中
mProvidersByClass.put(comp, dst);
String names[]=dst.info.authority.split(";");
for(int j=0;j<names.length;j++)
mProvidersByName.put(names[j],dst);//以authority为key,
//mLaunchingProviders用于保存处于启动状态的Provider
int NL=mLaunchingProviders.size();
int j;
for(j=0;j<NL;j++){
if(mLaunchingProviders.get(j)==dst){
mLaunchingProviders.remove(j);
j—;
NL—;
}//
}//for(j=0;j<NL;j++)结束
synchronized(dst){
dst.provider=src.provider;
dst.proc=r;
dst.notifyAll();
}//synchronized结束
updateOomAdjLocked(r);//每发布一个Provider,需要调整对应进程的oom_adj
}//if(dst!=null)结束
}//for(int i=0;j<N;j++)结束
Binder.restoreCallingIdentity(origId);
}//synchronized(this)结束
}
这里应解释一下publishContentProviders的工作流程:
先根据调用者的PID找到对应的ProcessRecord对象。
该ProcessRecord的pubProviders中保存了ContentProviderRecord信息。该信息由前面介绍的AMS的generateApplicationProvidersLocked函数根据Package本身的信息生成。此处将判断要发布的ContentProvider是否由该Package声明。
如果判断返回成功,则将该ContentProvider及其对应的authority加到mProviders-ByName中。注意,AMS中还有一个mProvidersByClass变量,该变量以Content-Provider的ComponentName为key,即系统提供两种方式找到某一个Content-Provider,一种是通过authority,另一种方式就是指明ComponentName。
mLaunchingProviders和最后的notifyAll函数用于通知那些等待ContentProvider所在进程启动的客户端进程。例如,进程A要查询一个数据库,需要通过进程B中的某个ContentProvider来实施。如果B还未启动,那么AMS就需要先启动B。在这段时间内,A需要等待B启动并注册对应的ContentProvider。B一旦完成注册,就需要告知A退出等待以继续后续的查询工作。
现在,一个SettingsProvider就算正式在系统中挂牌并注册了,此后,和Settings数据库相关的操作均由它来管理。
3.AMS的installSystemProviders总结
AMS的installSystemProviders函数其实就是用于启动SettingsProvider,其中比较复杂的是ContentProvider相关的数据结构,读者可参考图6-9。