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.2.3 AMS的installSystemProviders函数分析 - 图1

图 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.2.3 AMS的installSystemProviders函数分析 - 图2

图 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.2.3 AMS的installSystemProviders函数分析 - 图3

图 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。