blob: 5fee4de75920ffd4ce78c22dcf03a73d30da1494 [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
Doug Zongker885cfc232009-10-21 16:52:44 -070019import android.Manifest;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080020import android.app.ActivityManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070021import android.app.Notification;
22import android.app.NotificationManager;
23import android.app.PendingIntent;
Fred Quintanaa698f422009-04-08 19:14:54 -070024import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070025import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070026import android.content.ContentValues;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
Fred Quintanab839afc2009-10-14 15:57:28 -070030import android.content.ServiceConnection;
Doug Zongker885cfc232009-10-21 16:52:44 -070031import android.content.pm.ApplicationInfo;
32import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070033import android.content.pm.PackageManager;
34import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070035import android.content.pm.RegisteredServicesCacheListener;
Brian Carlstrom46703b02011-04-06 15:41:29 -070036import android.content.res.Resources;
Fred Quintana60307342009-03-24 22:48:12 -070037import android.database.Cursor;
38import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070039import android.database.sqlite.SQLiteDatabase;
40import android.database.sqlite.SQLiteOpenHelper;
Doug Zongker885cfc232009-10-21 16:52:44 -070041import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070042import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080043import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070044import android.os.Handler;
45import android.os.HandlerThread;
46import android.os.IBinder;
47import android.os.Looper;
48import android.os.Message;
49import android.os.RemoteException;
50import android.os.SystemClock;
Fred Quintanaa698f422009-04-08 19:14:54 -070051import android.text.TextUtils;
52import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070053import android.util.Pair;
Fred Quintana60307342009-03-24 22:48:12 -070054
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070055import com.android.internal.R;
56
Oscar Montemayora8529f62009-11-18 10:14:20 -080057import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -070058import java.io.FileDescriptor;
59import java.io.PrintWriter;
60import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -080061import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -070062import java.util.Collection;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070063import java.util.HashMap;
Fred Quintana56285a62010-12-02 14:20:51 -080064import java.util.LinkedHashMap;
Andy McFadden2f362292012-01-20 14:43:38 -080065import java.util.Map;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070066import java.util.concurrent.atomic.AtomicInteger;
67import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -070068
Fred Quintana60307342009-03-24 22:48:12 -070069/**
70 * A system service that provides account, password, and authtoken management for all
71 * accounts on the device. Some of these calls are implemented with the help of the corresponding
72 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
73 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -070074 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -070075 * @hide
Fred Quintana60307342009-03-24 22:48:12 -070076 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -070077public class AccountManagerService
78 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080079 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -070080 private static final String TAG = "AccountManagerService";
81
82 private static final int TIMEOUT_DELAY_MS = 1000 * 60;
83 private static final String DATABASE_NAME = "accounts.db";
Costin Manolache3348f142009-09-29 18:58:36 -070084 private static final int DATABASE_VERSION = 4;
Fred Quintana60307342009-03-24 22:48:12 -070085
86 private final Context mContext;
87
Fred Quintana56285a62010-12-02 14:20:51 -080088 private final PackageManager mPackageManager;
89
Fred Quintana60307342009-03-24 22:48:12 -070090 private HandlerThread mMessageThread;
91 private final MessageHandler mMessageHandler;
92
93 // Messages that can be sent on mHandler
94 private static final int MESSAGE_TIMED_OUT = 3;
Fred Quintana60307342009-03-24 22:48:12 -070095
Fred Quintana56285a62010-12-02 14:20:51 -080096 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -070097 private final DatabaseHelper mOpenHelper;
Fred Quintana60307342009-03-24 22:48:12 -070098
99 private static final String TABLE_ACCOUNTS = "accounts";
100 private static final String ACCOUNTS_ID = "_id";
101 private static final String ACCOUNTS_NAME = "name";
102 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700103 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700104 private static final String ACCOUNTS_PASSWORD = "password";
105
106 private static final String TABLE_AUTHTOKENS = "authtokens";
107 private static final String AUTHTOKENS_ID = "_id";
108 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
109 private static final String AUTHTOKENS_TYPE = "type";
110 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
111
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700112 private static final String TABLE_GRANTS = "grants";
113 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
114 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
115 private static final String GRANTS_GRANTEE_UID = "uid";
116
Fred Quintana60307342009-03-24 22:48:12 -0700117 private static final String TABLE_EXTRAS = "extras";
118 private static final String EXTRAS_ID = "_id";
119 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
120 private static final String EXTRAS_KEY = "key";
121 private static final String EXTRAS_VALUE = "value";
122
123 private static final String TABLE_META = "meta";
124 private static final String META_KEY = "key";
125 private static final String META_VALUE = "value";
126
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700127 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
128 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700129 private static final Intent ACCOUNTS_CHANGED_INTENT;
Fred Quintanaa698f422009-04-08 19:14:54 -0700130
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700131 private static final String COUNT_OF_MATCHING_GRANTS = ""
132 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
133 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
134 + " AND " + GRANTS_GRANTEE_UID + "=?"
135 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
136 + " AND " + ACCOUNTS_NAME + "=?"
137 + " AND " + ACCOUNTS_TYPE + "=?";
138
Fred Quintana56285a62010-12-02 14:20:51 -0800139 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
140 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
141 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
142 AUTHTOKENS_AUTHTOKEN};
143
144 private static final String SELECTION_USERDATA_BY_ACCOUNT =
145 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
146 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
147
Fred Quintanaa698f422009-04-08 19:14:54 -0700148 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700149 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
150
151 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
152 mCredentialsPermissionNotificationIds =
153 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
154 private final HashMap<Account, Integer> mSigninRequiredNotificationIds =
155 new HashMap<Account, Integer>();
156 private static AtomicReference<AccountManagerService> sThis =
157 new AtomicReference<AccountManagerService>();
158
Fred Quintana31957f12009-10-21 13:43:10 -0700159 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700160
161 static {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700162 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
Fred Quintana7be59642009-08-24 18:29:25 -0700163 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
164 }
165
Fred Quintana56285a62010-12-02 14:20:51 -0800166 private final Object mCacheLock = new Object();
167 /** protected by the {@link #mCacheLock} */
168 private final HashMap<String, Account[]> mAccountCache = new HashMap<String, Account[]>();
169 /** protected by the {@link #mCacheLock} */
170 private HashMap<Account, HashMap<String, String>> mUserDataCache =
171 new HashMap<Account, HashMap<String, String>>();
172 /** protected by the {@link #mCacheLock} */
173 private HashMap<Account, HashMap<String, String>> mAuthTokenCache =
174 new HashMap<Account, HashMap<String, String>>();
175
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700176 /**
177 * This should only be called by system code. One should only call this after the service
178 * has started.
179 * @return a reference to the AccountManagerService instance
180 * @hide
181 */
182 public static AccountManagerService getSingleton() {
183 return sThis.get();
184 }
Fred Quintana60307342009-03-24 22:48:12 -0700185
Fred Quintana56285a62010-12-02 14:20:51 -0800186 public AccountManagerService(Context context) {
187 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700188 }
189
Fred Quintana56285a62010-12-02 14:20:51 -0800190 public AccountManagerService(Context context, PackageManager packageManager,
191 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700192 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800193 mPackageManager = packageManager;
Fred Quintana60307342009-03-24 22:48:12 -0700194
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800195 synchronized (mCacheLock) {
196 mOpenHelper = new DatabaseHelper(mContext);
197 }
Fred Quintana60307342009-03-24 22:48:12 -0700198
199 mMessageThread = new HandlerThread("AccountManagerService");
200 mMessageThread.start();
201 mMessageHandler = new MessageHandler(mMessageThread.getLooper());
202
Fred Quintana56285a62010-12-02 14:20:51 -0800203 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800204 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700205
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700206 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800207
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800208 IntentFilter intentFilter = new IntentFilter();
209 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
210 intentFilter.addDataScheme("package");
211 mContext.registerReceiver(new BroadcastReceiver() {
212 @Override
213 public void onReceive(Context context1, Intent intent) {
214 purgeOldGrants();
215 }
216 }, intentFilter);
217 purgeOldGrants();
218
Fred Quintana56285a62010-12-02 14:20:51 -0800219 validateAccountsAndPopulateCache();
Fred Quintanaafa92b82009-12-01 16:27:03 -0800220 }
221
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800222 private void purgeOldGrants() {
223 synchronized (mCacheLock) {
224 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
225 final Cursor cursor = db.query(TABLE_GRANTS,
226 new String[]{GRANTS_GRANTEE_UID},
227 null, null, GRANTS_GRANTEE_UID, null, null);
228 try {
229 while (cursor.moveToNext()) {
230 final int uid = cursor.getInt(0);
231 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
232 if (packageExists) {
233 continue;
234 }
235 Log.d(TAG, "deleting grants for UID " + uid
236 + " because its package is no longer installed");
237 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
238 new String[]{Integer.toString(uid)});
239 }
240 } finally {
241 cursor.close();
242 }
243 }
244 }
245
Fred Quintana56285a62010-12-02 14:20:51 -0800246 private void validateAccountsAndPopulateCache() {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800247 synchronized (mCacheLock) {
248 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
249 boolean accountDeleted = false;
250 Cursor cursor = db.query(TABLE_ACCOUNTS,
251 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
252 null, null, null, null, null);
253 try {
Fred Quintana56285a62010-12-02 14:20:51 -0800254 mAccountCache.clear();
255 final HashMap<String, ArrayList<String>> accountNamesByType =
256 new HashMap<String, ArrayList<String>>();
257 while (cursor.moveToNext()) {
258 final long accountId = cursor.getLong(0);
259 final String accountType = cursor.getString(1);
260 final String accountName = cursor.getString(2);
261 if (mAuthenticatorCache.getServiceInfo(
262 AuthenticatorDescription.newKey(accountType)) == null) {
263 Log.d(TAG, "deleting account " + accountName + " because type "
264 + accountType + " no longer has a registered authenticator");
265 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
266 accountDeleted = true;
267 final Account account = new Account(accountName, accountType);
268 mUserDataCache.remove(account);
269 mAuthTokenCache.remove(account);
270 } else {
271 ArrayList<String> accountNames = accountNamesByType.get(accountType);
272 if (accountNames == null) {
273 accountNames = new ArrayList<String>();
274 accountNamesByType.put(accountType, accountNames);
275 }
276 accountNames.add(accountName);
277 }
278 }
Andy McFadden2f362292012-01-20 14:43:38 -0800279 for (Map.Entry<String, ArrayList<String>> cur
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800280 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800281 final String accountType = cur.getKey();
282 final ArrayList<String> accountNames = cur.getValue();
283 final Account[] accountsForType = new Account[accountNames.size()];
284 int i = 0;
285 for (String accountName : accountNames) {
286 accountsForType[i] = new Account(accountName, accountType);
287 ++i;
288 }
289 mAccountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800290 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800291 } finally {
292 cursor.close();
293 if (accountDeleted) {
294 sendAccountsChangedBroadcast();
295 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800296 }
297 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700298 }
299
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800300 public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
Fred Quintana56285a62010-12-02 14:20:51 -0800301 validateAccountsAndPopulateCache();
Fred Quintana60307342009-03-24 22:48:12 -0700302 }
303
Fred Quintanaa698f422009-04-08 19:14:54 -0700304 public String getPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800305 if (Log.isLoggable(TAG, Log.VERBOSE)) {
306 Log.v(TAG, "getPassword: " + account
307 + ", caller's uid " + Binder.getCallingUid()
308 + ", pid " + Binder.getCallingPid());
309 }
Fred Quintana382601f2010-03-25 12:25:10 -0700310 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700311 checkAuthenticateAccountsPermission(account);
312
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700313 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700314 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800315 return readPasswordInternal(account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700316 } finally {
317 restoreCallingIdentity(identityToken);
318 }
319 }
320
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800321 private String readPasswordInternal(Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700322 if (account == null) {
323 return null;
324 }
325
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800326 synchronized (mCacheLock) {
327 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
328 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
329 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
330 new String[]{account.name, account.type}, null, null, null);
331 try {
332 if (cursor.moveToNext()) {
333 return cursor.getString(0);
334 }
335 return null;
336 } finally {
337 cursor.close();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700338 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700339 }
340 }
341
342 public String getUserData(Account account, String key) {
Fred Quintana56285a62010-12-02 14:20:51 -0800343 if (Log.isLoggable(TAG, Log.VERBOSE)) {
344 Log.v(TAG, "getUserData: " + account
345 + ", key " + key
346 + ", caller's uid " + Binder.getCallingUid()
347 + ", pid " + Binder.getCallingPid());
348 }
Fred Quintana382601f2010-03-25 12:25:10 -0700349 if (account == null) throw new IllegalArgumentException("account is null");
350 if (key == null) throw new IllegalArgumentException("key is null");
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700351 checkAuthenticateAccountsPermission(account);
352 long identityToken = clearCallingIdentity();
353 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800354 return readUserDataInternal(account, key);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700355 } finally {
356 restoreCallingIdentity(identityToken);
357 }
358 }
359
Fred Quintana97889762009-06-15 12:29:24 -0700360 public AuthenticatorDescription[] getAuthenticatorTypes() {
Fred Quintana56285a62010-12-02 14:20:51 -0800361 if (Log.isLoggable(TAG, Log.VERBOSE)) {
362 Log.v(TAG, "getAuthenticatorTypes: "
363 + "caller's uid " + Binder.getCallingUid()
364 + ", pid " + Binder.getCallingPid());
365 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700366 long identityToken = clearCallingIdentity();
367 try {
Fred Quintana97889762009-06-15 12:29:24 -0700368 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
369 authenticatorCollection = mAuthenticatorCache.getAllServices();
370 AuthenticatorDescription[] types =
371 new AuthenticatorDescription[authenticatorCollection.size()];
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700372 int i = 0;
Fred Quintana97889762009-06-15 12:29:24 -0700373 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
Fred Quintana718d8a22009-04-29 17:53:20 -0700374 : authenticatorCollection) {
375 types[i] = authenticator.type;
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700376 i++;
377 }
378 return types;
379 } finally {
380 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700381 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700382 }
383
Fred Quintanaa698f422009-04-08 19:14:54 -0700384 public boolean addAccount(Account account, String password, Bundle extras) {
Fred Quintana56285a62010-12-02 14:20:51 -0800385 if (Log.isLoggable(TAG, Log.VERBOSE)) {
386 Log.v(TAG, "addAccount: " + account
387 + ", caller's uid " + Binder.getCallingUid()
388 + ", pid " + Binder.getCallingPid());
389 }
Fred Quintana382601f2010-03-25 12:25:10 -0700390 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700391 checkAuthenticateAccountsPermission(account);
392
Fred Quintana60307342009-03-24 22:48:12 -0700393 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700394 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700395 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800396 return addAccountInternal(account, password, extras);
Fred Quintana60307342009-03-24 22:48:12 -0700397 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700398 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700399 }
400 }
401
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800402 private boolean addAccountInternal(Account account, String password, Bundle extras) {
Fred Quintana743dfad2010-07-15 10:59:25 -0700403 if (account == null) {
404 return false;
405 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800406 synchronized (mCacheLock) {
407 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
408 db.beginTransaction();
409 try {
410 long numMatches = DatabaseUtils.longForQuery(db,
411 "select count(*) from " + TABLE_ACCOUNTS
412 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
413 new String[]{account.name, account.type});
414 if (numMatches > 0) {
415 Log.w(TAG, "insertAccountIntoDatabase: " + account
416 + ", skipping since the account already exists");
417 return false;
418 }
419 ContentValues values = new ContentValues();
420 values.put(ACCOUNTS_NAME, account.name);
421 values.put(ACCOUNTS_TYPE, account.type);
422 values.put(ACCOUNTS_PASSWORD, password);
423 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
424 if (accountId < 0) {
425 Log.w(TAG, "insertAccountIntoDatabase: " + account
426 + ", skipping the DB insert failed");
427 return false;
428 }
429 if (extras != null) {
430 for (String key : extras.keySet()) {
431 final String value = extras.getString(key);
432 if (insertExtraLocked(db, accountId, key, value) < 0) {
433 Log.w(TAG, "insertAccountIntoDatabase: " + account
434 + ", skipping since insertExtra failed for key " + key);
435 return false;
436 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700437 }
438 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800439 db.setTransactionSuccessful();
440 insertAccountIntoCacheLocked(account);
441 } finally {
442 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700443 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800444 sendAccountsChangedBroadcast();
445 return true;
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700446 }
447 }
448
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800449 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -0700450 ContentValues values = new ContentValues();
451 values.put(EXTRAS_KEY, key);
452 values.put(EXTRAS_ACCOUNTS_ID, accountId);
453 values.put(EXTRAS_VALUE, value);
454 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
455 }
456
Fred Quintana3084a6f2010-01-14 18:02:03 -0800457 public void hasFeatures(IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800458 Account account, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -0800459 if (Log.isLoggable(TAG, Log.VERBOSE)) {
460 Log.v(TAG, "hasFeatures: " + account
461 + ", response " + response
462 + ", features " + stringArrayToString(features)
463 + ", caller's uid " + Binder.getCallingUid()
464 + ", pid " + Binder.getCallingPid());
465 }
Fred Quintana382601f2010-03-25 12:25:10 -0700466 if (response == null) throw new IllegalArgumentException("response is null");
467 if (account == null) throw new IllegalArgumentException("account is null");
468 if (features == null) throw new IllegalArgumentException("features is null");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800469 checkReadAccountsPermission();
470 long identityToken = clearCallingIdentity();
471 try {
472 new TestFeaturesSession(response, account, features).bind();
473 } finally {
474 restoreCallingIdentity(identityToken);
475 }
476 }
477
478 private class TestFeaturesSession extends Session {
479 private final String[] mFeatures;
480 private final Account mAccount;
481
482 public TestFeaturesSession(IAccountManagerResponse response,
483 Account account, String[] features) {
Fred Quintana8570f742010-02-18 10:32:54 -0800484 super(response, account.type, false /* expectActivityLaunch */,
485 true /* stripAuthTokenFromResult */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800486 mFeatures = features;
487 mAccount = account;
488 }
489
490 public void run() throws RemoteException {
491 try {
492 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
493 } catch (RemoteException e) {
494 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
495 }
496 }
497
498 public void onResult(Bundle result) {
499 IAccountManagerResponse response = getResponseAndClose();
500 if (response != null) {
501 try {
502 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -0700503 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800504 return;
505 }
Fred Quintana56285a62010-12-02 14:20:51 -0800506 if (Log.isLoggable(TAG, Log.VERBOSE)) {
507 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
508 + response);
509 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800510 final Bundle newResult = new Bundle();
511 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
512 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
513 response.onResult(newResult);
514 } catch (RemoteException e) {
515 // if the caller is dead then there is no one to care about remote exceptions
516 if (Log.isLoggable(TAG, Log.VERBOSE)) {
517 Log.v(TAG, "failure while notifying response", e);
518 }
519 }
520 }
521 }
522
523 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -0800524 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800525 + ", " + mAccount
526 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
527 }
528 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800529
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700530 public void removeAccount(IAccountManagerResponse response, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800531 if (Log.isLoggable(TAG, Log.VERBOSE)) {
532 Log.v(TAG, "removeAccount: " + account
533 + ", response " + response
534 + ", caller's uid " + Binder.getCallingUid()
535 + ", pid " + Binder.getCallingPid());
536 }
Fred Quintana382601f2010-03-25 12:25:10 -0700537 if (response == null) throw new IllegalArgumentException("response is null");
538 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700539 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700540 long identityToken = clearCallingIdentity();
Costin Manolacheec0c4f42010-11-16 09:57:28 -0800541
542 cancelNotification(getSigninRequiredNotificationId(account));
543 synchronized(mCredentialsPermissionNotificationIds) {
544 for (Pair<Pair<Account, String>, Integer> pair:
545 mCredentialsPermissionNotificationIds.keySet()) {
546 if (account.equals(pair.first.first)) {
547 int id = mCredentialsPermissionNotificationIds.get(pair);
548 cancelNotification(id);
549 }
550 }
551 }
552
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700553 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700554 new RemoveAccountSession(response, account).bind();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700555 } finally {
556 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700557 }
Fred Quintana60307342009-03-24 22:48:12 -0700558 }
559
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700560 private class RemoveAccountSession extends Session {
561 final Account mAccount;
562 public RemoveAccountSession(IAccountManagerResponse response, Account account) {
Fred Quintana8570f742010-02-18 10:32:54 -0800563 super(response, account.type, false /* expectActivityLaunch */,
564 true /* stripAuthTokenFromResult */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700565 mAccount = account;
566 }
567
568 protected String toDebugString(long now) {
569 return super.toDebugString(now) + ", removeAccount"
570 + ", account " + mAccount;
571 }
572
573 public void run() throws RemoteException {
574 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
575 }
576
577 public void onResult(Bundle result) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700578 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
579 && !result.containsKey(AccountManager.KEY_INTENT)) {
580 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700581 if (removalAllowed) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800582 removeAccountInternal(mAccount);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700583 }
584 IAccountManagerResponse response = getResponseAndClose();
585 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -0800586 if (Log.isLoggable(TAG, Log.VERBOSE)) {
587 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
588 + response);
589 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700590 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700591 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700592 try {
593 response.onResult(result2);
594 } catch (RemoteException e) {
595 // ignore
596 }
597 }
598 }
599 super.onResult(result);
600 }
601 }
602
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800603 protected void removeAccountInternal(Account account) {
604 synchronized (mCacheLock) {
605 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
606 db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
607 new String[]{account.name, account.type});
608 removeAccountFromCacheLocked(account);
609 sendAccountsChangedBroadcast();
610 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700611 }
612
Fred Quintanaa698f422009-04-08 19:14:54 -0700613 public void invalidateAuthToken(String accountType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -0800614 if (Log.isLoggable(TAG, Log.VERBOSE)) {
615 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
616 + ", caller's uid " + Binder.getCallingUid()
617 + ", pid " + Binder.getCallingPid());
618 }
Fred Quintana382601f2010-03-25 12:25:10 -0700619 if (accountType == null) throw new IllegalArgumentException("accountType is null");
620 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Fred Quintanab38eb142010-02-24 13:40:54 -0800621 checkManageAccountsOrUseCredentialsPermissions();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700622 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700623 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800624 synchronized (mCacheLock) {
625 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
626 db.beginTransaction();
627 try {
628 invalidateAuthTokenLocked(db, accountType, authToken);
629 db.setTransactionSuccessful();
630 } finally {
631 db.endTransaction();
632 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700633 }
Fred Quintana60307342009-03-24 22:48:12 -0700634 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700635 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700636 }
637 }
638
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800639 private void invalidateAuthTokenLocked(SQLiteDatabase db, String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700640 if (authToken == null || accountType == null) {
641 return;
642 }
Fred Quintana33269202009-04-20 16:05:10 -0700643 Cursor cursor = db.rawQuery(
644 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
645 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
646 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
647 + " FROM " + TABLE_ACCOUNTS
648 + " JOIN " + TABLE_AUTHTOKENS
649 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
650 + " = " + AUTHTOKENS_ACCOUNTS_ID
651 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
652 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
653 new String[]{authToken, accountType});
654 try {
655 while (cursor.moveToNext()) {
656 long authTokenId = cursor.getLong(0);
657 String accountName = cursor.getString(1);
658 String authTokenType = cursor.getString(2);
659 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800660 writeAuthTokenIntoCacheLocked(db, new Account(accountName, accountType),
661 authTokenType, null);
Fred Quintana60307342009-03-24 22:48:12 -0700662 }
Fred Quintana33269202009-04-20 16:05:10 -0700663 } finally {
664 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -0700665 }
666 }
667
668 private boolean saveAuthTokenToDatabase(Account account, String type, String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -0700669 if (account == null || type == null) {
670 return false;
671 }
Fred Quintana6dfd1382009-09-16 17:32:42 -0700672 cancelNotification(getSigninRequiredNotificationId(account));
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800673 synchronized (mCacheLock) {
674 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
675 db.beginTransaction();
676 try {
677 long accountId = getAccountIdLocked(db, account);
678 if (accountId < 0) {
679 return false;
680 }
681 db.delete(TABLE_AUTHTOKENS,
682 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
683 new String[]{type});
684 ContentValues values = new ContentValues();
685 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
686 values.put(AUTHTOKENS_TYPE, type);
687 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
688 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
689 db.setTransactionSuccessful();
690 writeAuthTokenIntoCacheLocked(db, account, type, authToken);
691 return true;
692 }
Fred Quintana33269202009-04-20 16:05:10 -0700693 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800694 } finally {
695 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -0700696 }
Fred Quintana60307342009-03-24 22:48:12 -0700697 }
698 }
699
Fred Quintanaa698f422009-04-08 19:14:54 -0700700 public String peekAuthToken(Account account, String authTokenType) {
Fred Quintana56285a62010-12-02 14:20:51 -0800701 if (Log.isLoggable(TAG, Log.VERBOSE)) {
702 Log.v(TAG, "peekAuthToken: " + account
703 + ", authTokenType " + authTokenType
704 + ", caller's uid " + Binder.getCallingUid()
705 + ", pid " + Binder.getCallingPid());
706 }
Fred Quintana382601f2010-03-25 12:25:10 -0700707 if (account == null) throw new IllegalArgumentException("account is null");
708 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700709 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700710 long identityToken = clearCallingIdentity();
711 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800712 return readAuthTokenInternal(account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700713 } finally {
714 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700715 }
Fred Quintana60307342009-03-24 22:48:12 -0700716 }
717
Fred Quintanaa698f422009-04-08 19:14:54 -0700718 public void setAuthToken(Account account, String authTokenType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -0800719 if (Log.isLoggable(TAG, Log.VERBOSE)) {
720 Log.v(TAG, "setAuthToken: " + account
721 + ", authTokenType " + authTokenType
722 + ", caller's uid " + Binder.getCallingUid()
723 + ", pid " + Binder.getCallingPid());
724 }
Fred Quintana382601f2010-03-25 12:25:10 -0700725 if (account == null) throw new IllegalArgumentException("account is null");
726 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700727 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700728 long identityToken = clearCallingIdentity();
729 try {
Fred Quintana6dfd1382009-09-16 17:32:42 -0700730 saveAuthTokenToDatabase(account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700731 } finally {
732 restoreCallingIdentity(identityToken);
733 }
Fred Quintana60307342009-03-24 22:48:12 -0700734 }
735
Fred Quintanaa698f422009-04-08 19:14:54 -0700736 public void setPassword(Account account, String password) {
Fred Quintana56285a62010-12-02 14:20:51 -0800737 if (Log.isLoggable(TAG, Log.VERBOSE)) {
738 Log.v(TAG, "setAuthToken: " + account
739 + ", caller's uid " + Binder.getCallingUid()
740 + ", pid " + Binder.getCallingPid());
741 }
Fred Quintana382601f2010-03-25 12:25:10 -0700742 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700743 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700744 long identityToken = clearCallingIdentity();
745 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800746 setPasswordInternal(account, password);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700747 } finally {
748 restoreCallingIdentity(identityToken);
749 }
Fred Quintana60307342009-03-24 22:48:12 -0700750 }
751
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800752 private void setPasswordInternal(Account account, String password) {
Fred Quintana31957f12009-10-21 13:43:10 -0700753 if (account == null) {
754 return;
755 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800756 synchronized (mCacheLock) {
757 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
758 db.beginTransaction();
759 try {
760 final ContentValues values = new ContentValues();
761 values.put(ACCOUNTS_PASSWORD, password);
762 final long accountId = getAccountIdLocked(db, account);
763 if (accountId >= 0) {
764 final String[] argsAccountId = {String.valueOf(accountId)};
765 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
766 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Costin Manolachef5ffe892011-01-19 09:35:32 -0800767 mAuthTokenCache.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800768 db.setTransactionSuccessful();
Costin Manolachef5ffe892011-01-19 09:35:32 -0800769 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800770 } finally {
771 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -0800772 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800773 sendAccountsChangedBroadcast();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -0800774 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700775 }
776
Fred Quintana33269202009-04-20 16:05:10 -0700777 private void sendAccountsChangedBroadcast() {
Fred Quintana56285a62010-12-02 14:20:51 -0800778 Log.i(TAG, "the accounts changed, sending broadcast of "
779 + ACCOUNTS_CHANGED_INTENT.getAction());
Fred Quintana33269202009-04-20 16:05:10 -0700780 mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
781 }
782
Fred Quintanaa698f422009-04-08 19:14:54 -0700783 public void clearPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800784 if (Log.isLoggable(TAG, Log.VERBOSE)) {
785 Log.v(TAG, "clearPassword: " + account
786 + ", caller's uid " + Binder.getCallingUid()
787 + ", pid " + Binder.getCallingPid());
788 }
Fred Quintana382601f2010-03-25 12:25:10 -0700789 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700790 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700791 long identityToken = clearCallingIdentity();
792 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800793 setPasswordInternal(account, null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700794 } finally {
795 restoreCallingIdentity(identityToken);
796 }
Fred Quintana60307342009-03-24 22:48:12 -0700797 }
798
Fred Quintanaa698f422009-04-08 19:14:54 -0700799 public void setUserData(Account account, String key, String value) {
Fred Quintana56285a62010-12-02 14:20:51 -0800800 if (Log.isLoggable(TAG, Log.VERBOSE)) {
801 Log.v(TAG, "setUserData: " + account
802 + ", key " + key
803 + ", caller's uid " + Binder.getCallingUid()
804 + ", pid " + Binder.getCallingPid());
805 }
Fred Quintana382601f2010-03-25 12:25:10 -0700806 if (key == null) throw new IllegalArgumentException("key is null");
807 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700808 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700809 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700810 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800811 setUserdataInternal(account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -0700812 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700813 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700814 }
815 }
816
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800817 private void setUserdataInternal(Account account, String key, String value) {
Fred Quintana31957f12009-10-21 13:43:10 -0700818 if (account == null || key == null) {
819 return;
820 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800821 synchronized (mCacheLock) {
822 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
823 db.beginTransaction();
824 try {
825 long accountId = getAccountIdLocked(db, account);
826 if (accountId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700827 return;
828 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800829 long extrasId = getExtrasIdLocked(db, accountId, key);
830 if (extrasId < 0 ) {
831 extrasId = insertExtraLocked(db, accountId, key, value);
832 if (extrasId < 0) {
833 return;
834 }
835 } else {
836 ContentValues values = new ContentValues();
837 values.put(EXTRAS_VALUE, value);
838 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
839 return;
840 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700841
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800842 }
843 writeUserDataIntoCacheLocked(db, account, key, value);
844 db.setTransactionSuccessful();
845 } finally {
846 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700847 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700848 }
849 }
850
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700851 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -0800852 if (result == null) {
853 Log.e(TAG, "the result is unexpectedly null", new Exception());
854 }
855 if (Log.isLoggable(TAG, Log.VERBOSE)) {
856 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
857 + response);
858 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700859 try {
860 response.onResult(result);
861 } catch (RemoteException e) {
862 // if the caller is dead then there is no one to care about remote
863 // exceptions
864 if (Log.isLoggable(TAG, Log.VERBOSE)) {
865 Log.v(TAG, "failure while notifying response", e);
866 }
867 }
868 }
869
Costin Manolache5f383ad92010-12-02 16:44:46 -0800870 void getAuthTokenLabel(final IAccountManagerResponse response,
871 final Account account, final String authTokenType) {
872 if (account == null) throw new IllegalArgumentException("account is null");
873 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
874
875 checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
876
877 long identityToken = clearCallingIdentity();
878 try {
879 new Session(response, account.type, false,
880 false /* stripAuthTokenFromResult */) {
881 protected String toDebugString(long now) {
882 return super.toDebugString(now) + ", getAuthTokenLabel"
883 + ", " + account
884 + ", authTokenType " + authTokenType;
885 }
886
887 public void run() throws RemoteException {
888 mAuthenticator.getAuthTokenLabel(this, authTokenType);
889 }
890
891 public void onResult(Bundle result) {
892 if (result != null) {
893 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
894 Bundle bundle = new Bundle();
895 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
896 super.onResult(bundle);
897 return;
898 } else {
899 super.onResult(result);
900 }
901 }
902 }.bind();
903 } finally {
904 restoreCallingIdentity(identityToken);
905 }
906 }
907
Fred Quintanaa698f422009-04-08 19:14:54 -0700908 public void getAuthToken(IAccountManagerResponse response, final Account account,
909 final String authTokenType, final boolean notifyOnAuthFailure,
Costin Manolachec6684f92011-01-14 11:25:39 -0800910 final boolean expectActivityLaunch, Bundle loginOptionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -0800911 if (Log.isLoggable(TAG, Log.VERBOSE)) {
912 Log.v(TAG, "getAuthToken: " + account
913 + ", response " + response
914 + ", authTokenType " + authTokenType
915 + ", notifyOnAuthFailure " + notifyOnAuthFailure
916 + ", expectActivityLaunch " + expectActivityLaunch
917 + ", caller's uid " + Binder.getCallingUid()
918 + ", pid " + Binder.getCallingPid());
919 }
Fred Quintana382601f2010-03-25 12:25:10 -0700920 if (response == null) throw new IllegalArgumentException("response is null");
921 if (account == null) throw new IllegalArgumentException("account is null");
922 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700923 checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
Costin Manolachea40c6302010-12-13 14:50:45 -0800924 AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
925 mAuthenticatorCache.getServiceInfo(
926 AuthenticatorDescription.newKey(account.type));
927 final boolean customTokens =
928 authenticatorInfo != null && authenticatorInfo.type.customTokens;
929
930 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700931 final int callerUid = Binder.getCallingUid();
Costin Manolachea40c6302010-12-13 14:50:45 -0800932 final boolean permissionGranted = customTokens ||
933 permissionIsGranted(account, authTokenType, callerUid);
934
Costin Manolachec6684f92011-01-14 11:25:39 -0800935 final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
936 loginOptionsIn;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700937 // let authenticator know the identity of the caller
938 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
939 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
940 if (notifyOnAuthFailure) {
941 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -0800942 }
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700943
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700944 long identityToken = clearCallingIdentity();
945 try {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700946 // if the caller has permission, do the peek. otherwise go the more expensive
947 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -0800948 if (!customTokens && permissionGranted) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800949 String authToken = readAuthTokenInternal(account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700950 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700951 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700952 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
953 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
954 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700955 onResult(response, result);
956 return;
Fred Quintanaa698f422009-04-08 19:14:54 -0700957 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700958 }
959
Fred Quintana8570f742010-02-18 10:32:54 -0800960 new Session(response, account.type, expectActivityLaunch,
961 false /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700962 protected String toDebugString(long now) {
963 if (loginOptions != null) loginOptions.keySet();
964 return super.toDebugString(now) + ", getAuthToken"
965 + ", " + account
966 + ", authTokenType " + authTokenType
967 + ", loginOptions " + loginOptions
968 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
969 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700970
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700971 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700972 // If the caller doesn't have permission then create and return the
973 // "grant permission" intent instead of the "getAuthToken" intent.
974 if (!permissionGranted) {
975 mAuthenticator.getAuthTokenLabel(this, authTokenType);
976 } else {
977 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
978 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700979 }
980
981 public void onResult(Bundle result) {
982 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700983 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700984 Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
985 new AccountAuthenticatorResponse(this),
986 authTokenType,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700987 result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700988 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700989 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700990 onResult(bundle);
991 return;
992 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700993 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700994 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700995 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
996 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700997 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700998 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700999 "the type and name should not be empty");
1000 return;
1001 }
Costin Manolachea40c6302010-12-13 14:50:45 -08001002 if (!customTokens) {
1003 saveAuthTokenToDatabase(new Account(name, type),
1004 authTokenType, authToken);
1005 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001006 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001007
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001008 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08001009 if (intent != null && notifyOnAuthFailure && !customTokens) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001010 doNotification(
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001011 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001012 intent);
1013 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001014 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001015 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07001016 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001017 }.bind();
1018 } finally {
1019 restoreCallingIdentity(identityToken);
1020 }
Fred Quintana60307342009-03-24 22:48:12 -07001021 }
1022
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001023 private void createNoCredentialsPermissionNotification(Account account, Intent intent) {
1024 int uid = intent.getIntExtra(
1025 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
1026 String authTokenType = intent.getStringExtra(
1027 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
1028 String authTokenLabel = intent.getStringExtra(
1029 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);
1030
1031 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
1032 0 /* when */);
Eric Fischeree452ee2009-08-31 17:58:06 -07001033 final String titleAndSubtitle =
1034 mContext.getString(R.string.permission_request_notification_with_subtitle,
1035 account.name);
1036 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07001037 String title = titleAndSubtitle;
1038 String subtitle = "";
1039 if (index > 0) {
1040 title = titleAndSubtitle.substring(0, index);
1041 subtitle = titleAndSubtitle.substring(index + 1);
1042 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001043 n.setLatestEventInfo(mContext,
Eric Fischeree452ee2009-08-31 17:58:06 -07001044 title, subtitle,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001045 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
Fred Quintana56285a62010-12-02 14:20:51 -08001046 installNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001047 }
1048
Costin Manolache5f383ad92010-12-02 16:44:46 -08001049 String getAccountLabel(String accountType) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001050 RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo =
Costin Manolache5f383ad92010-12-02 16:44:46 -08001051 mAuthenticatorCache.getServiceInfo(
1052 AuthenticatorDescription.newKey(accountType));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001053 if (serviceInfo == null) {
Costin Manolache5f383ad92010-12-02 16:44:46 -08001054 throw new IllegalArgumentException("unknown account type: " + accountType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001055 }
1056
1057 final Context authContext;
1058 try {
1059 authContext = mContext.createPackageContext(
Costin Manolache5f383ad92010-12-02 16:44:46 -08001060 serviceInfo.type.packageName, 0);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001061 } catch (PackageManager.NameNotFoundException e) {
Costin Manolache5f383ad92010-12-02 16:44:46 -08001062 throw new IllegalArgumentException("unknown account type: " + accountType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001063 }
Brian Carlstrom46703b02011-04-06 15:41:29 -07001064 try {
1065 return authContext.getString(serviceInfo.type.labelId);
1066 } catch (Resources.NotFoundException e) {
1067 throw new IllegalArgumentException("unknown account type: " + accountType);
1068 }
Costin Manolache5f383ad92010-12-02 16:44:46 -08001069 }
1070
1071 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
1072 AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001073
1074 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07001075 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08001076 // Since it was set in Eclair+ we can't change it without breaking apps using
1077 // the intent from a non-Activity context.
1078 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001079 intent.addCategory(
1080 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08001081
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001082 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001083 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
1084 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001085 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001086
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001087 return intent;
1088 }
1089
1090 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
1091 int uid) {
1092 Integer id;
1093 synchronized(mCredentialsPermissionNotificationIds) {
1094 final Pair<Pair<Account, String>, Integer> key =
1095 new Pair<Pair<Account, String>, Integer>(
1096 new Pair<Account, String>(account, authTokenType), uid);
1097 id = mCredentialsPermissionNotificationIds.get(key);
1098 if (id == null) {
1099 id = mNotificationIds.incrementAndGet();
1100 mCredentialsPermissionNotificationIds.put(key, id);
1101 }
1102 }
1103 return id;
1104 }
1105
1106 private Integer getSigninRequiredNotificationId(Account account) {
1107 Integer id;
1108 synchronized(mSigninRequiredNotificationIds) {
1109 id = mSigninRequiredNotificationIds.get(account);
1110 if (id == null) {
1111 id = mNotificationIds.incrementAndGet();
1112 mSigninRequiredNotificationIds.put(account, id);
1113 }
1114 }
1115 return id;
1116 }
1117
Fred Quintana33269202009-04-20 16:05:10 -07001118 public void addAcount(final IAccountManagerResponse response, final String accountType,
1119 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001120 final boolean expectActivityLaunch, final Bundle optionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08001121 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1122 Log.v(TAG, "addAccount: accountType " + accountType
1123 + ", response " + response
1124 + ", authTokenType " + authTokenType
1125 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
1126 + ", expectActivityLaunch " + expectActivityLaunch
1127 + ", caller's uid " + Binder.getCallingUid()
1128 + ", pid " + Binder.getCallingPid());
1129 }
Fred Quintana382601f2010-03-25 12:25:10 -07001130 if (response == null) throw new IllegalArgumentException("response is null");
1131 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001132 checkManageAccountsPermission();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001133
1134 final int pid = Binder.getCallingPid();
1135 final int uid = Binder.getCallingUid();
1136 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
1137 options.putInt(AccountManager.KEY_CALLER_UID, uid);
1138 options.putInt(AccountManager.KEY_CALLER_PID, pid);
1139
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001140 long identityToken = clearCallingIdentity();
1141 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001142 new Session(response, accountType, expectActivityLaunch,
1143 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001144 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07001145 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07001146 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001147 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001148
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001149 protected String toDebugString(long now) {
1150 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07001151 + ", accountType " + accountType
1152 + ", requiredFeatures "
1153 + (requiredFeatures != null
1154 ? TextUtils.join(",", requiredFeatures)
1155 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001156 }
1157 }.bind();
1158 } finally {
1159 restoreCallingIdentity(identityToken);
1160 }
Fred Quintana60307342009-03-24 22:48:12 -07001161 }
1162
Fred Quintanaa698f422009-04-08 19:14:54 -07001163 public void confirmCredentials(IAccountManagerResponse response,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001164 final Account account, final Bundle options, final boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08001165 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1166 Log.v(TAG, "confirmCredentials: " + account
1167 + ", response " + response
1168 + ", expectActivityLaunch " + expectActivityLaunch
1169 + ", caller's uid " + Binder.getCallingUid()
1170 + ", pid " + Binder.getCallingPid());
1171 }
Fred Quintana382601f2010-03-25 12:25:10 -07001172 if (response == null) throw new IllegalArgumentException("response is null");
1173 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001174 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001175 long identityToken = clearCallingIdentity();
1176 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001177 new Session(response, account.type, expectActivityLaunch,
1178 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001179 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001180 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001181 }
1182 protected String toDebugString(long now) {
1183 return super.toDebugString(now) + ", confirmCredentials"
1184 + ", " + account;
1185 }
1186 }.bind();
1187 } finally {
1188 restoreCallingIdentity(identityToken);
1189 }
Fred Quintana60307342009-03-24 22:48:12 -07001190 }
1191
Fred Quintanaa698f422009-04-08 19:14:54 -07001192 public void updateCredentials(IAccountManagerResponse response, final Account account,
1193 final String authTokenType, final boolean expectActivityLaunch,
1194 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08001195 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1196 Log.v(TAG, "updateCredentials: " + account
1197 + ", response " + response
1198 + ", authTokenType " + authTokenType
1199 + ", expectActivityLaunch " + expectActivityLaunch
1200 + ", caller's uid " + Binder.getCallingUid()
1201 + ", pid " + Binder.getCallingPid());
1202 }
Fred Quintana382601f2010-03-25 12:25:10 -07001203 if (response == null) throw new IllegalArgumentException("response is null");
1204 if (account == null) throw new IllegalArgumentException("account is null");
1205 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001206 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001207 long identityToken = clearCallingIdentity();
1208 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001209 new Session(response, account.type, expectActivityLaunch,
1210 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001211 public void run() throws RemoteException {
1212 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
1213 }
1214 protected String toDebugString(long now) {
1215 if (loginOptions != null) loginOptions.keySet();
1216 return super.toDebugString(now) + ", updateCredentials"
1217 + ", " + account
1218 + ", authTokenType " + authTokenType
1219 + ", loginOptions " + loginOptions;
1220 }
1221 }.bind();
1222 } finally {
1223 restoreCallingIdentity(identityToken);
1224 }
Fred Quintana60307342009-03-24 22:48:12 -07001225 }
1226
Fred Quintanaa698f422009-04-08 19:14:54 -07001227 public void editProperties(IAccountManagerResponse response, final String accountType,
1228 final boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08001229 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1230 Log.v(TAG, "editProperties: accountType " + accountType
1231 + ", response " + response
1232 + ", expectActivityLaunch " + expectActivityLaunch
1233 + ", caller's uid " + Binder.getCallingUid()
1234 + ", pid " + Binder.getCallingPid());
1235 }
Fred Quintana382601f2010-03-25 12:25:10 -07001236 if (response == null) throw new IllegalArgumentException("response is null");
1237 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001238 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001239 long identityToken = clearCallingIdentity();
1240 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001241 new Session(response, accountType, expectActivityLaunch,
1242 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001243 public void run() throws RemoteException {
1244 mAuthenticator.editProperties(this, mAccountType);
1245 }
1246 protected String toDebugString(long now) {
1247 return super.toDebugString(now) + ", editProperties"
1248 + ", accountType " + accountType;
1249 }
1250 }.bind();
1251 } finally {
1252 restoreCallingIdentity(identityToken);
1253 }
Fred Quintana60307342009-03-24 22:48:12 -07001254 }
1255
Fred Quintana33269202009-04-20 16:05:10 -07001256 private class GetAccountsByTypeAndFeatureSession extends Session {
1257 private final String[] mFeatures;
1258 private volatile Account[] mAccountsOfType = null;
1259 private volatile ArrayList<Account> mAccountsWithFeatures = null;
1260 private volatile int mCurrentAccount = 0;
1261
1262 public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response,
1263 String type, String[] features) {
Fred Quintana8570f742010-02-18 10:32:54 -08001264 super(response, type, false /* expectActivityLaunch */,
1265 true /* stripAuthTokenFromResult */);
Fred Quintana33269202009-04-20 16:05:10 -07001266 mFeatures = features;
1267 }
1268
1269 public void run() throws RemoteException {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001270 synchronized (mCacheLock) {
1271 mAccountsOfType = getAccountsFromCacheLocked(mAccountType);
1272 }
Fred Quintana33269202009-04-20 16:05:10 -07001273 // check whether each account matches the requested features
1274 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
1275 mCurrentAccount = 0;
1276
1277 checkAccount();
1278 }
1279
1280 public void checkAccount() {
1281 if (mCurrentAccount >= mAccountsOfType.length) {
1282 sendResult();
1283 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001284 }
Fred Quintana33269202009-04-20 16:05:10 -07001285
Fred Quintana29e94b82010-03-10 12:11:51 -08001286 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
1287 if (accountAuthenticator == null) {
1288 // It is possible that the authenticator has died, which is indicated by
1289 // mAuthenticator being set to null. If this happens then just abort.
1290 // There is no need to send back a result or error in this case since
1291 // that already happened when mAuthenticator was cleared.
1292 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1293 Log.v(TAG, "checkAccount: aborting session since we are no longer"
1294 + " connected to the authenticator, " + toDebugString());
1295 }
1296 return;
1297 }
Fred Quintana33269202009-04-20 16:05:10 -07001298 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08001299 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07001300 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001301 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07001302 }
1303 }
1304
1305 public void onResult(Bundle result) {
1306 mNumResults++;
1307 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001308 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07001309 return;
1310 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001311 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07001312 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
1313 }
1314 mCurrentAccount++;
1315 checkAccount();
1316 }
1317
1318 public void sendResult() {
1319 IAccountManagerResponse response = getResponseAndClose();
1320 if (response != null) {
1321 try {
1322 Account[] accounts = new Account[mAccountsWithFeatures.size()];
1323 for (int i = 0; i < accounts.length; i++) {
1324 accounts[i] = mAccountsWithFeatures.get(i);
1325 }
Fred Quintana56285a62010-12-02 14:20:51 -08001326 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1327 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1328 + response);
1329 }
Fred Quintana33269202009-04-20 16:05:10 -07001330 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001331 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07001332 response.onResult(result);
1333 } catch (RemoteException e) {
1334 // if the caller is dead then there is no one to care about remote exceptions
1335 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1336 Log.v(TAG, "failure while notifying response", e);
1337 }
1338 }
1339 }
1340 }
1341
1342
1343 protected String toDebugString(long now) {
1344 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
1345 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1346 }
1347 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001348
1349 public Account[] getAccounts(String type) {
Fred Quintana56285a62010-12-02 14:20:51 -08001350 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1351 Log.v(TAG, "getAccounts: accountType " + type
1352 + ", caller's uid " + Binder.getCallingUid()
1353 + ", pid " + Binder.getCallingPid());
1354 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001355 checkReadAccountsPermission();
1356 long identityToken = clearCallingIdentity();
1357 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001358 synchronized (mCacheLock) {
1359 return getAccountsFromCacheLocked(type);
1360 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001361 } finally {
1362 restoreCallingIdentity(identityToken);
1363 }
1364 }
1365
1366 public void getAccountsByFeatures(IAccountManagerResponse response,
Fred Quintana33269202009-04-20 16:05:10 -07001367 String type, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -08001368 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1369 Log.v(TAG, "getAccounts: accountType " + type
1370 + ", response " + response
1371 + ", features " + stringArrayToString(features)
1372 + ", caller's uid " + Binder.getCallingUid()
1373 + ", pid " + Binder.getCallingPid());
1374 }
Fred Quintana382601f2010-03-25 12:25:10 -07001375 if (response == null) throw new IllegalArgumentException("response is null");
1376 if (type == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001377 checkReadAccountsPermission();
Fred Quintana33269202009-04-20 16:05:10 -07001378 long identityToken = clearCallingIdentity();
1379 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001380 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001381 Account[] accounts;
1382 synchronized (mCacheLock) {
1383 accounts = getAccountsFromCacheLocked(type);
1384 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001385 Bundle result = new Bundle();
1386 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
1387 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001388 return;
1389 }
Fred Quintana33269202009-04-20 16:05:10 -07001390 new GetAccountsByTypeAndFeatureSession(response, type, features).bind();
1391 } finally {
1392 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001393 }
1394 }
1395
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001396 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07001397 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001398 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07001399 try {
1400 if (cursor.moveToNext()) {
1401 return cursor.getLong(0);
1402 }
1403 return -1;
1404 } finally {
1405 cursor.close();
1406 }
1407 }
1408
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001409 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07001410 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
1411 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
1412 new String[]{key}, null, null, null);
1413 try {
1414 if (cursor.moveToNext()) {
1415 return cursor.getLong(0);
1416 }
1417 return -1;
1418 } finally {
1419 cursor.close();
1420 }
1421 }
1422
Fred Quintanaa698f422009-04-08 19:14:54 -07001423 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07001424 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07001425 IAccountManagerResponse mResponse;
1426 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07001427 final boolean mExpectActivityLaunch;
1428 final long mCreationTime;
1429
Fred Quintana33269202009-04-20 16:05:10 -07001430 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07001431 private int mNumRequestContinued = 0;
1432 private int mNumErrors = 0;
1433
Fred Quintana60307342009-03-24 22:48:12 -07001434
1435 IAccountAuthenticator mAuthenticator = null;
1436
Fred Quintana8570f742010-02-18 10:32:54 -08001437 private final boolean mStripAuthTokenFromResult;
1438
Fred Quintanaa698f422009-04-08 19:14:54 -07001439 public Session(IAccountManagerResponse response, String accountType,
Fred Quintana8570f742010-02-18 10:32:54 -08001440 boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
Fred Quintana60307342009-03-24 22:48:12 -07001441 super();
Fred Quintanaa698f422009-04-08 19:14:54 -07001442 if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07001443 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintana8570f742010-02-18 10:32:54 -08001444 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07001445 mResponse = response;
1446 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07001447 mExpectActivityLaunch = expectActivityLaunch;
1448 mCreationTime = SystemClock.elapsedRealtime();
1449 synchronized (mSessions) {
1450 mSessions.put(toString(), this);
1451 }
1452 try {
1453 response.asBinder().linkToDeath(this, 0 /* flags */);
1454 } catch (RemoteException e) {
1455 mResponse = null;
1456 binderDied();
1457 }
Fred Quintana60307342009-03-24 22:48:12 -07001458 }
1459
Fred Quintanaa698f422009-04-08 19:14:54 -07001460 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07001461 if (mResponse == null) {
1462 // this session has already been closed
1463 return null;
1464 }
Fred Quintana60307342009-03-24 22:48:12 -07001465 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07001466 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07001467 return response;
1468 }
1469
Fred Quintanaa698f422009-04-08 19:14:54 -07001470 private void close() {
1471 synchronized (mSessions) {
1472 if (mSessions.remove(toString()) == null) {
1473 // the session was already closed, so bail out now
1474 return;
1475 }
1476 }
1477 if (mResponse != null) {
1478 // stop listening for response deaths
1479 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
1480
1481 // clear this so that we don't accidentally send any further results
1482 mResponse = null;
1483 }
1484 cancelTimeout();
1485 unbind();
1486 }
1487
1488 public void binderDied() {
1489 mResponse = null;
1490 close();
1491 }
1492
1493 protected String toDebugString() {
1494 return toDebugString(SystemClock.elapsedRealtime());
1495 }
1496
1497 protected String toDebugString(long now) {
1498 return "Session: expectLaunch " + mExpectActivityLaunch
1499 + ", connected " + (mAuthenticator != null)
1500 + ", stats (" + mNumResults + "/" + mNumRequestContinued
1501 + "/" + mNumErrors + ")"
1502 + ", lifetime " + ((now - mCreationTime) / 1000.0);
1503 }
1504
Fred Quintana60307342009-03-24 22:48:12 -07001505 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001506 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1507 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
1508 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001509 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001510 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001511 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07001512 }
1513 }
1514
1515 private void unbind() {
1516 if (mAuthenticator != null) {
1517 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07001518 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07001519 }
1520 }
1521
1522 public void scheduleTimeout() {
1523 mMessageHandler.sendMessageDelayed(
1524 mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
1525 }
1526
1527 public void cancelTimeout() {
1528 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
1529 }
1530
Fred Quintanab839afc2009-10-14 15:57:28 -07001531 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07001532 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07001533 try {
1534 run();
1535 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001536 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07001537 "remote exception");
1538 }
Fred Quintana60307342009-03-24 22:48:12 -07001539 }
1540
Fred Quintanab839afc2009-10-14 15:57:28 -07001541 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001542 mAuthenticator = null;
1543 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001544 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001545 try {
1546 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
1547 "disconnected");
1548 } catch (RemoteException e) {
1549 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1550 Log.v(TAG, "Session.onServiceDisconnected: "
1551 + "caught RemoteException while responding", e);
1552 }
1553 }
Fred Quintana60307342009-03-24 22:48:12 -07001554 }
1555 }
1556
Fred Quintanab839afc2009-10-14 15:57:28 -07001557 public abstract void run() throws RemoteException;
1558
Fred Quintana60307342009-03-24 22:48:12 -07001559 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001560 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001561 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001562 try {
1563 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
1564 "timeout");
1565 } catch (RemoteException e) {
1566 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1567 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
1568 e);
1569 }
1570 }
Fred Quintana60307342009-03-24 22:48:12 -07001571 }
1572 }
1573
Fred Quintanaa698f422009-04-08 19:14:54 -07001574 public void onResult(Bundle result) {
1575 mNumResults++;
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001576 if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
1577 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1578 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001579 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
1580 Account account = new Account(accountName, accountType);
1581 cancelNotification(getSigninRequiredNotificationId(account));
1582 }
Fred Quintana60307342009-03-24 22:48:12 -07001583 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001584 IAccountManagerResponse response;
1585 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001586 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001587 response = mResponse;
1588 } else {
1589 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001590 }
Fred Quintana60307342009-03-24 22:48:12 -07001591 if (response != null) {
1592 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07001593 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001594 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1595 Log.v(TAG, getClass().getSimpleName()
1596 + " calling onError() on response " + response);
1597 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001598 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07001599 "null bundle returned");
1600 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08001601 if (mStripAuthTokenFromResult) {
1602 result.remove(AccountManager.KEY_AUTHTOKEN);
1603 }
Fred Quintana56285a62010-12-02 14:20:51 -08001604 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1605 Log.v(TAG, getClass().getSimpleName()
1606 + " calling onResult() on response " + response);
1607 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001608 response.onResult(result);
1609 }
Fred Quintana60307342009-03-24 22:48:12 -07001610 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001611 // if the caller is dead then there is no one to care about remote exceptions
1612 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1613 Log.v(TAG, "failure while notifying response", e);
1614 }
Fred Quintana60307342009-03-24 22:48:12 -07001615 }
1616 }
1617 }
Fred Quintana60307342009-03-24 22:48:12 -07001618
Fred Quintanaa698f422009-04-08 19:14:54 -07001619 public void onRequestContinued() {
1620 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07001621 }
1622
1623 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001624 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07001625 IAccountManagerResponse response = getResponseAndClose();
1626 if (response != null) {
1627 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08001628 Log.v(TAG, getClass().getSimpleName()
1629 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07001630 }
1631 try {
1632 response.onError(errorCode, errorMessage);
1633 } catch (RemoteException e) {
1634 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1635 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
1636 }
1637 }
1638 } else {
1639 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1640 Log.v(TAG, "Session.onError: already closed");
1641 }
Fred Quintana60307342009-03-24 22:48:12 -07001642 }
1643 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001644
1645 /**
1646 * find the component name for the authenticator and initiate a bind
1647 * if no authenticator or the bind fails then return false, otherwise return true
1648 */
1649 private boolean bindToAuthenticator(String authenticatorType) {
1650 AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
1651 mAuthenticatorCache.getServiceInfo(
1652 AuthenticatorDescription.newKey(authenticatorType));
1653 if (authenticatorInfo == null) {
1654 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1655 Log.v(TAG, "there is no authenticator for " + authenticatorType
1656 + ", bailing out");
1657 }
1658 return false;
1659 }
1660
1661 Intent intent = new Intent();
1662 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
1663 intent.setComponent(authenticatorInfo.componentName);
1664 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1665 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
1666 }
1667 if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
1668 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1669 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
1670 }
1671 return false;
1672 }
1673
1674
1675 return true;
1676 }
Fred Quintana60307342009-03-24 22:48:12 -07001677 }
1678
1679 private class MessageHandler extends Handler {
1680 MessageHandler(Looper looper) {
1681 super(looper);
1682 }
Costin Manolache3348f142009-09-29 18:58:36 -07001683
Fred Quintana60307342009-03-24 22:48:12 -07001684 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07001685 switch (msg.what) {
1686 case MESSAGE_TIMED_OUT:
1687 Session session = (Session)msg.obj;
1688 session.onTimedOut();
1689 break;
1690
1691 default:
1692 throw new IllegalStateException("unhandled message: " + msg.what);
1693 }
1694 }
1695 }
1696
Oscar Montemayora8529f62009-11-18 10:14:20 -08001697 private static String getDatabaseName() {
1698 if(Environment.isEncryptedFilesystemEnabled()) {
1699 // Hard-coded path in case of encrypted file system
1700 return Environment.getSystemSecureDirectory().getPath() + File.separator + DATABASE_NAME;
1701 } else {
1702 // Regular path in case of non-encrypted file system
1703 return DATABASE_NAME;
1704 }
1705 }
1706
Fred Quintana60307342009-03-24 22:48:12 -07001707 private class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08001708
Fred Quintana60307342009-03-24 22:48:12 -07001709 public DatabaseHelper(Context context) {
Oscar Montemayora8529f62009-11-18 10:14:20 -08001710 super(context, AccountManagerService.getDatabaseName(), null, DATABASE_VERSION);
Fred Quintana60307342009-03-24 22:48:12 -07001711 }
1712
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001713 /**
1714 * This call needs to be made while the mCacheLock is held. The way to
1715 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
1716 * @param db The database.
1717 */
Fred Quintana60307342009-03-24 22:48:12 -07001718 @Override
1719 public void onCreate(SQLiteDatabase db) {
1720 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
1721 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1722 + ACCOUNTS_NAME + " TEXT NOT NULL, "
1723 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
1724 + ACCOUNTS_PASSWORD + " TEXT, "
1725 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
1726
1727 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
1728 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1729 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
1730 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
1731 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
1732 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
1733
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001734 createGrantsTable(db);
1735
Fred Quintana60307342009-03-24 22:48:12 -07001736 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
1737 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1738 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
1739 + EXTRAS_KEY + " TEXT NOT NULL, "
1740 + EXTRAS_VALUE + " TEXT, "
1741 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
1742
1743 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
1744 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
1745 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07001746
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001747 createAccountsDeletionTrigger(db);
1748 }
1749
1750 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001751 db.execSQL(""
1752 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
1753 + " BEGIN"
1754 + " DELETE FROM " + TABLE_AUTHTOKENS
1755 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
1756 + " DELETE FROM " + TABLE_EXTRAS
1757 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001758 + " DELETE FROM " + TABLE_GRANTS
1759 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07001760 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07001761 }
1762
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001763 private void createGrantsTable(SQLiteDatabase db) {
1764 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
1765 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
1766 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
1767 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
1768 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
1769 + "," + GRANTS_GRANTEE_UID + "))");
1770 }
1771
Fred Quintana60307342009-03-24 22:48:12 -07001772 @Override
1773 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001774 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07001775
Fred Quintanaa698f422009-04-08 19:14:54 -07001776 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001777 // no longer need to do anything since the work is done
1778 // when upgrading from version 2
1779 oldVersion++;
1780 }
1781
1782 if (oldVersion == 2) {
1783 createGrantsTable(db);
1784 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
1785 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07001786 oldVersion++;
1787 }
Costin Manolache3348f142009-09-29 18:58:36 -07001788
1789 if (oldVersion == 3) {
1790 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
1791 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
1792 oldVersion++;
1793 }
Fred Quintana60307342009-03-24 22:48:12 -07001794 }
1795
1796 @Override
1797 public void onOpen(SQLiteDatabase db) {
1798 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
1799 }
1800 }
1801
1802 private void setMetaValue(String key, String value) {
1803 ContentValues values = new ContentValues();
1804 values.put(META_KEY, key);
1805 values.put(META_VALUE, value);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001806 synchronized (mCacheLock) {
1807 mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values);
1808 }
Fred Quintana60307342009-03-24 22:48:12 -07001809 }
1810
Fred Quintana60307342009-03-24 22:48:12 -07001811 public IBinder onBind(Intent intent) {
1812 return asBinder();
1813 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001814
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001815 /**
1816 * Searches array of arguments for the specified string
1817 * @param args array of argument strings
1818 * @param value value to search for
1819 * @return true if the value is contained in the array
1820 */
1821 private static boolean scanArgs(String[] args, String value) {
1822 if (args != null) {
1823 for (String arg : args) {
1824 if (value.equals(arg)) {
1825 return true;
1826 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001827 }
1828 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001829 return false;
1830 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001831
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001832 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07001833 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1834 != PackageManager.PERMISSION_GRANTED) {
1835 fout.println("Permission Denial: can't dump AccountsManager from from pid="
1836 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
1837 + " without permission " + android.Manifest.permission.DUMP);
1838 return;
1839 }
1840
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001841 synchronized (mCacheLock) {
1842 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Fred Quintanaa698f422009-04-08 19:14:54 -07001843
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001844 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001845
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001846 if (isCheckinRequest) {
1847 // This is a checkin request. *Only* upload the account types and the count of each.
1848 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
1849 null, null, ACCOUNTS_TYPE, null, null);
1850 try {
1851 while (cursor.moveToNext()) {
1852 // print type,count
1853 fout.println(cursor.getString(0) + "," + cursor.getString(1));
1854 }
1855 } finally {
1856 if (cursor != null) {
1857 cursor.close();
1858 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001859 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001860 } else {
1861 Account[] accounts = getAccountsFromCacheLocked(null /* type */);
1862 fout.println("Accounts: " + accounts.length);
1863 for (Account account : accounts) {
1864 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001865 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001866
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001867 fout.println();
1868 synchronized (mSessions) {
1869 final long now = SystemClock.elapsedRealtime();
1870 fout.println("Active Sessions: " + mSessions.size());
1871 for (Session session : mSessions.values()) {
1872 fout.println(" " + session.toDebugString(now));
1873 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001874 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001875
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001876 fout.println();
1877 mAuthenticatorCache.dump(fd, fout, args);
1878 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001879 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001880 }
1881
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001882 private void doNotification(Account account, CharSequence message, Intent intent) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001883 long identityToken = clearCallingIdentity();
1884 try {
1885 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1886 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
1887 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001888
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001889 if (intent.getComponent() != null &&
1890 GrantCredentialsPermissionActivity.class.getName().equals(
1891 intent.getComponent().getClassName())) {
1892 createNoCredentialsPermissionNotification(account, intent);
1893 } else {
Fred Quintana33f889a2009-09-14 17:31:26 -07001894 final Integer notificationId = getSigninRequiredNotificationId(account);
1895 intent.addCategory(String.valueOf(notificationId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001896 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
1897 0 /* when */);
Fred Quintana33f889a2009-09-14 17:31:26 -07001898 final String notificationTitleFormat =
1899 mContext.getText(R.string.notification_title).toString();
1900 n.setLatestEventInfo(mContext,
1901 String.format(notificationTitleFormat, account.name),
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001902 message, PendingIntent.getActivity(
1903 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
Fred Quintana56285a62010-12-02 14:20:51 -08001904 installNotification(notificationId, n);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001905 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001906 } finally {
1907 restoreCallingIdentity(identityToken);
1908 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001909 }
1910
Fred Quintana56285a62010-12-02 14:20:51 -08001911 protected void installNotification(final int notificationId, final Notification n) {
1912 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
1913 .notify(notificationId, n);
1914 }
1915
1916 protected void cancelNotification(int id) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001917 long identityToken = clearCallingIdentity();
1918 try {
1919 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001920 .cancel(id);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001921 } finally {
1922 restoreCallingIdentity(identityToken);
1923 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001924 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001925
Fred Quintanab38eb142010-02-24 13:40:54 -08001926 /** Succeeds if any of the specified permissions are granted. */
1927 private void checkBinderPermission(String... permissions) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001928 final int uid = Binder.getCallingUid();
Fred Quintanab38eb142010-02-24 13:40:54 -08001929
1930 for (String perm : permissions) {
1931 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
1932 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08001933 Log.v(TAG, " caller uid " + uid + " has " + perm);
Fred Quintanab38eb142010-02-24 13:40:54 -08001934 }
1935 return;
1936 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001937 }
Fred Quintanab38eb142010-02-24 13:40:54 -08001938
1939 String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
Fred Quintana56285a62010-12-02 14:20:51 -08001940 Log.w(TAG, " " + msg);
Fred Quintanab38eb142010-02-24 13:40:54 -08001941 throw new SecurityException(msg);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001942 }
1943
Fred Quintana7be59642009-08-24 18:29:25 -07001944 private boolean inSystemImage(int callerUid) {
Fred Quintana56285a62010-12-02 14:20:51 -08001945 String[] packages = mPackageManager.getPackagesForUid(callerUid);
Fred Quintana7be59642009-08-24 18:29:25 -07001946 for (String name : packages) {
1947 try {
Fred Quintana56285a62010-12-02 14:20:51 -08001948 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
1949 if (packageInfo != null
1950 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07001951 return true;
1952 }
1953 } catch (PackageManager.NameNotFoundException e) {
1954 return false;
1955 }
1956 }
1957 return false;
1958 }
1959
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001960 private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
Fred Quintanab839afc2009-10-14 15:57:28 -07001961 final boolean inSystemImage = inSystemImage(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07001962 final boolean fromAuthenticator = account != null
1963 && hasAuthenticatorUid(account.type, callerUid);
1964 final boolean hasExplicitGrants = account != null
1965 && hasExplicitlyGrantedPermission(account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001966 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1967 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08001968 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001969 + ": is authenticator? " + fromAuthenticator
1970 + ", has explicit permission? " + hasExplicitGrants);
1971 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001972 return fromAuthenticator || hasExplicitGrants || inSystemImage;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001973 }
1974
Fred Quintana1a231912009-10-15 11:31:30 -07001975 private boolean hasAuthenticatorUid(String accountType, int callingUid) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001976 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
1977 mAuthenticatorCache.getAllServices()) {
1978 if (serviceInfo.type.type.equals(accountType)) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001979 return (serviceInfo.uid == callingUid) ||
Fred Quintana56285a62010-12-02 14:20:51 -08001980 (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001981 == PackageManager.SIGNATURE_MATCH);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001982 }
1983 }
1984 return false;
1985 }
1986
1987 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType) {
1988 if (Binder.getCallingUid() == android.os.Process.SYSTEM_UID) {
1989 return true;
1990 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001991 synchronized (mCacheLock) {
1992 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1993 String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType,
1994 account.name, account.type};
1995 final boolean permissionGranted =
1996 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
1997 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
1998 // TODO: Skip this check when running automated tests. Replace this
1999 // with a more general solution.
2000 Log.d(TAG, "no credentials permission for usage of " + account + ", "
2001 + authTokenType + " by uid " + Binder.getCallingUid()
2002 + " but ignoring since device is in test harness.");
2003 return true;
2004 }
2005 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002006 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002007 }
2008
2009 private void checkCallingUidAgainstAuthenticator(Account account) {
2010 final int uid = Binder.getCallingUid();
Fred Quintana31957f12009-10-21 13:43:10 -07002011 if (account == null || !hasAuthenticatorUid(account.type, uid)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002012 String msg = "caller uid " + uid + " is different than the authenticator's uid";
2013 Log.w(TAG, msg);
2014 throw new SecurityException(msg);
2015 }
2016 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2017 Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
2018 }
2019 }
2020
2021 private void checkAuthenticateAccountsPermission(Account account) {
2022 checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
2023 checkCallingUidAgainstAuthenticator(account);
2024 }
2025
2026 private void checkReadAccountsPermission() {
2027 checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
2028 }
2029
2030 private void checkManageAccountsPermission() {
2031 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
2032 }
2033
Fred Quintanab38eb142010-02-24 13:40:54 -08002034 private void checkManageAccountsOrUseCredentialsPermissions() {
2035 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
2036 Manifest.permission.USE_CREDENTIALS);
2037 }
2038
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002039 /**
2040 * Allow callers with the given uid permission to get credentials for account/authTokenType.
2041 * <p>
2042 * Although this is public it can only be accessed via the AccountManagerService object
2043 * which is in the system. This means we don't need to protect it with permissions.
2044 * @hide
2045 */
2046 public void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07002047 if (account == null || authTokenType == null) {
2048 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07002049 return;
2050 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002051 synchronized (mCacheLock) {
2052 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2053 db.beginTransaction();
2054 try {
2055 long accountId = getAccountIdLocked(db, account);
2056 if (accountId >= 0) {
2057 ContentValues values = new ContentValues();
2058 values.put(GRANTS_ACCOUNTS_ID, accountId);
2059 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
2060 values.put(GRANTS_GRANTEE_UID, uid);
2061 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
2062 db.setTransactionSuccessful();
2063 }
2064 } finally {
2065 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002066 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002067 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002068 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002069 }
2070
2071 /**
2072 * Don't allow callers with the given uid permission to get credentials for
2073 * account/authTokenType.
2074 * <p>
2075 * Although this is public it can only be accessed via the AccountManagerService object
2076 * which is in the system. This means we don't need to protect it with permissions.
2077 * @hide
2078 */
2079 public void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07002080 if (account == null || authTokenType == null) {
2081 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07002082 return;
2083 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002084 synchronized (mCacheLock) {
2085 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2086 db.beginTransaction();
2087 try {
2088 long accountId = getAccountIdLocked(db, account);
2089 if (accountId >= 0) {
2090 db.delete(TABLE_GRANTS,
2091 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
2092 + GRANTS_GRANTEE_UID + "=?",
2093 new String[]{String.valueOf(accountId), authTokenType,
2094 String.valueOf(uid)});
2095 db.setTransactionSuccessful();
2096 }
2097 } finally {
2098 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002099 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002100 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002101 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002102 }
Fred Quintana56285a62010-12-02 14:20:51 -08002103
2104 static final private String stringArrayToString(String[] value) {
2105 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
2106 }
2107
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002108 private void removeAccountFromCacheLocked(Account account) {
2109 final Account[] oldAccountsForType = mAccountCache.get(account.type);
2110 if (oldAccountsForType != null) {
2111 ArrayList<Account> newAccountsList = new ArrayList<Account>();
2112 for (Account curAccount : oldAccountsForType) {
2113 if (!curAccount.equals(account)) {
2114 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08002115 }
2116 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002117 if (newAccountsList.isEmpty()) {
2118 mAccountCache.remove(account.type);
2119 } else {
2120 Account[] newAccountsForType = new Account[newAccountsList.size()];
2121 newAccountsForType = newAccountsList.toArray(newAccountsForType);
2122 mAccountCache.put(account.type, newAccountsForType);
2123 }
Fred Quintana56285a62010-12-02 14:20:51 -08002124 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002125 mUserDataCache.remove(account);
2126 mAuthTokenCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08002127 }
2128
2129 /**
2130 * This assumes that the caller has already checked that the account is not already present.
2131 */
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002132 private void insertAccountIntoCacheLocked(Account account) {
2133 Account[] accountsForType = mAccountCache.get(account.type);
2134 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
2135 Account[] newAccountsForType = new Account[oldLength + 1];
2136 if (accountsForType != null) {
2137 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08002138 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002139 newAccountsForType[oldLength] = account;
2140 mAccountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08002141 }
2142
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002143 protected Account[] getAccountsFromCacheLocked(String accountType) {
2144 if (accountType != null) {
2145 final Account[] accounts = mAccountCache.get(accountType);
2146 if (accounts == null) {
2147 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08002148 } else {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002149 return Arrays.copyOf(accounts, accounts.length);
Fred Quintana56285a62010-12-02 14:20:51 -08002150 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002151 } else {
2152 int totalLength = 0;
2153 for (Account[] accounts : mAccountCache.values()) {
2154 totalLength += accounts.length;
2155 }
2156 if (totalLength == 0) {
2157 return EMPTY_ACCOUNT_ARRAY;
2158 }
2159 Account[] accounts = new Account[totalLength];
2160 totalLength = 0;
2161 for (Account[] accountsOfType : mAccountCache.values()) {
2162 System.arraycopy(accountsOfType, 0, accounts, totalLength,
2163 accountsOfType.length);
2164 totalLength += accountsOfType.length;
2165 }
2166 return accounts;
Fred Quintana56285a62010-12-02 14:20:51 -08002167 }
2168 }
2169
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002170 protected void writeUserDataIntoCacheLocked(final SQLiteDatabase db, Account account,
2171 String key, String value) {
2172 HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
2173 if (userDataForAccount == null) {
2174 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
2175 mUserDataCache.put(account, userDataForAccount);
2176 }
2177 if (value == null) {
2178 userDataForAccount.remove(key);
2179 } else {
2180 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08002181 }
2182 }
2183
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002184 protected void writeAuthTokenIntoCacheLocked(final SQLiteDatabase db, Account account,
2185 String key, String value) {
2186 HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
2187 if (authTokensForAccount == null) {
2188 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
2189 mAuthTokenCache.put(account, authTokensForAccount);
2190 }
2191 if (value == null) {
2192 authTokensForAccount.remove(key);
2193 } else {
2194 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08002195 }
2196 }
2197
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002198 protected String readAuthTokenInternal(Account account, String authTokenType) {
Fred Quintana56285a62010-12-02 14:20:51 -08002199 synchronized (mCacheLock) {
2200 HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
2201 if (authTokensForAccount == null) {
2202 // need to populate the cache for this account
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002203 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2204 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Fred Quintana56285a62010-12-02 14:20:51 -08002205 mAuthTokenCache.put(account, authTokensForAccount);
2206 }
2207 return authTokensForAccount.get(authTokenType);
2208 }
2209 }
2210
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002211 protected String readUserDataInternal(Account account, String key) {
Fred Quintana56285a62010-12-02 14:20:51 -08002212 synchronized (mCacheLock) {
2213 HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
2214 if (userDataForAccount == null) {
2215 // need to populate the cache for this account
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002216 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2217 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Fred Quintana56285a62010-12-02 14:20:51 -08002218 mUserDataCache.put(account, userDataForAccount);
2219 }
2220 return userDataForAccount.get(key);
2221 }
2222 }
2223
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002224 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
2225 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08002226 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08002227 Cursor cursor = db.query(TABLE_EXTRAS,
2228 COLUMNS_EXTRAS_KEY_AND_VALUE,
2229 SELECTION_USERDATA_BY_ACCOUNT,
2230 new String[]{account.name, account.type},
2231 null, null, null);
2232 try {
2233 while (cursor.moveToNext()) {
2234 final String tmpkey = cursor.getString(0);
2235 final String value = cursor.getString(1);
2236 userDataForAccount.put(tmpkey, value);
2237 }
2238 } finally {
2239 cursor.close();
2240 }
2241 return userDataForAccount;
2242 }
2243
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002244 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
2245 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08002246 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08002247 Cursor cursor = db.query(TABLE_AUTHTOKENS,
2248 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
2249 SELECTION_AUTHTOKENS_BY_ACCOUNT,
2250 new String[]{account.name, account.type},
2251 null, null, null);
2252 try {
2253 while (cursor.moveToNext()) {
2254 final String type = cursor.getString(0);
2255 final String authToken = cursor.getString(1);
2256 authTokensForAccount.put(type, authToken);
2257 }
2258 } finally {
2259 cursor.close();
2260 }
2261 return authTokensForAccount;
2262 }
Fred Quintana60307342009-03-24 22:48:12 -07002263}