8.3 AccountManagerService分析

本节将分析AccountManagerService。如前所述,AccountManagerService负责管理手机中用户的online账户,主要工作涉及账户的添加和删除、AuthToken(全称为authentication token。有了它,客户端就无须每次操作都向服务器发送密码了)的获取和更新等。关于AccountManagerSerivce更详细的功能,可阅读SDK文档中AccountManager的说明。

8.3.1 初识AccountManagerService

下面看AccountManagerService创建时的代码:

[—>SystemServer. java:ServerThread.run]


……

//注册AccountManagerService到ServiceManager,服务名为account

ServiceManager.addService(Context.ACCOUNT_SERVICE,

new AccountManagerService(context));


其构造函数的代码如下:

[—>AccountManagerService. java:AccountManagerService]


public AccountManagerService(Context context){

//调用另外一个构造函数,其第三个参数将构造一个AccountAuthenticatorCache对象,它是

//什么呢?见下文分析

this(context, context.getPackageManager(),

new AccountAuthenticatorCache(context));

}


在AccountManagerService构造函数中创建了一个AccountAuthenticatorCache对象,它是什么?来看下文。

1.AccountAuthenticatorCache分析

AccountAuthenticatorCache是Android平台中账户验证服务(Account Authenticator Service, AAS)的管理中心。AAS由应用程序通过在AndroidManifest.xml中输出符合指定要求的Service信息而来。稍后读者将看到这些要求的具体格式。

先来看AccountAuthenticatorCache的派生关系,如图8-3所示。

8.3 AccountManagerService分析 - 图1

图 8-3 AccountAuthenticatorCache类图

由图8-3可知:

AccountAuthenticatorCache从RegisteredServicesCache<AuthenticatorDescription>派生。RegisteredServicesCache是一个模板类,专门用于管理系统中指定Service的信息收集和更新,而具体是哪些Service,则由RegisteredServicesCache构造时的参数指定。AccountAuthenticatorCache对外输出是由RegisteredServicesCache模板参数指定的类的实例。在图8-3中就是AuthenticatorDescription。

AuthenticatorDescription继承了Parcelable接口,这代表它可以跨Binder传递。该类描述了AAS相关的信息。

AccountAuthenticatorCache实现了IAccountAuthenticatorCache接口。这个接口供外部调用者使用以获取AAS的信息。

下面看AccountAuthenticatorCache的创建,其相关代码如下:

[—>AccountAuthenticatorCache. java:AccountAuthenticatorCache]


public AccountAuthenticatorCache(Context context){

/*

ACTION_AUTHENTICATOR_INTENT值为"android.accounts.AccountAuthenticator"

AUTHENTICATOR_META_DATA_NAME值为"android.accounts.AccountAuthenticator"

AUTHENTICATOR_ATTRIBUTES_NAME值为"account-authenticator"

*/

super(context,

AccountManager.ACTION_AUTHENTICATOR_INTENT,

AccountManager.AUTHENTICATOR_META_DATA_NAME,

AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);

}


AccountAuthenticatorCache调用其基类RegisteredServicesCache的构造函数时,传递了3个字符串参数,这3个参数用于控制RegisteredServicesCache从PackageManagerService获取哪些Service的信息。

(1)RegisteredServicesCache分析

[—>RegisteredServicesCache.java:RegisteredServicesCache]


public RegisteredServicesCache(Context context, String interfaceName,

String metaDataName, String attributeName,

XmlSerializerAndParser<V>serializerAndParser){

mContext=context;

//保存传递进来的参数

mInterfaceName=interfaceName;

mMetaDataName=metaDataName;

mAttributesName=attributeName;

mSerializerAndParser=serializerAndParser;

File dataDir=Environment.getDataDirectory();

File systemDir=new File(dataDir,"system");

//syncDir指向/data/system/registered_service目录

File syncDir=new File(systemDir,"registered_services");

//下面这个文件指向syncDir目录下的android.accounts.AccountAuthenticator.xml

mPersistentServicesFile=new AtomicFile(new File(syncDir,

interfaceName+".xml"));

//生成服务信息

generateServicesMap();

final BroadcastReceiver receiver=new BroadcastReceiver(){

public void onReceive(Context context1,Intent intent){

generateServicesMap();

}

};

//注册Package安装、卸载和更新等广播监听者

mReceiver=new AtomicReference<BroadcastReceiver>(receiver);

IntentFilter intentFilter=new IntentFilter();

intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);

intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);

intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);

intentFilter.addDataScheme("package");

mContext.registerReceiver(receiver, intentFilter);

IntentFilter sdFilter=new IntentFilter();

sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);

sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);

mContext.registerReceiver(receiver, sdFilter);

}


由以上代码可知:

成员变量mPersistentServicesFile指向/data/system/registered_service目录下的一个文件,该文件保存了以前获取的对应Service的信息。就AccountAuthenticator而言,mPersistentServicesFile指向该目录的android.accounts.AccountAuthenticator.xml文件。

由于RegisteredServicesCache管理的是系统中指定Service的信息,当系统中有Package安装、卸载或更新时,RegisteredServicesCache也需要对应更新自己的信息,因为有些Service可能会随着APK被删除而不复存在。

generateServiceMap函数将获取指定的Service信息,其代码如下:

[—>RegisteredServicesCache. java:generateServicesMap]


void generateServicesMap(){

//获取PackageManager接口,用来和PackageManagerService交互

PackageManager pm=mContext.getPackageManager();

ArrayList<ServiceInfo<V>>serviceInfos=new ArrayList<ServiceInfo<V>>();

/*

在本例中,查询PKMS中满足Intent Action为"android.accounts.AccountAuthenticator"

的服务信息。由以下代码可知,这些信息指的是Service中声明的MetaData信息

*/

List<ResolveInfo>resolveInfos=pm.queryIntentServices(

new Intent(mInterfaceName),PackageManager.GET_META_DATA);

for(ResolveInfo resolveInfo:resolveInfos){

try{

/*

调用parserServiceInfo函数解析从PKMS中获得的MetaData信息,该函数

返回的是一个模板类对象。就本例而言,这个函数返回一个

ServiceInfo<AccountAuthenticator>类型的对象

*/

ServiceInfo<V>info=parseServiceInfo(resolveInfo);

serviceInfos.add(info);

}

}

synchronized(mServicesLock){

if(mPersistentServices==null)

readPersistentServicesLocked();

mServices=Maps.newHashMap();

StringBuilder changes=new StringBuilder();

……//检查mPersistentServices保存的服务信息和当前从PKMS中取出来的PKMS

//信息,判断是否有变化,如果有变化,需要通知监听者。读者可自行阅读这段代码,

//注意其中uid的作用

mPersistentServicesFileDidNotExist=false;

}

}


接下来解析Service的parseServiceInfo函数。

(2)parseServiceInfo函数分析

这部分的代码如下:

[—>RegisteredServicesCache. java:parseServiceInfo]


private ServiceInfo<V>parseServiceInfo(ResolveInfo service)

throws XmlPullParserException, IOException{

android.content.pm.ServiceInfo si=service.serviceInfo;

ComponentName componentName=new ComponentName(si.packageName, si.name);

PackageManager pm=mContext.getPackageManager();

XmlResourceParser parser=null;

try{

//解析MetaData信息

parser=si.loadXmlMetaData(pm, mMetaDataName);

AttributeSet attrs=Xml.asAttributeSet(parser);

int type;

……

String nodeName=parser.getName();

//调用子类实现的parseServiceAttributes得到一个真实的对象,在本例中它是

//AuthenticatorDescription。注意,传递给parseServiceAttributes的第一个

//参数代表MetaData中的resource信息。详细内容见下文Email的图例

V v=parseServiceAttributes(

pm.getResourcesForApplication(si.applicationInfo),

si.packageName, attrs);

final android.content.pm.ServiceInfo serviceInfo=service.serviceInfo;

final ApplicationInfo applicationInfo=serviceInfo.applicationInfo;

final int uid=applicationInfo.uid;

return new ServiceInfo<V>(v, componentName, uid);

}……finally{

if(parser!=null)parser.close();

}

}


parseServiceInfo将解析Service中的MetaData信息,然后调用子类实现的parseService-Attributes函数,以获取特定类型Service的信息。

下面通过实例向读者展示最终的解析结果。

(3)AccountAuthenticatorCache分析总结

在Email应用的AndroidManifest.xml中定义一个AAS,如图8-4所示。

8.3 AccountManagerService分析 - 图2

图 8-4 Email AAS定义

由图8-4可知,在Email中这个Service对应为EasAuthenticatorService,其Intent匹配的Action为android.accounts.AccountAuthenticator,其MetaData的name为android.accounts.AccountAuthenticator,而MetaData的具体信息保存在resource资源中,在本例中,它指向另外一个XML文件,即eas_authenticator.xml,此文件的内容如图8-5所示。

8.3 AccountManagerService分析 - 图3

图 8-5 eas_authenticator.xml的内容

这个XML文件中的内容是有严格要求的,其中:

accountType标签用于指定账户类型(账户类型和具体应用有关,Android并未规定账户的类型)。

icon、smallIcon、label和accountPreferences等用于界面显示。例如,当需要用户输入账户信息时,系统会弹出一个Activity,上述几个标签就用于界面显示。详细情况可阅读SDK文档AbstractAccountAuthenticator的说明。

android. accounts.AccountAuthenticator.xml的内容如图8-6所示。

8.3 AccountManagerService分析 - 图4

图 8-6 android.accounts.AccountAuthenticator.xml的内容

由图8-6可知,笔者的测试机器上有3个AAS服务,其中同一个uid有两个服务(即uid为10015对应的两个Service)。

提示 uid是在为PackageManagerService解析APK文件时赋予APK的。读者不妨自行阅读frameworks/base/services/java/com/android/server/pm/Settings.java中的newUserIdLPw函数。

下面来看AccountManagerService的构造函数。

2.AccountManagerService构造函数分析

AccountManagerService构造函数的代码如下:

[—>AccountManagerService. java:AccountManagerService]


public AccountManagerService(Context context, PackageManager packageManager,

IAccountAuthenticatorCache authenticatorCache){

mContext=context;

mPackageManager=packageManager;

synchronized(mCacheLock){

//此数据库文件对应为/data/system/accounts.db

mOpenHelper=new DatabaseHelper(mContext);

}

mMessageThread=new HandlerThread("AccountManagerService");

mMessageThread.start();

mMessageHandler=new MessageHandler(mMessageThread.getLooper());

mAuthenticatorCache=authenticatorCache;

//为AccountAuthenticatorCache设置一个监听者,一旦AAS服务发生变化,

//AccountManagerService需要做对应处理

mAuthenticatorCache.setListener(this, null/Handler/);

sThis.set(this);

//监听ACTION_PACKAGE_REMOVED广播

IntentFilter intentFilter=new IntentFilter();

intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);

intentFilter.addDataScheme("package");

mContext.registerReceiver(new BroadcastReceiver(){

public void onReceive(Context context1,Intent intent){

purgeOldGrants();

}

},intentFilter);

/*

accounts.db数据库中有一个grants表,用于存储授权信息,该信息用于保存有权限获取账

户信息的Package。下面的函数将根据grants表中的数据查询PKMS判断这些Package

是否还存在。如果系统中已经不存在这些Package,则grants表需要更新

*/

purgeOldGrants();

/*

accounts.db中有一个accounts表,该表中存储了账户类型和账户名。其中,账户类型

就是AuthenticatorDescription中的accountType,它和具体应用有关。下面这个

函数将比较accounts表中的内容与AccountAuthenticatorCache中服务的信息,如果

AccountAuthenticatorCache已经不存在对应账户类型的服务,则需要删除accounts表

中的对应项

*/

validateAccountsAndPopulateCache();

}


AccountManagerService的构造函数较简单,有兴趣的读者可自行研究以上代码中未详细分析的函数。