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 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-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-5 eas_authenticator.xml的内容
这个XML文件中的内容是有严格要求的,其中:
accountType标签用于指定账户类型(账户类型和具体应用有关,Android并未规定账户的类型)。
icon、smallIcon、label和accountPreferences等用于界面显示。例如,当需要用户输入账户信息时,系统会弹出一个Activity,上述几个标签就用于界面显示。详细情况可阅读SDK文档AbstractAccountAuthenticator的说明。
android. accounts.AccountAuthenticator.xml的内容如图8-6所示。
图 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的构造函数较简单,有兴趣的读者可自行研究以上代码中未详细分析的函数。