blob: 173da8d41e57ad345b996e1a507bce3f15d31f1c [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;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070065import java.util.concurrent.atomic.AtomicInteger;
66import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -070067
Fred Quintana60307342009-03-24 22:48:12 -070068/**
69 * A system service that provides account, password, and authtoken management for all
70 * accounts on the device. Some of these calls are implemented with the help of the corresponding
71 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
72 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -070073 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -070074 * @hide
Fred Quintana60307342009-03-24 22:48:12 -070075 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -070076public class AccountManagerService
77 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080078 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -070079 private static final String TAG = "AccountManagerService";
80
81 private static final int TIMEOUT_DELAY_MS = 1000 * 60;
82 private static final String DATABASE_NAME = "accounts.db";
Costin Manolache3348f142009-09-29 18:58:36 -070083 private static final int DATABASE_VERSION = 4;
Fred Quintana60307342009-03-24 22:48:12 -070084
85 private final Context mContext;
86
Fred Quintana56285a62010-12-02 14:20:51 -080087 private final PackageManager mPackageManager;
88
Fred Quintana60307342009-03-24 22:48:12 -070089 private HandlerThread mMessageThread;
90 private final MessageHandler mMessageHandler;
91
92 // Messages that can be sent on mHandler
93 private static final int MESSAGE_TIMED_OUT = 3;
Fred Quintana60307342009-03-24 22:48:12 -070094
Fred Quintana56285a62010-12-02 14:20:51 -080095 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -070096 private final DatabaseHelper mOpenHelper;
Fred Quintana60307342009-03-24 22:48:12 -070097
98 private static final String TABLE_ACCOUNTS = "accounts";
99 private static final String ACCOUNTS_ID = "_id";
100 private static final String ACCOUNTS_NAME = "name";
101 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700102 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700103 private static final String ACCOUNTS_PASSWORD = "password";
104
105 private static final String TABLE_AUTHTOKENS = "authtokens";
106 private static final String AUTHTOKENS_ID = "_id";
107 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
108 private static final String AUTHTOKENS_TYPE = "type";
109 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
110
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700111 private static final String TABLE_GRANTS = "grants";
112 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
113 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
114 private static final String GRANTS_GRANTEE_UID = "uid";
115
Fred Quintana60307342009-03-24 22:48:12 -0700116 private static final String TABLE_EXTRAS = "extras";
117 private static final String EXTRAS_ID = "_id";
118 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
119 private static final String EXTRAS_KEY = "key";
120 private static final String EXTRAS_VALUE = "value";
121
122 private static final String TABLE_META = "meta";
123 private static final String META_KEY = "key";
124 private static final String META_VALUE = "value";
125
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700126 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
127 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700128 private static final Intent ACCOUNTS_CHANGED_INTENT;
Fred Quintanaa698f422009-04-08 19:14:54 -0700129
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700130 private static final String COUNT_OF_MATCHING_GRANTS = ""
131 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
132 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
133 + " AND " + GRANTS_GRANTEE_UID + "=?"
134 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
135 + " AND " + ACCOUNTS_NAME + "=?"
136 + " AND " + ACCOUNTS_TYPE + "=?";
137
Fred Quintana56285a62010-12-02 14:20:51 -0800138 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
139 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
140 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
141 AUTHTOKENS_AUTHTOKEN};
142
143 private static final String SELECTION_USERDATA_BY_ACCOUNT =
144 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
145 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
146
Fred Quintanaa698f422009-04-08 19:14:54 -0700147 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700148 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
149
150 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
151 mCredentialsPermissionNotificationIds =
152 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
153 private final HashMap<Account, Integer> mSigninRequiredNotificationIds =
154 new HashMap<Account, Integer>();
155 private static AtomicReference<AccountManagerService> sThis =
156 new AtomicReference<AccountManagerService>();
157
Fred Quintana31957f12009-10-21 13:43:10 -0700158 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700159
160 static {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700161 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
Fred Quintana7be59642009-08-24 18:29:25 -0700162 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
163 }
164
Fred Quintana56285a62010-12-02 14:20:51 -0800165 private final Object mCacheLock = new Object();
166 /** protected by the {@link #mCacheLock} */
167 private final HashMap<String, Account[]> mAccountCache = new HashMap<String, Account[]>();
168 /** protected by the {@link #mCacheLock} */
169 private HashMap<Account, HashMap<String, String>> mUserDataCache =
170 new HashMap<Account, HashMap<String, String>>();
171 /** protected by the {@link #mCacheLock} */
172 private HashMap<Account, HashMap<String, String>> mAuthTokenCache =
173 new HashMap<Account, HashMap<String, String>>();
174
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700175 /**
176 * This should only be called by system code. One should only call this after the service
177 * has started.
178 * @return a reference to the AccountManagerService instance
179 * @hide
180 */
181 public static AccountManagerService getSingleton() {
182 return sThis.get();
183 }
Fred Quintana60307342009-03-24 22:48:12 -0700184
Fred Quintana56285a62010-12-02 14:20:51 -0800185 public AccountManagerService(Context context) {
186 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700187 }
188
Fred Quintana56285a62010-12-02 14:20:51 -0800189 public AccountManagerService(Context context, PackageManager packageManager,
190 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700191 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800192 mPackageManager = packageManager;
Fred Quintana60307342009-03-24 22:48:12 -0700193
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800194 synchronized (mCacheLock) {
195 mOpenHelper = new DatabaseHelper(mContext);
196 }
Fred Quintana60307342009-03-24 22:48:12 -0700197
198 mMessageThread = new HandlerThread("AccountManagerService");
199 mMessageThread.start();
200 mMessageHandler = new MessageHandler(mMessageThread.getLooper());
201
Fred Quintana56285a62010-12-02 14:20:51 -0800202 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800203 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700204
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700205 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800206
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800207 IntentFilter intentFilter = new IntentFilter();
208 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
209 intentFilter.addDataScheme("package");
210 mContext.registerReceiver(new BroadcastReceiver() {
211 @Override
212 public void onReceive(Context context1, Intent intent) {
213 purgeOldGrants();
214 }
215 }, intentFilter);
216 purgeOldGrants();
217
Fred Quintana56285a62010-12-02 14:20:51 -0800218 validateAccountsAndPopulateCache();
Fred Quintanaafa92b82009-12-01 16:27:03 -0800219 }
220
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800221 private void purgeOldGrants() {
222 synchronized (mCacheLock) {
223 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
224 final Cursor cursor = db.query(TABLE_GRANTS,
225 new String[]{GRANTS_GRANTEE_UID},
226 null, null, GRANTS_GRANTEE_UID, null, null);
227 try {
228 while (cursor.moveToNext()) {
229 final int uid = cursor.getInt(0);
230 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
231 if (packageExists) {
232 continue;
233 }
234 Log.d(TAG, "deleting grants for UID " + uid
235 + " because its package is no longer installed");
236 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
237 new String[]{Integer.toString(uid)});
238 }
239 } finally {
240 cursor.close();
241 }
242 }
243 }
244
Fred Quintana56285a62010-12-02 14:20:51 -0800245 private void validateAccountsAndPopulateCache() {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800246 synchronized (mCacheLock) {
247 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
248 boolean accountDeleted = false;
249 Cursor cursor = db.query(TABLE_ACCOUNTS,
250 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
251 null, null, null, null, null);
252 try {
Fred Quintana56285a62010-12-02 14:20:51 -0800253 mAccountCache.clear();
254 final HashMap<String, ArrayList<String>> accountNamesByType =
255 new HashMap<String, ArrayList<String>>();
256 while (cursor.moveToNext()) {
257 final long accountId = cursor.getLong(0);
258 final String accountType = cursor.getString(1);
259 final String accountName = cursor.getString(2);
260 if (mAuthenticatorCache.getServiceInfo(
261 AuthenticatorDescription.newKey(accountType)) == null) {
262 Log.d(TAG, "deleting account " + accountName + " because type "
263 + accountType + " no longer has a registered authenticator");
264 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
265 accountDeleted = true;
266 final Account account = new Account(accountName, accountType);
267 mUserDataCache.remove(account);
268 mAuthTokenCache.remove(account);
269 } else {
270 ArrayList<String> accountNames = accountNamesByType.get(accountType);
271 if (accountNames == null) {
272 accountNames = new ArrayList<String>();
273 accountNamesByType.put(accountType, accountNames);
274 }
275 accountNames.add(accountName);
276 }
277 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800278 for (HashMap.Entry<String, ArrayList<String>> cur
279 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800280 final String accountType = cur.getKey();
281 final ArrayList<String> accountNames = cur.getValue();
282 final Account[] accountsForType = new Account[accountNames.size()];
283 int i = 0;
284 for (String accountName : accountNames) {
285 accountsForType[i] = new Account(accountName, accountType);
286 ++i;
287 }
288 mAccountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800289 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800290 } finally {
291 cursor.close();
292 if (accountDeleted) {
293 sendAccountsChangedBroadcast();
294 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800295 }
296 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700297 }
298
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800299 public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
Fred Quintana56285a62010-12-02 14:20:51 -0800300 validateAccountsAndPopulateCache();
Fred Quintana60307342009-03-24 22:48:12 -0700301 }
302
Fred Quintanaa698f422009-04-08 19:14:54 -0700303 public String getPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800304 if (Log.isLoggable(TAG, Log.VERBOSE)) {
305 Log.v(TAG, "getPassword: " + account
306 + ", caller's uid " + Binder.getCallingUid()
307 + ", pid " + Binder.getCallingPid());
308 }
Fred Quintana382601f2010-03-25 12:25:10 -0700309 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700310 checkAuthenticateAccountsPermission(account);
311
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700312 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700313 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800314 return readPasswordInternal(account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700315 } finally {
316 restoreCallingIdentity(identityToken);
317 }
318 }
319
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800320 private String readPasswordInternal(Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700321 if (account == null) {
322 return null;
323 }
324
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800325 synchronized (mCacheLock) {
326 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
327 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
328 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
329 new String[]{account.name, account.type}, null, null, null);
330 try {
331 if (cursor.moveToNext()) {
332 return cursor.getString(0);
333 }
334 return null;
335 } finally {
336 cursor.close();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700337 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700338 }
339 }
340
341 public String getUserData(Account account, String key) {
Fred Quintana56285a62010-12-02 14:20:51 -0800342 if (Log.isLoggable(TAG, Log.VERBOSE)) {
343 Log.v(TAG, "getUserData: " + account
344 + ", key " + key
345 + ", caller's uid " + Binder.getCallingUid()
346 + ", pid " + Binder.getCallingPid());
347 }
Fred Quintana382601f2010-03-25 12:25:10 -0700348 if (account == null) throw new IllegalArgumentException("account is null");
349 if (key == null) throw new IllegalArgumentException("key is null");
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700350 checkAuthenticateAccountsPermission(account);
351 long identityToken = clearCallingIdentity();
352 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800353 return readUserDataInternal(account, key);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700354 } finally {
355 restoreCallingIdentity(identityToken);
356 }
357 }
358
Fred Quintana97889762009-06-15 12:29:24 -0700359 public AuthenticatorDescription[] getAuthenticatorTypes() {
Fred Quintana56285a62010-12-02 14:20:51 -0800360 if (Log.isLoggable(TAG, Log.VERBOSE)) {
361 Log.v(TAG, "getAuthenticatorTypes: "
362 + "caller's uid " + Binder.getCallingUid()
363 + ", pid " + Binder.getCallingPid());
364 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700365 long identityToken = clearCallingIdentity();
366 try {
Fred Quintana97889762009-06-15 12:29:24 -0700367 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
368 authenticatorCollection = mAuthenticatorCache.getAllServices();
369 AuthenticatorDescription[] types =
370 new AuthenticatorDescription[authenticatorCollection.size()];
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700371 int i = 0;
Fred Quintana97889762009-06-15 12:29:24 -0700372 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
Fred Quintana718d8a22009-04-29 17:53:20 -0700373 : authenticatorCollection) {
374 types[i] = authenticator.type;
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700375 i++;
376 }
377 return types;
378 } finally {
379 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700380 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700381 }
382
Fred Quintanaa698f422009-04-08 19:14:54 -0700383 public boolean addAccount(Account account, String password, Bundle extras) {
Fred Quintana56285a62010-12-02 14:20:51 -0800384 if (Log.isLoggable(TAG, Log.VERBOSE)) {
385 Log.v(TAG, "addAccount: " + account
386 + ", caller's uid " + Binder.getCallingUid()
387 + ", pid " + Binder.getCallingPid());
388 }
Fred Quintana382601f2010-03-25 12:25:10 -0700389 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700390 checkAuthenticateAccountsPermission(account);
391
Fred Quintana60307342009-03-24 22:48:12 -0700392 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700393 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700394 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800395 return addAccountInternal(account, password, extras);
Fred Quintana60307342009-03-24 22:48:12 -0700396 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700397 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700398 }
399 }
400
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800401 private boolean addAccountInternal(Account account, String password, Bundle extras) {
Fred Quintana743dfad2010-07-15 10:59:25 -0700402 if (account == null) {
403 return false;
404 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800405 synchronized (mCacheLock) {
406 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
407 db.beginTransaction();
408 try {
409 long numMatches = DatabaseUtils.longForQuery(db,
410 "select count(*) from " + TABLE_ACCOUNTS
411 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
412 new String[]{account.name, account.type});
413 if (numMatches > 0) {
414 Log.w(TAG, "insertAccountIntoDatabase: " + account
415 + ", skipping since the account already exists");
416 return false;
417 }
418 ContentValues values = new ContentValues();
419 values.put(ACCOUNTS_NAME, account.name);
420 values.put(ACCOUNTS_TYPE, account.type);
421 values.put(ACCOUNTS_PASSWORD, password);
422 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
423 if (accountId < 0) {
424 Log.w(TAG, "insertAccountIntoDatabase: " + account
425 + ", skipping the DB insert failed");
426 return false;
427 }
428 if (extras != null) {
429 for (String key : extras.keySet()) {
430 final String value = extras.getString(key);
431 if (insertExtraLocked(db, accountId, key, value) < 0) {
432 Log.w(TAG, "insertAccountIntoDatabase: " + account
433 + ", skipping since insertExtra failed for key " + key);
434 return false;
435 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700436 }
437 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800438 db.setTransactionSuccessful();
439 insertAccountIntoCacheLocked(account);
440 } finally {
441 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700442 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800443 sendAccountsChangedBroadcast();
444 return true;
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700445 }
446 }
447
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800448 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -0700449 ContentValues values = new ContentValues();
450 values.put(EXTRAS_KEY, key);
451 values.put(EXTRAS_ACCOUNTS_ID, accountId);
452 values.put(EXTRAS_VALUE, value);
453 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
454 }
455
Fred Quintana3084a6f2010-01-14 18:02:03 -0800456 public void hasFeatures(IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800457 Account account, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -0800458 if (Log.isLoggable(TAG, Log.VERBOSE)) {
459 Log.v(TAG, "hasFeatures: " + account
460 + ", response " + response
461 + ", features " + stringArrayToString(features)
462 + ", caller's uid " + Binder.getCallingUid()
463 + ", pid " + Binder.getCallingPid());
464 }
Fred Quintana382601f2010-03-25 12:25:10 -0700465 if (response == null) throw new IllegalArgumentException("response is null");
466 if (account == null) throw new IllegalArgumentException("account is null");
467 if (features == null) throw new IllegalArgumentException("features is null");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800468 checkReadAccountsPermission();
469 long identityToken = clearCallingIdentity();
470 try {
471 new TestFeaturesSession(response, account, features).bind();
472 } finally {
473 restoreCallingIdentity(identityToken);
474 }
475 }
476
477 private class TestFeaturesSession extends Session {
478 private final String[] mFeatures;
479 private final Account mAccount;
480
481 public TestFeaturesSession(IAccountManagerResponse response,
482 Account account, String[] features) {
Fred Quintana8570f742010-02-18 10:32:54 -0800483 super(response, account.type, false /* expectActivityLaunch */,
484 true /* stripAuthTokenFromResult */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800485 mFeatures = features;
486 mAccount = account;
487 }
488
489 public void run() throws RemoteException {
490 try {
491 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
492 } catch (RemoteException e) {
493 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
494 }
495 }
496
497 public void onResult(Bundle result) {
498 IAccountManagerResponse response = getResponseAndClose();
499 if (response != null) {
500 try {
501 if (result == null) {
502 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
503 return;
504 }
Fred Quintana56285a62010-12-02 14:20:51 -0800505 if (Log.isLoggable(TAG, Log.VERBOSE)) {
506 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
507 + response);
508 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800509 final Bundle newResult = new Bundle();
510 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
511 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
512 response.onResult(newResult);
513 } catch (RemoteException e) {
514 // if the caller is dead then there is no one to care about remote exceptions
515 if (Log.isLoggable(TAG, Log.VERBOSE)) {
516 Log.v(TAG, "failure while notifying response", e);
517 }
518 }
519 }
520 }
521
522 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -0800523 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800524 + ", " + mAccount
525 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
526 }
527 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800528
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700529 public void removeAccount(IAccountManagerResponse response, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800530 if (Log.isLoggable(TAG, Log.VERBOSE)) {
531 Log.v(TAG, "removeAccount: " + account
532 + ", response " + response
533 + ", caller's uid " + Binder.getCallingUid()
534 + ", pid " + Binder.getCallingPid());
535 }
Fred Quintana382601f2010-03-25 12:25:10 -0700536 if (response == null) throw new IllegalArgumentException("response is null");
537 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700538 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700539 long identityToken = clearCallingIdentity();
Costin Manolacheec0c4f42010-11-16 09:57:28 -0800540
541 cancelNotification(getSigninRequiredNotificationId(account));
542 synchronized(mCredentialsPermissionNotificationIds) {
543 for (Pair<Pair<Account, String>, Integer> pair:
544 mCredentialsPermissionNotificationIds.keySet()) {
545 if (account.equals(pair.first.first)) {
546 int id = mCredentialsPermissionNotificationIds.get(pair);
547 cancelNotification(id);
548 }
549 }
550 }
551
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700552 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700553 new RemoveAccountSession(response, account).bind();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700554 } finally {
555 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700556 }
Fred Quintana60307342009-03-24 22:48:12 -0700557 }
558
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700559 private class RemoveAccountSession extends Session {
560 final Account mAccount;
561 public RemoveAccountSession(IAccountManagerResponse response, Account account) {
Fred Quintana8570f742010-02-18 10:32:54 -0800562 super(response, account.type, false /* expectActivityLaunch */,
563 true /* stripAuthTokenFromResult */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700564 mAccount = account;
565 }
566
567 protected String toDebugString(long now) {
568 return super.toDebugString(now) + ", removeAccount"
569 + ", account " + mAccount;
570 }
571
572 public void run() throws RemoteException {
573 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
574 }
575
576 public void onResult(Bundle result) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700577 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
578 && !result.containsKey(AccountManager.KEY_INTENT)) {
579 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700580 if (removalAllowed) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800581 removeAccountInternal(mAccount);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700582 }
583 IAccountManagerResponse response = getResponseAndClose();
584 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -0800585 if (Log.isLoggable(TAG, Log.VERBOSE)) {
586 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
587 + response);
588 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700589 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700590 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700591 try {
592 response.onResult(result2);
593 } catch (RemoteException e) {
594 // ignore
595 }
596 }
597 }
598 super.onResult(result);
599 }
600 }
601
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800602 protected void removeAccountInternal(Account account) {
603 synchronized (mCacheLock) {
604 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
605 db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
606 new String[]{account.name, account.type});
607 removeAccountFromCacheLocked(account);
608 sendAccountsChangedBroadcast();
609 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700610 }
611
Fred Quintanaa698f422009-04-08 19:14:54 -0700612 public void invalidateAuthToken(String accountType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -0800613 if (Log.isLoggable(TAG, Log.VERBOSE)) {
614 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
615 + ", caller's uid " + Binder.getCallingUid()
616 + ", pid " + Binder.getCallingPid());
617 }
Fred Quintana382601f2010-03-25 12:25:10 -0700618 if (accountType == null) throw new IllegalArgumentException("accountType is null");
619 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Fred Quintanab38eb142010-02-24 13:40:54 -0800620 checkManageAccountsOrUseCredentialsPermissions();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700621 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700622 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800623 synchronized (mCacheLock) {
624 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
625 db.beginTransaction();
626 try {
627 invalidateAuthTokenLocked(db, accountType, authToken);
628 db.setTransactionSuccessful();
629 } finally {
630 db.endTransaction();
631 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700632 }
Fred Quintana60307342009-03-24 22:48:12 -0700633 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700634 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700635 }
636 }
637
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800638 private void invalidateAuthTokenLocked(SQLiteDatabase db, String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700639 if (authToken == null || accountType == null) {
640 return;
641 }
Fred Quintana33269202009-04-20 16:05:10 -0700642 Cursor cursor = db.rawQuery(
643 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
644 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
645 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
646 + " FROM " + TABLE_ACCOUNTS
647 + " JOIN " + TABLE_AUTHTOKENS
648 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
649 + " = " + AUTHTOKENS_ACCOUNTS_ID
650 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
651 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
652 new String[]{authToken, accountType});
653 try {
654 while (cursor.moveToNext()) {
655 long authTokenId = cursor.getLong(0);
656 String accountName = cursor.getString(1);
657 String authTokenType = cursor.getString(2);
658 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800659 writeAuthTokenIntoCacheLocked(db, new Account(accountName, accountType),
660 authTokenType, null);
Fred Quintana60307342009-03-24 22:48:12 -0700661 }
Fred Quintana33269202009-04-20 16:05:10 -0700662 } finally {
663 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -0700664 }
665 }
666
667 private boolean saveAuthTokenToDatabase(Account account, String type, String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -0700668 if (account == null || type == null) {
669 return false;
670 }
Fred Quintana6dfd1382009-09-16 17:32:42 -0700671 cancelNotification(getSigninRequiredNotificationId(account));
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800672 synchronized (mCacheLock) {
673 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
674 db.beginTransaction();
675 try {
676 long accountId = getAccountIdLocked(db, account);
677 if (accountId < 0) {
678 return false;
679 }
680 db.delete(TABLE_AUTHTOKENS,
681 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
682 new String[]{type});
683 ContentValues values = new ContentValues();
684 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
685 values.put(AUTHTOKENS_TYPE, type);
686 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
687 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
688 db.setTransactionSuccessful();
689 writeAuthTokenIntoCacheLocked(db, account, type, authToken);
690 return true;
691 }
Fred Quintana33269202009-04-20 16:05:10 -0700692 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800693 } finally {
694 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -0700695 }
Fred Quintana60307342009-03-24 22:48:12 -0700696 }
697 }
698
Fred Quintanaa698f422009-04-08 19:14:54 -0700699 public String peekAuthToken(Account account, String authTokenType) {
Fred Quintana56285a62010-12-02 14:20:51 -0800700 if (Log.isLoggable(TAG, Log.VERBOSE)) {
701 Log.v(TAG, "peekAuthToken: " + account
702 + ", authTokenType " + authTokenType
703 + ", caller's uid " + Binder.getCallingUid()
704 + ", pid " + Binder.getCallingPid());
705 }
Fred Quintana382601f2010-03-25 12:25:10 -0700706 if (account == null) throw new IllegalArgumentException("account is null");
707 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700708 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700709 long identityToken = clearCallingIdentity();
710 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800711 return readAuthTokenInternal(account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700712 } finally {
713 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700714 }
Fred Quintana60307342009-03-24 22:48:12 -0700715 }
716
Fred Quintanaa698f422009-04-08 19:14:54 -0700717 public void setAuthToken(Account account, String authTokenType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -0800718 if (Log.isLoggable(TAG, Log.VERBOSE)) {
719 Log.v(TAG, "setAuthToken: " + account
720 + ", authTokenType " + authTokenType
721 + ", caller's uid " + Binder.getCallingUid()
722 + ", pid " + Binder.getCallingPid());
723 }
Fred Quintana382601f2010-03-25 12:25:10 -0700724 if (account == null) throw new IllegalArgumentException("account is null");
725 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700726 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700727 long identityToken = clearCallingIdentity();
728 try {
Fred Quintana6dfd1382009-09-16 17:32:42 -0700729 saveAuthTokenToDatabase(account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700730 } finally {
731 restoreCallingIdentity(identityToken);
732 }
Fred Quintana60307342009-03-24 22:48:12 -0700733 }
734
Fred Quintanaa698f422009-04-08 19:14:54 -0700735 public void setPassword(Account account, String password) {
Fred Quintana56285a62010-12-02 14:20:51 -0800736 if (Log.isLoggable(TAG, Log.VERBOSE)) {
737 Log.v(TAG, "setAuthToken: " + account
738 + ", caller's uid " + Binder.getCallingUid()
739 + ", pid " + Binder.getCallingPid());
740 }
Fred Quintana382601f2010-03-25 12:25:10 -0700741 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700742 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700743 long identityToken = clearCallingIdentity();
744 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800745 setPasswordInternal(account, password);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700746 } finally {
747 restoreCallingIdentity(identityToken);
748 }
Fred Quintana60307342009-03-24 22:48:12 -0700749 }
750
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800751 private void setPasswordInternal(Account account, String password) {
Fred Quintana31957f12009-10-21 13:43:10 -0700752 if (account == null) {
753 return;
754 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800755 synchronized (mCacheLock) {
756 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
757 db.beginTransaction();
758 try {
759 final ContentValues values = new ContentValues();
760 values.put(ACCOUNTS_PASSWORD, password);
761 final long accountId = getAccountIdLocked(db, account);
762 if (accountId >= 0) {
763 final String[] argsAccountId = {String.valueOf(accountId)};
764 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
765 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Costin Manolachef5ffe892011-01-19 09:35:32 -0800766 mAuthTokenCache.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800767 db.setTransactionSuccessful();
Costin Manolachef5ffe892011-01-19 09:35:32 -0800768 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800769 } finally {
770 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -0800771 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800772 sendAccountsChangedBroadcast();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -0800773 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700774 }
775
Fred Quintana33269202009-04-20 16:05:10 -0700776 private void sendAccountsChangedBroadcast() {
Fred Quintana56285a62010-12-02 14:20:51 -0800777 Log.i(TAG, "the accounts changed, sending broadcast of "
778 + ACCOUNTS_CHANGED_INTENT.getAction());
Fred Quintana33269202009-04-20 16:05:10 -0700779 mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
780 }
781
Fred Quintanaa698f422009-04-08 19:14:54 -0700782 public void clearPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800783 if (Log.isLoggable(TAG, Log.VERBOSE)) {
784 Log.v(TAG, "clearPassword: " + account
785 + ", caller's uid " + Binder.getCallingUid()
786 + ", pid " + Binder.getCallingPid());
787 }
Fred Quintana382601f2010-03-25 12:25:10 -0700788 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700789 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700790 long identityToken = clearCallingIdentity();
791 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800792 setPasswordInternal(account, null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700793 } finally {
794 restoreCallingIdentity(identityToken);
795 }
Fred Quintana60307342009-03-24 22:48:12 -0700796 }
797
Fred Quintanaa698f422009-04-08 19:14:54 -0700798 public void setUserData(Account account, String key, String value) {
Fred Quintana56285a62010-12-02 14:20:51 -0800799 if (Log.isLoggable(TAG, Log.VERBOSE)) {
800 Log.v(TAG, "setUserData: " + account
801 + ", key " + key
802 + ", caller's uid " + Binder.getCallingUid()
803 + ", pid " + Binder.getCallingPid());
804 }
Fred Quintana382601f2010-03-25 12:25:10 -0700805 if (key == null) throw new IllegalArgumentException("key is null");
806 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700807 checkAuthenticateAccountsPermission(account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700808 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700809 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800810 setUserdataInternal(account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -0700811 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700812 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700813 }
814 }
815
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800816 private void setUserdataInternal(Account account, String key, String value) {
Fred Quintana31957f12009-10-21 13:43:10 -0700817 if (account == null || key == null) {
818 return;
819 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800820 synchronized (mCacheLock) {
821 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
822 db.beginTransaction();
823 try {
824 long accountId = getAccountIdLocked(db, account);
825 if (accountId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700826 return;
827 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800828 long extrasId = getExtrasIdLocked(db, accountId, key);
829 if (extrasId < 0 ) {
830 extrasId = insertExtraLocked(db, accountId, key, value);
831 if (extrasId < 0) {
832 return;
833 }
834 } else {
835 ContentValues values = new ContentValues();
836 values.put(EXTRAS_VALUE, value);
837 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
838 return;
839 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700840
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800841 }
842 writeUserDataIntoCacheLocked(db, account, key, value);
843 db.setTransactionSuccessful();
844 } finally {
845 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700846 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700847 }
848 }
849
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700850 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -0800851 if (result == null) {
852 Log.e(TAG, "the result is unexpectedly null", new Exception());
853 }
854 if (Log.isLoggable(TAG, Log.VERBOSE)) {
855 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
856 + response);
857 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700858 try {
859 response.onResult(result);
860 } catch (RemoteException e) {
861 // if the caller is dead then there is no one to care about remote
862 // exceptions
863 if (Log.isLoggable(TAG, Log.VERBOSE)) {
864 Log.v(TAG, "failure while notifying response", e);
865 }
866 }
867 }
868
Costin Manolache5f383ad92010-12-02 16:44:46 -0800869 void getAuthTokenLabel(final IAccountManagerResponse response,
870 final Account account, final String authTokenType) {
871 if (account == null) throw new IllegalArgumentException("account is null");
872 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
873
874 checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
875
876 long identityToken = clearCallingIdentity();
877 try {
878 new Session(response, account.type, false,
879 false /* stripAuthTokenFromResult */) {
880 protected String toDebugString(long now) {
881 return super.toDebugString(now) + ", getAuthTokenLabel"
882 + ", " + account
883 + ", authTokenType " + authTokenType;
884 }
885
886 public void run() throws RemoteException {
887 mAuthenticator.getAuthTokenLabel(this, authTokenType);
888 }
889
890 public void onResult(Bundle result) {
891 if (result != null) {
892 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
893 Bundle bundle = new Bundle();
894 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
895 super.onResult(bundle);
896 return;
897 } else {
898 super.onResult(result);
899 }
900 }
901 }.bind();
902 } finally {
903 restoreCallingIdentity(identityToken);
904 }
905 }
906
Fred Quintanaa698f422009-04-08 19:14:54 -0700907 public void getAuthToken(IAccountManagerResponse response, final Account account,
908 final String authTokenType, final boolean notifyOnAuthFailure,
Costin Manolachec6684f92011-01-14 11:25:39 -0800909 final boolean expectActivityLaunch, Bundle loginOptionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -0800910 if (Log.isLoggable(TAG, Log.VERBOSE)) {
911 Log.v(TAG, "getAuthToken: " + account
912 + ", response " + response
913 + ", authTokenType " + authTokenType
914 + ", notifyOnAuthFailure " + notifyOnAuthFailure
915 + ", expectActivityLaunch " + expectActivityLaunch
916 + ", caller's uid " + Binder.getCallingUid()
917 + ", pid " + Binder.getCallingPid());
918 }
Fred Quintana382601f2010-03-25 12:25:10 -0700919 if (response == null) throw new IllegalArgumentException("response is null");
920 if (account == null) throw new IllegalArgumentException("account is null");
921 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700922 checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
Costin Manolachea40c6302010-12-13 14:50:45 -0800923 AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
924 mAuthenticatorCache.getServiceInfo(
925 AuthenticatorDescription.newKey(account.type));
926 final boolean customTokens =
927 authenticatorInfo != null && authenticatorInfo.type.customTokens;
928
929 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700930 final int callerUid = Binder.getCallingUid();
Costin Manolachea40c6302010-12-13 14:50:45 -0800931 final boolean permissionGranted = customTokens ||
932 permissionIsGranted(account, authTokenType, callerUid);
933
Costin Manolachec6684f92011-01-14 11:25:39 -0800934 final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
935 loginOptionsIn;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700936 // let authenticator know the identity of the caller
937 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
938 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
939 if (notifyOnAuthFailure) {
940 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -0800941 }
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700942
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700943 long identityToken = clearCallingIdentity();
944 try {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700945 // if the caller has permission, do the peek. otherwise go the more expensive
946 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -0800947 if (!customTokens && permissionGranted) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800948 String authToken = readAuthTokenInternal(account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700949 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700950 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700951 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
952 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
953 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700954 onResult(response, result);
955 return;
Fred Quintanaa698f422009-04-08 19:14:54 -0700956 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700957 }
958
Fred Quintana8570f742010-02-18 10:32:54 -0800959 new Session(response, account.type, expectActivityLaunch,
960 false /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700961 protected String toDebugString(long now) {
962 if (loginOptions != null) loginOptions.keySet();
963 return super.toDebugString(now) + ", getAuthToken"
964 + ", " + account
965 + ", authTokenType " + authTokenType
966 + ", loginOptions " + loginOptions
967 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
968 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700969
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700970 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700971 // If the caller doesn't have permission then create and return the
972 // "grant permission" intent instead of the "getAuthToken" intent.
973 if (!permissionGranted) {
974 mAuthenticator.getAuthTokenLabel(this, authTokenType);
975 } else {
976 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
977 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700978 }
979
980 public void onResult(Bundle result) {
981 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700982 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700983 Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
984 new AccountAuthenticatorResponse(this),
985 authTokenType,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700986 result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700987 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700988 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700989 onResult(bundle);
990 return;
991 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700992 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700993 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700994 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
995 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700996 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700997 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700998 "the type and name should not be empty");
999 return;
1000 }
Costin Manolachea40c6302010-12-13 14:50:45 -08001001 if (!customTokens) {
1002 saveAuthTokenToDatabase(new Account(name, type),
1003 authTokenType, authToken);
1004 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001005 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001006
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001007 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08001008 if (intent != null && notifyOnAuthFailure && !customTokens) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001009 doNotification(
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001010 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001011 intent);
1012 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001013 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001014 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07001015 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001016 }.bind();
1017 } finally {
1018 restoreCallingIdentity(identityToken);
1019 }
Fred Quintana60307342009-03-24 22:48:12 -07001020 }
1021
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001022 private void createNoCredentialsPermissionNotification(Account account, Intent intent) {
1023 int uid = intent.getIntExtra(
1024 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
1025 String authTokenType = intent.getStringExtra(
1026 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
1027 String authTokenLabel = intent.getStringExtra(
1028 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);
1029
1030 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
1031 0 /* when */);
Eric Fischeree452ee2009-08-31 17:58:06 -07001032 final String titleAndSubtitle =
1033 mContext.getString(R.string.permission_request_notification_with_subtitle,
1034 account.name);
1035 final int index = titleAndSubtitle.indexOf('\n');
1036 final String title = titleAndSubtitle.substring(0, index);
1037 final String subtitle = titleAndSubtitle.substring(index + 1);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001038 n.setLatestEventInfo(mContext,
Eric Fischeree452ee2009-08-31 17:58:06 -07001039 title, subtitle,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001040 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
Fred Quintana56285a62010-12-02 14:20:51 -08001041 installNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001042 }
1043
Costin Manolache5f383ad92010-12-02 16:44:46 -08001044 String getAccountLabel(String accountType) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001045 RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo =
Costin Manolache5f383ad92010-12-02 16:44:46 -08001046 mAuthenticatorCache.getServiceInfo(
1047 AuthenticatorDescription.newKey(accountType));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001048 if (serviceInfo == null) {
Costin Manolache5f383ad92010-12-02 16:44:46 -08001049 throw new IllegalArgumentException("unknown account type: " + accountType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001050 }
1051
1052 final Context authContext;
1053 try {
1054 authContext = mContext.createPackageContext(
Costin Manolache5f383ad92010-12-02 16:44:46 -08001055 serviceInfo.type.packageName, 0);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001056 } catch (PackageManager.NameNotFoundException e) {
Costin Manolache5f383ad92010-12-02 16:44:46 -08001057 throw new IllegalArgumentException("unknown account type: " + accountType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001058 }
Brian Carlstrom46703b02011-04-06 15:41:29 -07001059 try {
1060 return authContext.getString(serviceInfo.type.labelId);
1061 } catch (Resources.NotFoundException e) {
1062 throw new IllegalArgumentException("unknown account type: " + accountType);
1063 }
Costin Manolache5f383ad92010-12-02 16:44:46 -08001064 }
1065
1066 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
1067 AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001068
1069 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07001070 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08001071 // Since it was set in Eclair+ we can't change it without breaking apps using
1072 // the intent from a non-Activity context.
1073 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001074 intent.addCategory(
1075 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08001076
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001077 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001078 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
1079 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001080 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001081
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001082 return intent;
1083 }
1084
1085 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
1086 int uid) {
1087 Integer id;
1088 synchronized(mCredentialsPermissionNotificationIds) {
1089 final Pair<Pair<Account, String>, Integer> key =
1090 new Pair<Pair<Account, String>, Integer>(
1091 new Pair<Account, String>(account, authTokenType), uid);
1092 id = mCredentialsPermissionNotificationIds.get(key);
1093 if (id == null) {
1094 id = mNotificationIds.incrementAndGet();
1095 mCredentialsPermissionNotificationIds.put(key, id);
1096 }
1097 }
1098 return id;
1099 }
1100
1101 private Integer getSigninRequiredNotificationId(Account account) {
1102 Integer id;
1103 synchronized(mSigninRequiredNotificationIds) {
1104 id = mSigninRequiredNotificationIds.get(account);
1105 if (id == null) {
1106 id = mNotificationIds.incrementAndGet();
1107 mSigninRequiredNotificationIds.put(account, id);
1108 }
1109 }
1110 return id;
1111 }
1112
Fred Quintana33269202009-04-20 16:05:10 -07001113 public void addAcount(final IAccountManagerResponse response, final String accountType,
1114 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001115 final boolean expectActivityLaunch, final Bundle optionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08001116 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1117 Log.v(TAG, "addAccount: accountType " + accountType
1118 + ", response " + response
1119 + ", authTokenType " + authTokenType
1120 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
1121 + ", expectActivityLaunch " + expectActivityLaunch
1122 + ", caller's uid " + Binder.getCallingUid()
1123 + ", pid " + Binder.getCallingPid());
1124 }
Fred Quintana382601f2010-03-25 12:25:10 -07001125 if (response == null) throw new IllegalArgumentException("response is null");
1126 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001127 checkManageAccountsPermission();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001128
1129 final int pid = Binder.getCallingPid();
1130 final int uid = Binder.getCallingUid();
1131 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
1132 options.putInt(AccountManager.KEY_CALLER_UID, uid);
1133 options.putInt(AccountManager.KEY_CALLER_PID, pid);
1134
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001135 long identityToken = clearCallingIdentity();
1136 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001137 new Session(response, accountType, expectActivityLaunch,
1138 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001139 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07001140 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07001141 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001142 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001143
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001144 protected String toDebugString(long now) {
1145 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07001146 + ", accountType " + accountType
1147 + ", requiredFeatures "
1148 + (requiredFeatures != null
1149 ? TextUtils.join(",", requiredFeatures)
1150 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001151 }
1152 }.bind();
1153 } finally {
1154 restoreCallingIdentity(identityToken);
1155 }
Fred Quintana60307342009-03-24 22:48:12 -07001156 }
1157
Fred Quintanaa698f422009-04-08 19:14:54 -07001158 public void confirmCredentials(IAccountManagerResponse response,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001159 final Account account, final Bundle options, final boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08001160 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1161 Log.v(TAG, "confirmCredentials: " + account
1162 + ", response " + response
1163 + ", expectActivityLaunch " + expectActivityLaunch
1164 + ", caller's uid " + Binder.getCallingUid()
1165 + ", pid " + Binder.getCallingPid());
1166 }
Fred Quintana382601f2010-03-25 12:25:10 -07001167 if (response == null) throw new IllegalArgumentException("response is null");
1168 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001169 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001170 long identityToken = clearCallingIdentity();
1171 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001172 new Session(response, account.type, expectActivityLaunch,
1173 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001174 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001175 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001176 }
1177 protected String toDebugString(long now) {
1178 return super.toDebugString(now) + ", confirmCredentials"
1179 + ", " + account;
1180 }
1181 }.bind();
1182 } finally {
1183 restoreCallingIdentity(identityToken);
1184 }
Fred Quintana60307342009-03-24 22:48:12 -07001185 }
1186
Fred Quintanaa698f422009-04-08 19:14:54 -07001187 public void updateCredentials(IAccountManagerResponse response, final Account account,
1188 final String authTokenType, final boolean expectActivityLaunch,
1189 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08001190 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1191 Log.v(TAG, "updateCredentials: " + account
1192 + ", response " + response
1193 + ", authTokenType " + authTokenType
1194 + ", expectActivityLaunch " + expectActivityLaunch
1195 + ", caller's uid " + Binder.getCallingUid()
1196 + ", pid " + Binder.getCallingPid());
1197 }
Fred Quintana382601f2010-03-25 12:25:10 -07001198 if (response == null) throw new IllegalArgumentException("response is null");
1199 if (account == null) throw new IllegalArgumentException("account is null");
1200 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001201 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001202 long identityToken = clearCallingIdentity();
1203 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001204 new Session(response, account.type, expectActivityLaunch,
1205 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001206 public void run() throws RemoteException {
1207 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
1208 }
1209 protected String toDebugString(long now) {
1210 if (loginOptions != null) loginOptions.keySet();
1211 return super.toDebugString(now) + ", updateCredentials"
1212 + ", " + account
1213 + ", authTokenType " + authTokenType
1214 + ", loginOptions " + loginOptions;
1215 }
1216 }.bind();
1217 } finally {
1218 restoreCallingIdentity(identityToken);
1219 }
Fred Quintana60307342009-03-24 22:48:12 -07001220 }
1221
Fred Quintanaa698f422009-04-08 19:14:54 -07001222 public void editProperties(IAccountManagerResponse response, final String accountType,
1223 final boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08001224 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1225 Log.v(TAG, "editProperties: accountType " + accountType
1226 + ", response " + response
1227 + ", expectActivityLaunch " + expectActivityLaunch
1228 + ", caller's uid " + Binder.getCallingUid()
1229 + ", pid " + Binder.getCallingPid());
1230 }
Fred Quintana382601f2010-03-25 12:25:10 -07001231 if (response == null) throw new IllegalArgumentException("response is null");
1232 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001233 checkManageAccountsPermission();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001234 long identityToken = clearCallingIdentity();
1235 try {
Fred Quintana8570f742010-02-18 10:32:54 -08001236 new Session(response, accountType, expectActivityLaunch,
1237 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001238 public void run() throws RemoteException {
1239 mAuthenticator.editProperties(this, mAccountType);
1240 }
1241 protected String toDebugString(long now) {
1242 return super.toDebugString(now) + ", editProperties"
1243 + ", accountType " + accountType;
1244 }
1245 }.bind();
1246 } finally {
1247 restoreCallingIdentity(identityToken);
1248 }
Fred Quintana60307342009-03-24 22:48:12 -07001249 }
1250
Fred Quintana33269202009-04-20 16:05:10 -07001251 private class GetAccountsByTypeAndFeatureSession extends Session {
1252 private final String[] mFeatures;
1253 private volatile Account[] mAccountsOfType = null;
1254 private volatile ArrayList<Account> mAccountsWithFeatures = null;
1255 private volatile int mCurrentAccount = 0;
1256
1257 public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response,
1258 String type, String[] features) {
Fred Quintana8570f742010-02-18 10:32:54 -08001259 super(response, type, false /* expectActivityLaunch */,
1260 true /* stripAuthTokenFromResult */);
Fred Quintana33269202009-04-20 16:05:10 -07001261 mFeatures = features;
1262 }
1263
1264 public void run() throws RemoteException {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001265 synchronized (mCacheLock) {
1266 mAccountsOfType = getAccountsFromCacheLocked(mAccountType);
1267 }
Fred Quintana33269202009-04-20 16:05:10 -07001268 // check whether each account matches the requested features
1269 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
1270 mCurrentAccount = 0;
1271
1272 checkAccount();
1273 }
1274
1275 public void checkAccount() {
1276 if (mCurrentAccount >= mAccountsOfType.length) {
1277 sendResult();
1278 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001279 }
Fred Quintana33269202009-04-20 16:05:10 -07001280
Fred Quintana29e94b82010-03-10 12:11:51 -08001281 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
1282 if (accountAuthenticator == null) {
1283 // It is possible that the authenticator has died, which is indicated by
1284 // mAuthenticator being set to null. If this happens then just abort.
1285 // There is no need to send back a result or error in this case since
1286 // that already happened when mAuthenticator was cleared.
1287 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1288 Log.v(TAG, "checkAccount: aborting session since we are no longer"
1289 + " connected to the authenticator, " + toDebugString());
1290 }
1291 return;
1292 }
Fred Quintana33269202009-04-20 16:05:10 -07001293 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08001294 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07001295 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001296 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07001297 }
1298 }
1299
1300 public void onResult(Bundle result) {
1301 mNumResults++;
1302 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001303 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07001304 return;
1305 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001306 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07001307 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
1308 }
1309 mCurrentAccount++;
1310 checkAccount();
1311 }
1312
1313 public void sendResult() {
1314 IAccountManagerResponse response = getResponseAndClose();
1315 if (response != null) {
1316 try {
1317 Account[] accounts = new Account[mAccountsWithFeatures.size()];
1318 for (int i = 0; i < accounts.length; i++) {
1319 accounts[i] = mAccountsWithFeatures.get(i);
1320 }
Fred Quintana56285a62010-12-02 14:20:51 -08001321 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1322 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1323 + response);
1324 }
Fred Quintana33269202009-04-20 16:05:10 -07001325 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001326 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07001327 response.onResult(result);
1328 } catch (RemoteException e) {
1329 // if the caller is dead then there is no one to care about remote exceptions
1330 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1331 Log.v(TAG, "failure while notifying response", e);
1332 }
1333 }
1334 }
1335 }
1336
1337
1338 protected String toDebugString(long now) {
1339 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
1340 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1341 }
1342 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001343
1344 public Account[] getAccounts(String type) {
Fred Quintana56285a62010-12-02 14:20:51 -08001345 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1346 Log.v(TAG, "getAccounts: accountType " + type
1347 + ", caller's uid " + Binder.getCallingUid()
1348 + ", pid " + Binder.getCallingPid());
1349 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001350 checkReadAccountsPermission();
1351 long identityToken = clearCallingIdentity();
1352 try {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001353 synchronized (mCacheLock) {
1354 return getAccountsFromCacheLocked(type);
1355 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001356 } finally {
1357 restoreCallingIdentity(identityToken);
1358 }
1359 }
1360
1361 public void getAccountsByFeatures(IAccountManagerResponse response,
Fred Quintana33269202009-04-20 16:05:10 -07001362 String type, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -08001363 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1364 Log.v(TAG, "getAccounts: accountType " + type
1365 + ", response " + response
1366 + ", features " + stringArrayToString(features)
1367 + ", caller's uid " + Binder.getCallingUid()
1368 + ", pid " + Binder.getCallingPid());
1369 }
Fred Quintana382601f2010-03-25 12:25:10 -07001370 if (response == null) throw new IllegalArgumentException("response is null");
1371 if (type == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001372 checkReadAccountsPermission();
Fred Quintana33269202009-04-20 16:05:10 -07001373 long identityToken = clearCallingIdentity();
1374 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001375 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001376 Account[] accounts;
1377 synchronized (mCacheLock) {
1378 accounts = getAccountsFromCacheLocked(type);
1379 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001380 Bundle result = new Bundle();
1381 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
1382 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001383 return;
1384 }
Fred Quintana33269202009-04-20 16:05:10 -07001385 new GetAccountsByTypeAndFeatureSession(response, type, features).bind();
1386 } finally {
1387 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001388 }
1389 }
1390
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001391 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07001392 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001393 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07001394 try {
1395 if (cursor.moveToNext()) {
1396 return cursor.getLong(0);
1397 }
1398 return -1;
1399 } finally {
1400 cursor.close();
1401 }
1402 }
1403
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001404 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07001405 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
1406 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
1407 new String[]{key}, null, null, null);
1408 try {
1409 if (cursor.moveToNext()) {
1410 return cursor.getLong(0);
1411 }
1412 return -1;
1413 } finally {
1414 cursor.close();
1415 }
1416 }
1417
Fred Quintanaa698f422009-04-08 19:14:54 -07001418 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07001419 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07001420 IAccountManagerResponse mResponse;
1421 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07001422 final boolean mExpectActivityLaunch;
1423 final long mCreationTime;
1424
Fred Quintana33269202009-04-20 16:05:10 -07001425 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07001426 private int mNumRequestContinued = 0;
1427 private int mNumErrors = 0;
1428
Fred Quintana60307342009-03-24 22:48:12 -07001429
1430 IAccountAuthenticator mAuthenticator = null;
1431
Fred Quintana8570f742010-02-18 10:32:54 -08001432 private final boolean mStripAuthTokenFromResult;
1433
Fred Quintanaa698f422009-04-08 19:14:54 -07001434 public Session(IAccountManagerResponse response, String accountType,
Fred Quintana8570f742010-02-18 10:32:54 -08001435 boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
Fred Quintana60307342009-03-24 22:48:12 -07001436 super();
Fred Quintanaa698f422009-04-08 19:14:54 -07001437 if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07001438 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintana8570f742010-02-18 10:32:54 -08001439 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07001440 mResponse = response;
1441 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07001442 mExpectActivityLaunch = expectActivityLaunch;
1443 mCreationTime = SystemClock.elapsedRealtime();
1444 synchronized (mSessions) {
1445 mSessions.put(toString(), this);
1446 }
1447 try {
1448 response.asBinder().linkToDeath(this, 0 /* flags */);
1449 } catch (RemoteException e) {
1450 mResponse = null;
1451 binderDied();
1452 }
Fred Quintana60307342009-03-24 22:48:12 -07001453 }
1454
Fred Quintanaa698f422009-04-08 19:14:54 -07001455 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07001456 if (mResponse == null) {
1457 // this session has already been closed
1458 return null;
1459 }
Fred Quintana60307342009-03-24 22:48:12 -07001460 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07001461 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07001462 return response;
1463 }
1464
Fred Quintanaa698f422009-04-08 19:14:54 -07001465 private void close() {
1466 synchronized (mSessions) {
1467 if (mSessions.remove(toString()) == null) {
1468 // the session was already closed, so bail out now
1469 return;
1470 }
1471 }
1472 if (mResponse != null) {
1473 // stop listening for response deaths
1474 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
1475
1476 // clear this so that we don't accidentally send any further results
1477 mResponse = null;
1478 }
1479 cancelTimeout();
1480 unbind();
1481 }
1482
1483 public void binderDied() {
1484 mResponse = null;
1485 close();
1486 }
1487
1488 protected String toDebugString() {
1489 return toDebugString(SystemClock.elapsedRealtime());
1490 }
1491
1492 protected String toDebugString(long now) {
1493 return "Session: expectLaunch " + mExpectActivityLaunch
1494 + ", connected " + (mAuthenticator != null)
1495 + ", stats (" + mNumResults + "/" + mNumRequestContinued
1496 + "/" + mNumErrors + ")"
1497 + ", lifetime " + ((now - mCreationTime) / 1000.0);
1498 }
1499
Fred Quintana60307342009-03-24 22:48:12 -07001500 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001501 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1502 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
1503 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001504 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001505 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001506 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07001507 }
1508 }
1509
1510 private void unbind() {
1511 if (mAuthenticator != null) {
1512 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07001513 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07001514 }
1515 }
1516
1517 public void scheduleTimeout() {
1518 mMessageHandler.sendMessageDelayed(
1519 mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
1520 }
1521
1522 public void cancelTimeout() {
1523 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
1524 }
1525
Fred Quintanab839afc2009-10-14 15:57:28 -07001526 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07001527 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07001528 try {
1529 run();
1530 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001531 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07001532 "remote exception");
1533 }
Fred Quintana60307342009-03-24 22:48:12 -07001534 }
1535
Fred Quintanab839afc2009-10-14 15:57:28 -07001536 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001537 mAuthenticator = null;
1538 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001539 if (response != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001540 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07001541 "disconnected");
Fred Quintana60307342009-03-24 22:48:12 -07001542 }
1543 }
1544
Fred Quintanab839afc2009-10-14 15:57:28 -07001545 public abstract void run() throws RemoteException;
1546
Fred Quintana60307342009-03-24 22:48:12 -07001547 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001548 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001549 if (response != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001550 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07001551 "timeout");
Fred Quintana60307342009-03-24 22:48:12 -07001552 }
1553 }
1554
Fred Quintanaa698f422009-04-08 19:14:54 -07001555 public void onResult(Bundle result) {
1556 mNumResults++;
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001557 if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
1558 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1559 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001560 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
1561 Account account = new Account(accountName, accountType);
1562 cancelNotification(getSigninRequiredNotificationId(account));
1563 }
Fred Quintana60307342009-03-24 22:48:12 -07001564 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001565 IAccountManagerResponse response;
1566 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001567 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001568 response = mResponse;
1569 } else {
1570 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001571 }
Fred Quintana60307342009-03-24 22:48:12 -07001572 if (response != null) {
1573 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07001574 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001575 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1576 Log.v(TAG, getClass().getSimpleName()
1577 + " calling onError() on response " + response);
1578 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001579 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07001580 "null bundle returned");
1581 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08001582 if (mStripAuthTokenFromResult) {
1583 result.remove(AccountManager.KEY_AUTHTOKEN);
1584 }
Fred Quintana56285a62010-12-02 14:20:51 -08001585 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1586 Log.v(TAG, getClass().getSimpleName()
1587 + " calling onResult() on response " + response);
1588 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001589 response.onResult(result);
1590 }
Fred Quintana60307342009-03-24 22:48:12 -07001591 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001592 // if the caller is dead then there is no one to care about remote exceptions
1593 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1594 Log.v(TAG, "failure while notifying response", e);
1595 }
Fred Quintana60307342009-03-24 22:48:12 -07001596 }
1597 }
1598 }
Fred Quintana60307342009-03-24 22:48:12 -07001599
Fred Quintanaa698f422009-04-08 19:14:54 -07001600 public void onRequestContinued() {
1601 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07001602 }
1603
1604 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001605 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07001606 IAccountManagerResponse response = getResponseAndClose();
1607 if (response != null) {
1608 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08001609 Log.v(TAG, getClass().getSimpleName()
1610 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07001611 }
1612 try {
1613 response.onError(errorCode, errorMessage);
1614 } catch (RemoteException e) {
1615 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1616 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
1617 }
1618 }
1619 } else {
1620 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1621 Log.v(TAG, "Session.onError: already closed");
1622 }
Fred Quintana60307342009-03-24 22:48:12 -07001623 }
1624 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001625
1626 /**
1627 * find the component name for the authenticator and initiate a bind
1628 * if no authenticator or the bind fails then return false, otherwise return true
1629 */
1630 private boolean bindToAuthenticator(String authenticatorType) {
1631 AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
1632 mAuthenticatorCache.getServiceInfo(
1633 AuthenticatorDescription.newKey(authenticatorType));
1634 if (authenticatorInfo == null) {
1635 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1636 Log.v(TAG, "there is no authenticator for " + authenticatorType
1637 + ", bailing out");
1638 }
1639 return false;
1640 }
1641
1642 Intent intent = new Intent();
1643 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
1644 intent.setComponent(authenticatorInfo.componentName);
1645 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1646 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
1647 }
1648 if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
1649 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1650 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
1651 }
1652 return false;
1653 }
1654
1655
1656 return true;
1657 }
Fred Quintana60307342009-03-24 22:48:12 -07001658 }
1659
1660 private class MessageHandler extends Handler {
1661 MessageHandler(Looper looper) {
1662 super(looper);
1663 }
Costin Manolache3348f142009-09-29 18:58:36 -07001664
Fred Quintana60307342009-03-24 22:48:12 -07001665 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07001666 switch (msg.what) {
1667 case MESSAGE_TIMED_OUT:
1668 Session session = (Session)msg.obj;
1669 session.onTimedOut();
1670 break;
1671
1672 default:
1673 throw new IllegalStateException("unhandled message: " + msg.what);
1674 }
1675 }
1676 }
1677
Oscar Montemayora8529f62009-11-18 10:14:20 -08001678 private static String getDatabaseName() {
1679 if(Environment.isEncryptedFilesystemEnabled()) {
1680 // Hard-coded path in case of encrypted file system
1681 return Environment.getSystemSecureDirectory().getPath() + File.separator + DATABASE_NAME;
1682 } else {
1683 // Regular path in case of non-encrypted file system
1684 return DATABASE_NAME;
1685 }
1686 }
1687
Fred Quintana60307342009-03-24 22:48:12 -07001688 private class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08001689
Fred Quintana60307342009-03-24 22:48:12 -07001690 public DatabaseHelper(Context context) {
Oscar Montemayora8529f62009-11-18 10:14:20 -08001691 super(context, AccountManagerService.getDatabaseName(), null, DATABASE_VERSION);
Fred Quintana60307342009-03-24 22:48:12 -07001692 }
1693
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001694 /**
1695 * This call needs to be made while the mCacheLock is held. The way to
1696 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
1697 * @param db The database.
1698 */
Fred Quintana60307342009-03-24 22:48:12 -07001699 @Override
1700 public void onCreate(SQLiteDatabase db) {
1701 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
1702 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1703 + ACCOUNTS_NAME + " TEXT NOT NULL, "
1704 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
1705 + ACCOUNTS_PASSWORD + " TEXT, "
1706 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
1707
1708 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
1709 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1710 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
1711 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
1712 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
1713 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
1714
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001715 createGrantsTable(db);
1716
Fred Quintana60307342009-03-24 22:48:12 -07001717 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
1718 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1719 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
1720 + EXTRAS_KEY + " TEXT NOT NULL, "
1721 + EXTRAS_VALUE + " TEXT, "
1722 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
1723
1724 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
1725 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
1726 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07001727
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001728 createAccountsDeletionTrigger(db);
1729 }
1730
1731 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001732 db.execSQL(""
1733 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
1734 + " BEGIN"
1735 + " DELETE FROM " + TABLE_AUTHTOKENS
1736 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
1737 + " DELETE FROM " + TABLE_EXTRAS
1738 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001739 + " DELETE FROM " + TABLE_GRANTS
1740 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07001741 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07001742 }
1743
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001744 private void createGrantsTable(SQLiteDatabase db) {
1745 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
1746 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
1747 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
1748 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
1749 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
1750 + "," + GRANTS_GRANTEE_UID + "))");
1751 }
1752
Fred Quintana60307342009-03-24 22:48:12 -07001753 @Override
1754 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001755 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07001756
Fred Quintanaa698f422009-04-08 19:14:54 -07001757 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001758 // no longer need to do anything since the work is done
1759 // when upgrading from version 2
1760 oldVersion++;
1761 }
1762
1763 if (oldVersion == 2) {
1764 createGrantsTable(db);
1765 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
1766 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07001767 oldVersion++;
1768 }
Costin Manolache3348f142009-09-29 18:58:36 -07001769
1770 if (oldVersion == 3) {
1771 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
1772 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
1773 oldVersion++;
1774 }
Fred Quintana60307342009-03-24 22:48:12 -07001775 }
1776
1777 @Override
1778 public void onOpen(SQLiteDatabase db) {
1779 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
1780 }
1781 }
1782
1783 private void setMetaValue(String key, String value) {
1784 ContentValues values = new ContentValues();
1785 values.put(META_KEY, key);
1786 values.put(META_VALUE, value);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001787 synchronized (mCacheLock) {
1788 mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values);
1789 }
Fred Quintana60307342009-03-24 22:48:12 -07001790 }
1791
Fred Quintana60307342009-03-24 22:48:12 -07001792 public IBinder onBind(Intent intent) {
1793 return asBinder();
1794 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001795
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001796 /**
1797 * Searches array of arguments for the specified string
1798 * @param args array of argument strings
1799 * @param value value to search for
1800 * @return true if the value is contained in the array
1801 */
1802 private static boolean scanArgs(String[] args, String value) {
1803 if (args != null) {
1804 for (String arg : args) {
1805 if (value.equals(arg)) {
1806 return true;
1807 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001808 }
1809 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001810 return false;
1811 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001812
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001813 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001814 synchronized (mCacheLock) {
1815 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Fred Quintanaa698f422009-04-08 19:14:54 -07001816
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001817 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001818
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001819 if (isCheckinRequest) {
1820 // This is a checkin request. *Only* upload the account types and the count of each.
1821 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
1822 null, null, ACCOUNTS_TYPE, null, null);
1823 try {
1824 while (cursor.moveToNext()) {
1825 // print type,count
1826 fout.println(cursor.getString(0) + "," + cursor.getString(1));
1827 }
1828 } finally {
1829 if (cursor != null) {
1830 cursor.close();
1831 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001832 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001833 } else {
1834 Account[] accounts = getAccountsFromCacheLocked(null /* type */);
1835 fout.println("Accounts: " + accounts.length);
1836 for (Account account : accounts) {
1837 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001838 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001839
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001840 fout.println();
1841 synchronized (mSessions) {
1842 final long now = SystemClock.elapsedRealtime();
1843 fout.println("Active Sessions: " + mSessions.size());
1844 for (Session session : mSessions.values()) {
1845 fout.println(" " + session.toDebugString(now));
1846 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001847 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001848
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001849 fout.println();
1850 mAuthenticatorCache.dump(fd, fout, args);
1851 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07001852 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001853 }
1854
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001855 private void doNotification(Account account, CharSequence message, Intent intent) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001856 long identityToken = clearCallingIdentity();
1857 try {
1858 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1859 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
1860 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001861
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001862 if (intent.getComponent() != null &&
1863 GrantCredentialsPermissionActivity.class.getName().equals(
1864 intent.getComponent().getClassName())) {
1865 createNoCredentialsPermissionNotification(account, intent);
1866 } else {
Fred Quintana33f889a2009-09-14 17:31:26 -07001867 final Integer notificationId = getSigninRequiredNotificationId(account);
1868 intent.addCategory(String.valueOf(notificationId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001869 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
1870 0 /* when */);
Fred Quintana33f889a2009-09-14 17:31:26 -07001871 final String notificationTitleFormat =
1872 mContext.getText(R.string.notification_title).toString();
1873 n.setLatestEventInfo(mContext,
1874 String.format(notificationTitleFormat, account.name),
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001875 message, PendingIntent.getActivity(
1876 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
Fred Quintana56285a62010-12-02 14:20:51 -08001877 installNotification(notificationId, n);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001878 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001879 } finally {
1880 restoreCallingIdentity(identityToken);
1881 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001882 }
1883
Fred Quintana56285a62010-12-02 14:20:51 -08001884 protected void installNotification(final int notificationId, final Notification n) {
1885 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
1886 .notify(notificationId, n);
1887 }
1888
1889 protected void cancelNotification(int id) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001890 long identityToken = clearCallingIdentity();
1891 try {
1892 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001893 .cancel(id);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001894 } finally {
1895 restoreCallingIdentity(identityToken);
1896 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001897 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001898
Fred Quintanab38eb142010-02-24 13:40:54 -08001899 /** Succeeds if any of the specified permissions are granted. */
1900 private void checkBinderPermission(String... permissions) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001901 final int uid = Binder.getCallingUid();
Fred Quintanab38eb142010-02-24 13:40:54 -08001902
1903 for (String perm : permissions) {
1904 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
1905 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08001906 Log.v(TAG, " caller uid " + uid + " has " + perm);
Fred Quintanab38eb142010-02-24 13:40:54 -08001907 }
1908 return;
1909 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001910 }
Fred Quintanab38eb142010-02-24 13:40:54 -08001911
1912 String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
Fred Quintana56285a62010-12-02 14:20:51 -08001913 Log.w(TAG, " " + msg);
Fred Quintanab38eb142010-02-24 13:40:54 -08001914 throw new SecurityException(msg);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001915 }
1916
Fred Quintana7be59642009-08-24 18:29:25 -07001917 private boolean inSystemImage(int callerUid) {
Fred Quintana56285a62010-12-02 14:20:51 -08001918 String[] packages = mPackageManager.getPackagesForUid(callerUid);
Fred Quintana7be59642009-08-24 18:29:25 -07001919 for (String name : packages) {
1920 try {
Fred Quintana56285a62010-12-02 14:20:51 -08001921 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
1922 if (packageInfo != null
1923 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07001924 return true;
1925 }
1926 } catch (PackageManager.NameNotFoundException e) {
1927 return false;
1928 }
1929 }
1930 return false;
1931 }
1932
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001933 private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
Fred Quintanab839afc2009-10-14 15:57:28 -07001934 final boolean inSystemImage = inSystemImage(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07001935 final boolean fromAuthenticator = account != null
1936 && hasAuthenticatorUid(account.type, callerUid);
1937 final boolean hasExplicitGrants = account != null
1938 && hasExplicitlyGrantedPermission(account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001939 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1940 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08001941 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001942 + ": is authenticator? " + fromAuthenticator
1943 + ", has explicit permission? " + hasExplicitGrants);
1944 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001945 return fromAuthenticator || hasExplicitGrants || inSystemImage;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001946 }
1947
Fred Quintana1a231912009-10-15 11:31:30 -07001948 private boolean hasAuthenticatorUid(String accountType, int callingUid) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001949 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
1950 mAuthenticatorCache.getAllServices()) {
1951 if (serviceInfo.type.type.equals(accountType)) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001952 return (serviceInfo.uid == callingUid) ||
Fred Quintana56285a62010-12-02 14:20:51 -08001953 (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001954 == PackageManager.SIGNATURE_MATCH);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001955 }
1956 }
1957 return false;
1958 }
1959
1960 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType) {
1961 if (Binder.getCallingUid() == android.os.Process.SYSTEM_UID) {
1962 return true;
1963 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001964 synchronized (mCacheLock) {
1965 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1966 String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType,
1967 account.name, account.type};
1968 final boolean permissionGranted =
1969 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
1970 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
1971 // TODO: Skip this check when running automated tests. Replace this
1972 // with a more general solution.
1973 Log.d(TAG, "no credentials permission for usage of " + account + ", "
1974 + authTokenType + " by uid " + Binder.getCallingUid()
1975 + " but ignoring since device is in test harness.");
1976 return true;
1977 }
1978 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001979 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001980 }
1981
1982 private void checkCallingUidAgainstAuthenticator(Account account) {
1983 final int uid = Binder.getCallingUid();
Fred Quintana31957f12009-10-21 13:43:10 -07001984 if (account == null || !hasAuthenticatorUid(account.type, uid)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001985 String msg = "caller uid " + uid + " is different than the authenticator's uid";
1986 Log.w(TAG, msg);
1987 throw new SecurityException(msg);
1988 }
1989 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1990 Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
1991 }
1992 }
1993
1994 private void checkAuthenticateAccountsPermission(Account account) {
1995 checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
1996 checkCallingUidAgainstAuthenticator(account);
1997 }
1998
1999 private void checkReadAccountsPermission() {
2000 checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
2001 }
2002
2003 private void checkManageAccountsPermission() {
2004 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
2005 }
2006
Fred Quintanab38eb142010-02-24 13:40:54 -08002007 private void checkManageAccountsOrUseCredentialsPermissions() {
2008 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
2009 Manifest.permission.USE_CREDENTIALS);
2010 }
2011
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002012 /**
2013 * Allow callers with the given uid permission to get credentials for account/authTokenType.
2014 * <p>
2015 * Although this is public it can only be accessed via the AccountManagerService object
2016 * which is in the system. This means we don't need to protect it with permissions.
2017 * @hide
2018 */
2019 public void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07002020 if (account == null || authTokenType == null) {
2021 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07002022 return;
2023 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002024 synchronized (mCacheLock) {
2025 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2026 db.beginTransaction();
2027 try {
2028 long accountId = getAccountIdLocked(db, account);
2029 if (accountId >= 0) {
2030 ContentValues values = new ContentValues();
2031 values.put(GRANTS_ACCOUNTS_ID, accountId);
2032 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
2033 values.put(GRANTS_GRANTEE_UID, uid);
2034 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
2035 db.setTransactionSuccessful();
2036 }
2037 } finally {
2038 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002039 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002040 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002041 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002042 }
2043
2044 /**
2045 * Don't allow callers with the given uid permission to get credentials for
2046 * account/authTokenType.
2047 * <p>
2048 * Although this is public it can only be accessed via the AccountManagerService object
2049 * which is in the system. This means we don't need to protect it with permissions.
2050 * @hide
2051 */
2052 public void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07002053 if (account == null || authTokenType == null) {
2054 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07002055 return;
2056 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002057 synchronized (mCacheLock) {
2058 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2059 db.beginTransaction();
2060 try {
2061 long accountId = getAccountIdLocked(db, account);
2062 if (accountId >= 0) {
2063 db.delete(TABLE_GRANTS,
2064 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
2065 + GRANTS_GRANTEE_UID + "=?",
2066 new String[]{String.valueOf(accountId), authTokenType,
2067 String.valueOf(uid)});
2068 db.setTransactionSuccessful();
2069 }
2070 } finally {
2071 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002072 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002073 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002074 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002075 }
Fred Quintana56285a62010-12-02 14:20:51 -08002076
2077 static final private String stringArrayToString(String[] value) {
2078 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
2079 }
2080
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002081 private void removeAccountFromCacheLocked(Account account) {
2082 final Account[] oldAccountsForType = mAccountCache.get(account.type);
2083 if (oldAccountsForType != null) {
2084 ArrayList<Account> newAccountsList = new ArrayList<Account>();
2085 for (Account curAccount : oldAccountsForType) {
2086 if (!curAccount.equals(account)) {
2087 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08002088 }
2089 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002090 if (newAccountsList.isEmpty()) {
2091 mAccountCache.remove(account.type);
2092 } else {
2093 Account[] newAccountsForType = new Account[newAccountsList.size()];
2094 newAccountsForType = newAccountsList.toArray(newAccountsForType);
2095 mAccountCache.put(account.type, newAccountsForType);
2096 }
Fred Quintana56285a62010-12-02 14:20:51 -08002097 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002098 mUserDataCache.remove(account);
2099 mAuthTokenCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08002100 }
2101
2102 /**
2103 * This assumes that the caller has already checked that the account is not already present.
2104 */
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002105 private void insertAccountIntoCacheLocked(Account account) {
2106 Account[] accountsForType = mAccountCache.get(account.type);
2107 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
2108 Account[] newAccountsForType = new Account[oldLength + 1];
2109 if (accountsForType != null) {
2110 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08002111 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002112 newAccountsForType[oldLength] = account;
2113 mAccountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08002114 }
2115
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002116 protected Account[] getAccountsFromCacheLocked(String accountType) {
2117 if (accountType != null) {
2118 final Account[] accounts = mAccountCache.get(accountType);
2119 if (accounts == null) {
2120 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08002121 } else {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002122 return Arrays.copyOf(accounts, accounts.length);
Fred Quintana56285a62010-12-02 14:20:51 -08002123 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002124 } else {
2125 int totalLength = 0;
2126 for (Account[] accounts : mAccountCache.values()) {
2127 totalLength += accounts.length;
2128 }
2129 if (totalLength == 0) {
2130 return EMPTY_ACCOUNT_ARRAY;
2131 }
2132 Account[] accounts = new Account[totalLength];
2133 totalLength = 0;
2134 for (Account[] accountsOfType : mAccountCache.values()) {
2135 System.arraycopy(accountsOfType, 0, accounts, totalLength,
2136 accountsOfType.length);
2137 totalLength += accountsOfType.length;
2138 }
2139 return accounts;
Fred Quintana56285a62010-12-02 14:20:51 -08002140 }
2141 }
2142
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002143 protected void writeUserDataIntoCacheLocked(final SQLiteDatabase db, Account account,
2144 String key, String value) {
2145 HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
2146 if (userDataForAccount == null) {
2147 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
2148 mUserDataCache.put(account, userDataForAccount);
2149 }
2150 if (value == null) {
2151 userDataForAccount.remove(key);
2152 } else {
2153 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08002154 }
2155 }
2156
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002157 protected void writeAuthTokenIntoCacheLocked(final SQLiteDatabase db, Account account,
2158 String key, String value) {
2159 HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
2160 if (authTokensForAccount == null) {
2161 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
2162 mAuthTokenCache.put(account, authTokensForAccount);
2163 }
2164 if (value == null) {
2165 authTokensForAccount.remove(key);
2166 } else {
2167 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08002168 }
2169 }
2170
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002171 protected String readAuthTokenInternal(Account account, String authTokenType) {
Fred Quintana56285a62010-12-02 14:20:51 -08002172 synchronized (mCacheLock) {
2173 HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
2174 if (authTokensForAccount == null) {
2175 // need to populate the cache for this account
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002176 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2177 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Fred Quintana56285a62010-12-02 14:20:51 -08002178 mAuthTokenCache.put(account, authTokensForAccount);
2179 }
2180 return authTokensForAccount.get(authTokenType);
2181 }
2182 }
2183
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002184 protected String readUserDataInternal(Account account, String key) {
Fred Quintana56285a62010-12-02 14:20:51 -08002185 synchronized (mCacheLock) {
2186 HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
2187 if (userDataForAccount == null) {
2188 // need to populate the cache for this account
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002189 final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2190 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Fred Quintana56285a62010-12-02 14:20:51 -08002191 mUserDataCache.put(account, userDataForAccount);
2192 }
2193 return userDataForAccount.get(key);
2194 }
2195 }
2196
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002197 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
2198 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08002199 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08002200 Cursor cursor = db.query(TABLE_EXTRAS,
2201 COLUMNS_EXTRAS_KEY_AND_VALUE,
2202 SELECTION_USERDATA_BY_ACCOUNT,
2203 new String[]{account.name, account.type},
2204 null, null, null);
2205 try {
2206 while (cursor.moveToNext()) {
2207 final String tmpkey = cursor.getString(0);
2208 final String value = cursor.getString(1);
2209 userDataForAccount.put(tmpkey, value);
2210 }
2211 } finally {
2212 cursor.close();
2213 }
2214 return userDataForAccount;
2215 }
2216
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002217 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
2218 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08002219 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08002220 Cursor cursor = db.query(TABLE_AUTHTOKENS,
2221 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
2222 SELECTION_AUTHTOKENS_BY_ACCOUNT,
2223 new String[]{account.name, account.type},
2224 null, null, null);
2225 try {
2226 while (cursor.moveToNext()) {
2227 final String type = cursor.getString(0);
2228 final String authToken = cursor.getString(1);
2229 authTokensForAccount.put(type, authToken);
2230 }
2231 } finally {
2232 cursor.close();
2233 }
2234 return authTokensForAccount;
2235 }
Fred Quintana60307342009-03-24 22:48:12 -07002236}