获取系统中注册的帐号类型列表是一个典型的用例。比如,在系统设置界面中选择“添加帐户”,这是,系统显示一个所有支持的帐户类型的列表界面(ChooseAccountActivity)供用户点选。另外,在Contacts/Calendar等应用程序中,也会向系统请求创建帐户或者现实帐户列表。背后的操作是统一由Android Framework提供的。应用程序只要将设置好的intent发送出去即可。
在研究如何获取帐户类型列表之前,简要的描述一下,应用程序如何将一个帐号注册到系统中。这个注册过程包含一下的要素:
public class ChooseAccountActivity extends PreferenceActivity { private AuthenticatorDescription[] mAuthDescs; ... @Override protected void onCreate(Bundle icicle) { ... updateAuthDescriptions(); } … }
mAuthDescs = AccountManager.get(this).getAuthenticatorTypes();
public AuthenticatorDescription[] getAuthenticatorTypes() { try { return mService.getAuthenticatorTypes(); } catch (RemoteException e) { // will never happen throw new RuntimeException(e); } }
public AuthenticatorDescription[] getAuthenticatorTypes() { ... final int userId = UserHandle.getCallingUserId(); final long identityToken = clearCallingIdentity(); try { Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> authenticatorCollection = mAuthenticatorCache.getAllServices(userId); AuthenticatorDescription[] types = new AuthenticatorDescription[authenticatorCollection.size()]; int i = 0; for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator : authenticatorCollection) { types[i] = authenticator.type; i++; } return types; } finally { restoreCallingIdentity(identityToken); } }
class AccountAuthenticatorCache extends RegisteredServicesCache<AuthenticatorDescription> implements IAccountAuthenticatorCache {
public static class ServiceInfo<V> { public final V type; public final ComponentName componentName; public final int uid; ... }
public Collection<ServiceInfo<V>> getAllServices(int userId) { synchronized (mServicesLock) { // Find user and lazily populate cache final UserServices<V> user = findOrCreateUserLocked(userId); if (user.services == null) { generateServicesMap(userId); } return Collections.unmodifiableCollection( new ArrayList<ServiceInfo<V>>(user.services.values())); } }
private void generateServicesMap(int userId) { ... final PackageManager pm = mContext.getPackageManager(); final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>(); final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser( new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId); for (ResolveInfo resolveInfo : resolveInfos) { try { ServiceInfo<V> info = parseServiceInfo(resolveInfo); ... serviceInfos.add(info); } catch (XmlPullParserException e) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); } catch (IOException e) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); } } ... }
public AccountAuthenticatorCache(Context context) { super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT, AccountManager.AUTHENTICATOR_META_DATA_NAME, AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer); }
public RegisteredServicesCache(Context context, String interfaceName, String metaDataName, String attributeName, XmlSerializerAndParser<V> serializerAndParser) { mContext = context; mInterfaceName = interfaceName; mMetaDataName = metaDataName; mAttributesName = attributeName; ... }
public static final String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
/** * Information that is returned from resolving an intent * against an IntentFilter. This partially corresponds to * information collected from the AndroidManifest.xml's * <intent> tags. */ public class ResolveInfo implements Parcelable {
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 { parser = si.loadXmlMetaData(pm, mMetaDataName); if (parser == null) { throw new XmlPullParserException("No " + mMetaDataName + " meta-data"); } AttributeSet attrs = Xml.asAttributeSet(parser); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { } String nodeName = parser.getName(); if (!mAttributesName.equals(nodeName)) { throw new XmlPullParserException( "Meta-data does not start with " + mAttributesName + " tag"); } V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo), si.packageName, attrs); if (v == null) { return null; } 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); } catch (NameNotFoundException e) { throw new XmlPullParserException( "Unable to load resources for pacakge " + si.packageName); } finally { if (parser != null) parser.close(); } }
V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo), si.packageName, attrs);
public AuthenticatorDescription parseServiceAttributes(Resources res, String packageName, AttributeSet attrs) { TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AccountAuthenticator); try { final String accountType = sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType); final int labelId = sa.getResourceId( com.android.internal.R.styleable.AccountAuthenticator_label, 0); final int iconId = sa.getResourceId( com.android.internal.R.styleable.AccountAuthenticator_icon, 0); final int smallIconId = sa.getResourceId( com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0); final int prefId = sa.getResourceId( com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0); final boolean customTokens = sa.getBoolean( com.android.internal.R.styleable.AccountAuthenticator_customTokens, false); if (TextUtils.isEmpty(accountType)) { return null; } return new AuthenticatorDescription(accountType, packageName, labelId, iconId, smallIconId, prefId, customTokens); } finally { sa.recycle(); } }