blob: 6a14ff80e535867a13ab5c5b20d4f5fa73b938eb [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.accounts;
18
Fred Quintana60307342009-03-24 22:48:12 -070019import android.content.pm.PackageManager;
20import android.content.pm.ResolveInfo;
21import android.content.pm.ServiceInfo;
Fred Quintanaa698f422009-04-08 19:14:54 -070022import android.content.res.XmlResourceParser;
23import android.content.res.TypedArray;
24import android.content.BroadcastReceiver;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
Fred Quintana60307342009-03-24 22:48:12 -070029import android.util.Log;
30import android.util.AttributeSet;
31import android.util.Xml;
32
Fred Quintana60307342009-03-24 22:48:12 -070033import java.io.IOException;
Fred Quintanaa698f422009-04-08 19:14:54 -070034import java.io.FileDescriptor;
35import java.io.PrintWriter;
36import java.util.Collection;
37import java.util.Collections;
38import java.util.List;
39import java.util.Map;
Fred Quintana60307342009-03-24 22:48:12 -070040
41import com.google.android.collect.Maps;
42import org.xmlpull.v1.XmlPullParserException;
43import org.xmlpull.v1.XmlPullParser;
44
45/**
46 * A cache of services that export the {@link IAccountAuthenticator} interface. This cache
47 * is built by interrogating the {@link PackageManager} and is updated as packages are added,
48 * removed and changed. The authenticators are referred to by their account type and
49 * are made available via the {@link #getAuthenticatorInfo(String type)} method.
50 */
51public class AccountAuthenticatorCache {
52 private static final String TAG = "Account";
53
54 private static final String SERVICE_INTERFACE = "android.accounts.AccountAuthenticator";
55 private static final String SERVICE_META_DATA = "android.accounts.AccountAuthenticator";
56
57 private volatile Map<String, AuthenticatorInfo> mAuthenticators;
58
59 private final Context mContext;
60 private BroadcastReceiver mReceiver;
61
62 public AccountAuthenticatorCache(Context context) {
63 mContext = context;
64 mReceiver = new BroadcastReceiver() {
65 public void onReceive(Context context, Intent intent) {
66 buildAuthenticatorList();
67 }
68 };
69 }
70
Fred Quintanaa698f422009-04-08 19:14:54 -070071 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
72 getAllAuthenticators();
73 Map<String, AuthenticatorInfo> authenticators = mAuthenticators;
74 fout.println("AccountAuthenticatorCache: " + authenticators.size() + " authenticators");
75 for (AuthenticatorInfo info : authenticators.values()) {
76 fout.println(" " + info);
77 }
78 }
79
Fred Quintana60307342009-03-24 22:48:12 -070080 private void monitorPackageChanges() {
81 IntentFilter intentFilter = new IntentFilter();
82 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
83 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
84 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
85 mContext.registerReceiver(mReceiver, intentFilter);
86 }
87
88 /**
89 * Value type that describes an AccountAuthenticator. The information within can be used
90 * to bind to its {@link IAccountAuthenticator} interface.
91 */
92 public class AuthenticatorInfo {
93 public final String mType;
Fred Quintana60307342009-03-24 22:48:12 -070094 public final ComponentName mComponentName;
95
96 private AuthenticatorInfo(String type, ComponentName componentName) {
97 mType = type;
98 mComponentName = componentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070099 }
100
101 public String toString() {
102 return "AuthenticatorInfo: " + mType + ", " + mComponentName;
Fred Quintana60307342009-03-24 22:48:12 -0700103 }
104 }
105
106 /**
107 * Accessor for the registered authenticators.
108 * @param type the account type of the authenticator
109 * @return the AuthenticatorInfo that matches the account type or null if none is present
110 */
111 public AuthenticatorInfo getAuthenticatorInfo(String type) {
112 if (mAuthenticators == null) {
113 monitorPackageChanges();
114 buildAuthenticatorList();
115 }
116 return mAuthenticators.get(type);
117 }
118
119 /**
120 * @return a collection of {@link AuthenticatorInfo} objects for all
121 * registered authenticators.
122 */
123 public Collection<AuthenticatorInfo> getAllAuthenticators() {
124 if (mAuthenticators == null) {
125 monitorPackageChanges();
126 buildAuthenticatorList();
127 }
128 return Collections.unmodifiableCollection(mAuthenticators.values());
129 }
130
131 /**
132 * Stops the monitoring of package additions, removals and changes.
133 */
134 public void close() {
135 if (mReceiver != null) {
136 mContext.unregisterReceiver(mReceiver);
137 mReceiver = null;
138 }
139 }
140
141 protected void finalize() throws Throwable {
142 if (mReceiver != null) {
143 Log.e(TAG, "AccountAuthenticatorCache finalized without being closed");
144 }
145 close();
146 super.finalize();
147 }
148
149 private void buildAuthenticatorList() {
150 Map<String, AuthenticatorInfo> authenticators = Maps.newHashMap();
151 PackageManager pm = mContext.getPackageManager();
152
153 List<ResolveInfo> services =
154 pm.queryIntentServices(new Intent(SERVICE_INTERFACE), PackageManager.GET_META_DATA);
155
156 for (ResolveInfo resolveInfo : services) {
157 try {
158 AuthenticatorInfo info = parseAuthenticatorInfo(resolveInfo);
159 if (info != null) {
160 authenticators.put(info.mType, info);
161 } else {
162 Log.w(TAG, "Unable to load input method " + resolveInfo.toString());
163 }
164 } catch (XmlPullParserException e) {
165 Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
166 } catch (IOException e) {
167 Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
168 }
169 }
170
171 mAuthenticators = authenticators;
172 }
173
174 public AuthenticatorInfo parseAuthenticatorInfo(ResolveInfo service)
175 throws XmlPullParserException, IOException {
176 ServiceInfo si = service.serviceInfo;
177 ComponentName componentName = new ComponentName(si.packageName, si.name);
178
179 PackageManager pm = mContext.getPackageManager();
180 String authenticatorType = null;
181
182 XmlResourceParser parser = null;
183 try {
184 parser = si.loadXmlMetaData(pm, SERVICE_META_DATA);
185 if (parser == null) {
186 throw new XmlPullParserException("No " + SERVICE_META_DATA + " meta-data");
187 }
188
189 AttributeSet attrs = Xml.asAttributeSet(parser);
190
191 int type;
192 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
193 && type != XmlPullParser.START_TAG) {
194 }
195
196 String nodeName = parser.getName();
197 if (!"account-authenticator".equals(nodeName)) {
198 throw new XmlPullParserException(
199 "Meta-data does not start with account-authenticator tag");
200 }
201
202 TypedArray sa = mContext.getResources().obtainAttributes(attrs,
203 com.android.internal.R.styleable.AccountAuthenticator);
204 authenticatorType = sa.getString(
205 com.android.internal.R.styleable.AccountAuthenticator_accountType);
206 sa.recycle();
207 } finally {
208 if (parser != null) parser.close();
209 }
210
211 if (authenticatorType == null) {
212 return null;
213 }
214
215 return new AuthenticatorInfo(authenticatorType, componentName);
216 }
217}