blob: 7a9f2859109ad55464a7ec922ac0fde3b4f8cfb8 [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 Zongker885cfc22009-10-21 16:52:44 -070019import android.Manifest;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080020import android.app.ActivityManager;
Amith Yamasani04e0d262012-02-14 11:50:53 -080021import android.app.AppGlobals;
Doug Zongker885cfc22009-10-21 16:52:44 -070022import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
Fred Quintanaa698f422009-04-08 19:14:54 -070025import android.content.BroadcastReceiver;
Doug Zongker885cfc22009-10-21 16:52:44 -070026import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070027import android.content.ContentValues;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
Fred Quintanab839afc2009-10-14 15:57:28 -070031import android.content.ServiceConnection;
Doug Zongker885cfc22009-10-21 16:52:44 -070032import android.content.pm.ApplicationInfo;
33import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070034import android.content.pm.PackageManager;
35import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070036import android.content.pm.RegisteredServicesCacheListener;
Amith Yamasani04e0d262012-02-14 11:50:53 -080037import android.content.pm.UserInfo;
Brian Carlstrom46703b02011-04-06 15:41:29 -070038import android.content.res.Resources;
Fred Quintana60307342009-03-24 22:48:12 -070039import android.database.Cursor;
40import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070041import android.database.sqlite.SQLiteDatabase;
42import android.database.sqlite.SQLiteOpenHelper;
Doug Zongker885cfc22009-10-21 16:52:44 -070043import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070044import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080045import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070046import android.os.Handler;
47import android.os.HandlerThread;
48import android.os.IBinder;
49import android.os.Looper;
50import android.os.Message;
51import android.os.RemoteException;
52import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070053import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070054import android.os.UserManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070055import android.text.TextUtils;
56import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070057import android.util.Pair;
Amith Yamasani04e0d262012-02-14 11:50:53 -080058import android.util.SparseArray;
Fred Quintana60307342009-03-24 22:48:12 -070059
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070060import com.android.internal.R;
Amith Yamasani04e0d262012-02-14 11:50:53 -080061import com.android.internal.util.IndentingPrintWriter;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070062
Oscar Montemayora8529f62009-11-18 10:14:20 -080063import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -070064import java.io.FileDescriptor;
65import java.io.PrintWriter;
66import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -080067import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -070068import java.util.Collection;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070069import java.util.HashMap;
Fred Quintana56285a62010-12-02 14:20:51 -080070import java.util.LinkedHashMap;
Amith Yamasani04e0d262012-02-14 11:50:53 -080071import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -080072import java.util.Map;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070073import java.util.concurrent.atomic.AtomicInteger;
74import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -070075
Fred Quintana60307342009-03-24 22:48:12 -070076/**
77 * A system service that provides account, password, and authtoken management for all
78 * accounts on the device. Some of these calls are implemented with the help of the corresponding
79 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
80 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -070081 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -070082 * @hide
Fred Quintana60307342009-03-24 22:48:12 -070083 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -070084public class AccountManagerService
85 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -080086 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -070087 private static final String TAG = "AccountManagerService";
88
89 private static final int TIMEOUT_DELAY_MS = 1000 * 60;
90 private static final String DATABASE_NAME = "accounts.db";
Costin Manolache3348f142009-09-29 18:58:36 -070091 private static final int DATABASE_VERSION = 4;
Fred Quintana60307342009-03-24 22:48:12 -070092
93 private final Context mContext;
94
Fred Quintana56285a62010-12-02 14:20:51 -080095 private final PackageManager mPackageManager;
Amith Yamasani258848d2012-08-10 17:06:33 -070096 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -080097
Fred Quintana60307342009-03-24 22:48:12 -070098 private HandlerThread mMessageThread;
99 private final MessageHandler mMessageHandler;
100
101 // Messages that can be sent on mHandler
102 private static final int MESSAGE_TIMED_OUT = 3;
Fred Quintana60307342009-03-24 22:48:12 -0700103
Fred Quintana56285a62010-12-02 14:20:51 -0800104 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700105
106 private static final String TABLE_ACCOUNTS = "accounts";
107 private static final String ACCOUNTS_ID = "_id";
108 private static final String ACCOUNTS_NAME = "name";
109 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700110 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700111 private static final String ACCOUNTS_PASSWORD = "password";
112
113 private static final String TABLE_AUTHTOKENS = "authtokens";
114 private static final String AUTHTOKENS_ID = "_id";
115 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
116 private static final String AUTHTOKENS_TYPE = "type";
117 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
118
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700119 private static final String TABLE_GRANTS = "grants";
120 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
121 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
122 private static final String GRANTS_GRANTEE_UID = "uid";
123
Fred Quintana60307342009-03-24 22:48:12 -0700124 private static final String TABLE_EXTRAS = "extras";
125 private static final String EXTRAS_ID = "_id";
126 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
127 private static final String EXTRAS_KEY = "key";
128 private static final String EXTRAS_VALUE = "value";
129
130 private static final String TABLE_META = "meta";
131 private static final String META_KEY = "key";
132 private static final String META_VALUE = "value";
133
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700134 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
135 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700136 private static final Intent ACCOUNTS_CHANGED_INTENT;
Fred Quintanaa698f422009-04-08 19:14:54 -0700137
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700138 private static final String COUNT_OF_MATCHING_GRANTS = ""
139 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
140 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
141 + " AND " + GRANTS_GRANTEE_UID + "=?"
142 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
143 + " AND " + ACCOUNTS_NAME + "=?"
144 + " AND " + ACCOUNTS_TYPE + "=?";
145
Fred Quintana56285a62010-12-02 14:20:51 -0800146 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
147 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
148 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
149 AUTHTOKENS_AUTHTOKEN};
150
151 private static final String SELECTION_USERDATA_BY_ACCOUNT =
152 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
153 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
154
Fred Quintanaa698f422009-04-08 19:14:54 -0700155 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700156 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
157
Amith Yamasani04e0d262012-02-14 11:50:53 -0800158 static class UserAccounts {
159 private final int userId;
160 private final DatabaseHelper openHelper;
161 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
162 credentialsPermissionNotificationIds =
163 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
164 private final HashMap<Account, Integer> signinRequiredNotificationIds =
165 new HashMap<Account, Integer>();
166 private final Object cacheLock = new Object();
167 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700168 private final HashMap<String, Account[]> accountCache =
169 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800170 /** protected by the {@link #cacheLock} */
171 private HashMap<Account, HashMap<String, String>> userDataCache =
172 new HashMap<Account, HashMap<String, String>>();
173 /** protected by the {@link #cacheLock} */
174 private HashMap<Account, HashMap<String, String>> authTokenCache =
175 new HashMap<Account, HashMap<String, String>>();
176
177 UserAccounts(Context context, int userId) {
178 this.userId = userId;
179 synchronized (cacheLock) {
180 openHelper = new DatabaseHelper(context, userId);
181 }
182 }
183 }
184
185 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
186
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700187 private static AtomicReference<AccountManagerService> sThis =
188 new AtomicReference<AccountManagerService>();
Fred Quintana31957f12009-10-21 13:43:10 -0700189 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700190
191 static {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700192 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
Fred Quintana7be59642009-08-24 18:29:25 -0700193 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
194 }
195
Fred Quintana56285a62010-12-02 14:20:51 -0800196
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700197 /**
198 * This should only be called by system code. One should only call this after the service
199 * has started.
200 * @return a reference to the AccountManagerService instance
201 * @hide
202 */
203 public static AccountManagerService getSingleton() {
204 return sThis.get();
205 }
Fred Quintana60307342009-03-24 22:48:12 -0700206
Fred Quintana56285a62010-12-02 14:20:51 -0800207 public AccountManagerService(Context context) {
208 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700209 }
210
Fred Quintana56285a62010-12-02 14:20:51 -0800211 public AccountManagerService(Context context, PackageManager packageManager,
212 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700213 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800214 mPackageManager = packageManager;
Fred Quintana60307342009-03-24 22:48:12 -0700215
Fred Quintana60307342009-03-24 22:48:12 -0700216 mMessageThread = new HandlerThread("AccountManagerService");
217 mMessageThread.start();
218 mMessageHandler = new MessageHandler(mMessageThread.getLooper());
219
Fred Quintana56285a62010-12-02 14:20:51 -0800220 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800221 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700222
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700223 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800224
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800225 IntentFilter intentFilter = new IntentFilter();
226 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
227 intentFilter.addDataScheme("package");
228 mContext.registerReceiver(new BroadcastReceiver() {
229 @Override
230 public void onReceive(Context context1, Intent intent) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800231 purgeOldGrantsAll();
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800232 }
233 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800234
Amith Yamasani13593602012-03-22 16:16:17 -0700235 IntentFilter userFilter = new IntentFilter();
236 userFilter.addAction(Intent.ACTION_USER_REMOVED);
237 mContext.registerReceiver(new BroadcastReceiver() {
238 @Override
239 public void onReceive(Context context, Intent intent) {
240 onUserRemoved(intent);
241 }
242 }, userFilter);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800243 }
244
Kenny Root26ff6622012-07-30 12:58:03 -0700245 public void systemReady() {
246 mAuthenticatorCache.generateServicesMap();
247 initUser(0);
248 }
249
Amith Yamasani258848d2012-08-10 17:06:33 -0700250 private UserManager getUserManager() {
251 if (mUserManager == null) {
252 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
253 }
254 return mUserManager;
255 }
256
Amith Yamasani04e0d262012-02-14 11:50:53 -0800257 private UserAccounts initUser(int userId) {
258 synchronized (mUsers) {
259 UserAccounts accounts = mUsers.get(userId);
260 if (accounts == null) {
261 accounts = new UserAccounts(mContext, userId);
262 mUsers.append(userId, accounts);
263 purgeOldGrants(accounts);
264 validateAccountsAndPopulateCache(accounts);
265 }
266 return accounts;
267 }
268 }
269
270 private void purgeOldGrantsAll() {
271 synchronized (mUsers) {
272 for (int i = 0; i < mUsers.size(); i++) {
273 purgeOldGrants(mUsers.valueAt(i));
274 }
275 }
276 }
277
278 private void purgeOldGrants(UserAccounts accounts) {
279 synchronized (accounts.cacheLock) {
280 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800281 final Cursor cursor = db.query(TABLE_GRANTS,
282 new String[]{GRANTS_GRANTEE_UID},
283 null, null, GRANTS_GRANTEE_UID, null, null);
284 try {
285 while (cursor.moveToNext()) {
286 final int uid = cursor.getInt(0);
287 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
288 if (packageExists) {
289 continue;
290 }
291 Log.d(TAG, "deleting grants for UID " + uid
292 + " because its package is no longer installed");
293 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
294 new String[]{Integer.toString(uid)});
295 }
296 } finally {
297 cursor.close();
298 }
299 }
300 }
301
Amith Yamasani04e0d262012-02-14 11:50:53 -0800302 private void validateAccountsAndPopulateCache(UserAccounts accounts) {
303 synchronized (accounts.cacheLock) {
304 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800305 boolean accountDeleted = false;
306 Cursor cursor = db.query(TABLE_ACCOUNTS,
307 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
308 null, null, null, null, null);
309 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800310 accounts.accountCache.clear();
Fred Quintana56285a62010-12-02 14:20:51 -0800311 final HashMap<String, ArrayList<String>> accountNamesByType =
Amith Yamasanib483a992012-05-22 13:14:25 -0700312 new LinkedHashMap<String, ArrayList<String>>();
Fred Quintana56285a62010-12-02 14:20:51 -0800313 while (cursor.moveToNext()) {
314 final long accountId = cursor.getLong(0);
315 final String accountType = cursor.getString(1);
316 final String accountName = cursor.getString(2);
317 if (mAuthenticatorCache.getServiceInfo(
318 AuthenticatorDescription.newKey(accountType)) == null) {
319 Log.d(TAG, "deleting account " + accountName + " because type "
320 + accountType + " no longer has a registered authenticator");
321 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
322 accountDeleted = true;
323 final Account account = new Account(accountName, accountType);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800324 accounts.userDataCache.remove(account);
325 accounts.authTokenCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -0800326 } else {
327 ArrayList<String> accountNames = accountNamesByType.get(accountType);
328 if (accountNames == null) {
329 accountNames = new ArrayList<String>();
330 accountNamesByType.put(accountType, accountNames);
331 }
332 accountNames.add(accountName);
333 }
334 }
Andy McFadden2f362292012-01-20 14:43:38 -0800335 for (Map.Entry<String, ArrayList<String>> cur
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800336 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800337 final String accountType = cur.getKey();
338 final ArrayList<String> accountNames = cur.getValue();
339 final Account[] accountsForType = new Account[accountNames.size()];
340 int i = 0;
341 for (String accountName : accountNames) {
342 accountsForType[i] = new Account(accountName, accountType);
343 ++i;
344 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800345 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800346 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800347 } finally {
348 cursor.close();
349 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800350 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800351 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800352 }
353 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700354 }
355
Amith Yamasani04e0d262012-02-14 11:50:53 -0800356 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700357 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -0800358 }
359
360 protected UserAccounts getUserAccounts(int userId) {
361 synchronized (mUsers) {
362 UserAccounts accounts = mUsers.get(userId);
363 if (accounts == null) {
364 accounts = initUser(userId);
365 mUsers.append(userId, accounts);
366 }
367 return accounts;
368 }
369 }
370
Amith Yamasani13593602012-03-22 16:16:17 -0700371 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700372 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -0700373 if (userId < 1) return;
374
375 UserAccounts accounts;
376 synchronized (mUsers) {
377 accounts = mUsers.get(userId);
378 mUsers.remove(userId);
379 }
380 if (accounts == null) {
381 File dbFile = new File(getDatabaseName(userId));
382 dbFile.delete();
383 return;
384 }
385
386 synchronized (accounts.cacheLock) {
387 accounts.openHelper.close();
388 File dbFile = new File(getDatabaseName(userId));
389 dbFile.delete();
390 }
391 }
392
Amith Yamasani04e0d262012-02-14 11:50:53 -0800393 private List<UserInfo> getAllUsers() {
Amith Yamasani258848d2012-08-10 17:06:33 -0700394 return getUserManager().getUsers();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800395 }
396
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800397 public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800398 // Validate accounts for all users
399 List<UserInfo> users = getAllUsers();
400 if (users == null) {
401 validateAccountsAndPopulateCache(getUserAccountsForCaller());
402 } else {
403 for (UserInfo user : users) {
404 validateAccountsAndPopulateCache(getUserAccounts(user.id));
405 }
406 }
Fred Quintana60307342009-03-24 22:48:12 -0700407 }
408
Fred Quintanaa698f422009-04-08 19:14:54 -0700409 public String getPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800410 if (Log.isLoggable(TAG, Log.VERBOSE)) {
411 Log.v(TAG, "getPassword: " + account
412 + ", caller's uid " + Binder.getCallingUid()
413 + ", pid " + Binder.getCallingPid());
414 }
Fred Quintana382601f2010-03-25 12:25:10 -0700415 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700416 checkAuthenticateAccountsPermission(account);
417
Amith Yamasani04e0d262012-02-14 11:50:53 -0800418 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700419 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700420 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800421 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700422 } finally {
423 restoreCallingIdentity(identityToken);
424 }
425 }
426
Amith Yamasani04e0d262012-02-14 11:50:53 -0800427 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700428 if (account == null) {
429 return null;
430 }
431
Amith Yamasani04e0d262012-02-14 11:50:53 -0800432 synchronized (accounts.cacheLock) {
433 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800434 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
435 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
436 new String[]{account.name, account.type}, null, null, null);
437 try {
438 if (cursor.moveToNext()) {
439 return cursor.getString(0);
440 }
441 return null;
442 } finally {
443 cursor.close();
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700444 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700445 }
446 }
447
448 public String getUserData(Account account, String key) {
Fred Quintana56285a62010-12-02 14:20:51 -0800449 if (Log.isLoggable(TAG, Log.VERBOSE)) {
450 Log.v(TAG, "getUserData: " + account
451 + ", key " + key
452 + ", caller's uid " + Binder.getCallingUid()
453 + ", pid " + Binder.getCallingPid());
454 }
Fred Quintana382601f2010-03-25 12:25:10 -0700455 if (account == null) throw new IllegalArgumentException("account is null");
456 if (key == null) throw new IllegalArgumentException("key is null");
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700457 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800458 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700459 long identityToken = clearCallingIdentity();
460 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800461 return readUserDataInternal(accounts, account, key);
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700462 } finally {
463 restoreCallingIdentity(identityToken);
464 }
465 }
466
Fred Quintana97889762009-06-15 12:29:24 -0700467 public AuthenticatorDescription[] getAuthenticatorTypes() {
Fred Quintana56285a62010-12-02 14:20:51 -0800468 if (Log.isLoggable(TAG, Log.VERBOSE)) {
469 Log.v(TAG, "getAuthenticatorTypes: "
470 + "caller's uid " + Binder.getCallingUid()
471 + ", pid " + Binder.getCallingPid());
472 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700473 long identityToken = clearCallingIdentity();
474 try {
Fred Quintana97889762009-06-15 12:29:24 -0700475 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
476 authenticatorCollection = mAuthenticatorCache.getAllServices();
477 AuthenticatorDescription[] types =
478 new AuthenticatorDescription[authenticatorCollection.size()];
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700479 int i = 0;
Fred Quintana97889762009-06-15 12:29:24 -0700480 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
Fred Quintana718d8a22009-04-29 17:53:20 -0700481 : authenticatorCollection) {
482 types[i] = authenticator.type;
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700483 i++;
484 }
485 return types;
486 } finally {
487 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700488 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700489 }
490
Fred Quintanaa698f422009-04-08 19:14:54 -0700491 public boolean addAccount(Account account, String password, Bundle extras) {
Fred Quintana56285a62010-12-02 14:20:51 -0800492 if (Log.isLoggable(TAG, Log.VERBOSE)) {
493 Log.v(TAG, "addAccount: " + account
494 + ", caller's uid " + Binder.getCallingUid()
495 + ", pid " + Binder.getCallingPid());
496 }
Fred Quintana382601f2010-03-25 12:25:10 -0700497 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700498 checkAuthenticateAccountsPermission(account);
499
Amith Yamasani04e0d262012-02-14 11:50:53 -0800500 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana60307342009-03-24 22:48:12 -0700501 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700502 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700503 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800504 return addAccountInternal(accounts, account, password, extras);
Fred Quintana60307342009-03-24 22:48:12 -0700505 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700506 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700507 }
508 }
509
Amith Yamasani04e0d262012-02-14 11:50:53 -0800510 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
511 Bundle extras) {
Fred Quintana743dfad2010-07-15 10:59:25 -0700512 if (account == null) {
513 return false;
514 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800515 synchronized (accounts.cacheLock) {
516 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800517 db.beginTransaction();
518 try {
519 long numMatches = DatabaseUtils.longForQuery(db,
520 "select count(*) from " + TABLE_ACCOUNTS
521 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
522 new String[]{account.name, account.type});
523 if (numMatches > 0) {
524 Log.w(TAG, "insertAccountIntoDatabase: " + account
525 + ", skipping since the account already exists");
526 return false;
527 }
528 ContentValues values = new ContentValues();
529 values.put(ACCOUNTS_NAME, account.name);
530 values.put(ACCOUNTS_TYPE, account.type);
531 values.put(ACCOUNTS_PASSWORD, password);
532 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
533 if (accountId < 0) {
534 Log.w(TAG, "insertAccountIntoDatabase: " + account
535 + ", skipping the DB insert failed");
536 return false;
537 }
538 if (extras != null) {
539 for (String key : extras.keySet()) {
540 final String value = extras.getString(key);
541 if (insertExtraLocked(db, accountId, key, value) < 0) {
542 Log.w(TAG, "insertAccountIntoDatabase: " + account
543 + ", skipping since insertExtra failed for key " + key);
544 return false;
545 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700546 }
547 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800548 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800549 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800550 } finally {
551 db.endTransaction();
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700552 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800553 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800554 return true;
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700555 }
556 }
557
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800558 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -0700559 ContentValues values = new ContentValues();
560 values.put(EXTRAS_KEY, key);
561 values.put(EXTRAS_ACCOUNTS_ID, accountId);
562 values.put(EXTRAS_VALUE, value);
563 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
564 }
565
Fred Quintana3084a6f2010-01-14 18:02:03 -0800566 public void hasFeatures(IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800567 Account account, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -0800568 if (Log.isLoggable(TAG, Log.VERBOSE)) {
569 Log.v(TAG, "hasFeatures: " + account
570 + ", response " + response
571 + ", features " + stringArrayToString(features)
572 + ", caller's uid " + Binder.getCallingUid()
573 + ", pid " + Binder.getCallingPid());
574 }
Fred Quintana382601f2010-03-25 12:25:10 -0700575 if (response == null) throw new IllegalArgumentException("response is null");
576 if (account == null) throw new IllegalArgumentException("account is null");
577 if (features == null) throw new IllegalArgumentException("features is null");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800578 checkReadAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800579 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800580 long identityToken = clearCallingIdentity();
581 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800582 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800583 } finally {
584 restoreCallingIdentity(identityToken);
585 }
586 }
587
588 private class TestFeaturesSession extends Session {
589 private final String[] mFeatures;
590 private final Account mAccount;
591
Amith Yamasani04e0d262012-02-14 11:50:53 -0800592 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800593 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800594 super(accounts, response, account.type, false /* expectActivityLaunch */,
Fred Quintana8570f742010-02-18 10:32:54 -0800595 true /* stripAuthTokenFromResult */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800596 mFeatures = features;
597 mAccount = account;
598 }
599
600 public void run() throws RemoteException {
601 try {
602 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
603 } catch (RemoteException e) {
604 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
605 }
606 }
607
608 public void onResult(Bundle result) {
609 IAccountManagerResponse response = getResponseAndClose();
610 if (response != null) {
611 try {
612 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -0700613 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800614 return;
615 }
Fred Quintana56285a62010-12-02 14:20:51 -0800616 if (Log.isLoggable(TAG, Log.VERBOSE)) {
617 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
618 + response);
619 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800620 final Bundle newResult = new Bundle();
621 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
622 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
623 response.onResult(newResult);
624 } catch (RemoteException e) {
625 // if the caller is dead then there is no one to care about remote exceptions
626 if (Log.isLoggable(TAG, Log.VERBOSE)) {
627 Log.v(TAG, "failure while notifying response", e);
628 }
629 }
630 }
631 }
632
633 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -0800634 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800635 + ", " + mAccount
636 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
637 }
638 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800639
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700640 public void removeAccount(IAccountManagerResponse response, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800641 if (Log.isLoggable(TAG, Log.VERBOSE)) {
642 Log.v(TAG, "removeAccount: " + account
643 + ", response " + response
644 + ", caller's uid " + Binder.getCallingUid()
645 + ", pid " + Binder.getCallingPid());
646 }
Fred Quintana382601f2010-03-25 12:25:10 -0700647 if (response == null) throw new IllegalArgumentException("response is null");
648 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700649 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800650 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700651 long identityToken = clearCallingIdentity();
Costin Manolacheec0c4f42010-11-16 09:57:28 -0800652
Amith Yamasani04e0d262012-02-14 11:50:53 -0800653 cancelNotification(getSigninRequiredNotificationId(accounts, account));
654 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -0800655 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -0800656 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -0800657 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800658 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Costin Manolacheec0c4f42010-11-16 09:57:28 -0800659 cancelNotification(id);
660 }
661 }
662 }
663
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700664 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800665 new RemoveAccountSession(accounts, response, account).bind();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700666 } finally {
667 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700668 }
Fred Quintana60307342009-03-24 22:48:12 -0700669 }
670
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700671 private class RemoveAccountSession extends Session {
672 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800673 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
674 Account account) {
675 super(accounts, response, account.type, false /* expectActivityLaunch */,
Fred Quintana8570f742010-02-18 10:32:54 -0800676 true /* stripAuthTokenFromResult */);
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700677 mAccount = account;
678 }
679
680 protected String toDebugString(long now) {
681 return super.toDebugString(now) + ", removeAccount"
682 + ", account " + mAccount;
683 }
684
685 public void run() throws RemoteException {
686 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
687 }
688
689 public void onResult(Bundle result) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700690 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
691 && !result.containsKey(AccountManager.KEY_INTENT)) {
692 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700693 if (removalAllowed) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800694 removeAccountInternal(mAccounts, mAccount);
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700695 }
696 IAccountManagerResponse response = getResponseAndClose();
697 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -0800698 if (Log.isLoggable(TAG, Log.VERBOSE)) {
699 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
700 + response);
701 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700702 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700703 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700704 try {
705 response.onResult(result2);
706 } catch (RemoteException e) {
707 // ignore
708 }
709 }
710 }
711 super.onResult(result);
712 }
713 }
714
Amith Yamasani04e0d262012-02-14 11:50:53 -0800715 /* For testing */
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800716 protected void removeAccountInternal(Account account) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800717 removeAccountInternal(getUserAccountsForCaller(), account);
718 }
719
720 private void removeAccountInternal(UserAccounts accounts, Account account) {
721 synchronized (accounts.cacheLock) {
722 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800723 db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
724 new String[]{account.name, account.type});
Amith Yamasani04e0d262012-02-14 11:50:53 -0800725 removeAccountFromCacheLocked(accounts, account);
726 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800727 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700728 }
729
Fred Quintanaa698f422009-04-08 19:14:54 -0700730 public void invalidateAuthToken(String accountType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -0800731 if (Log.isLoggable(TAG, Log.VERBOSE)) {
732 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
733 + ", caller's uid " + Binder.getCallingUid()
734 + ", pid " + Binder.getCallingPid());
735 }
Fred Quintana382601f2010-03-25 12:25:10 -0700736 if (accountType == null) throw new IllegalArgumentException("accountType is null");
737 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Fred Quintanab38eb142010-02-24 13:40:54 -0800738 checkManageAccountsOrUseCredentialsPermissions();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800739 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700740 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700741 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800742 synchronized (accounts.cacheLock) {
743 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800744 db.beginTransaction();
745 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800746 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800747 db.setTransactionSuccessful();
748 } finally {
749 db.endTransaction();
750 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700751 }
Fred Quintana60307342009-03-24 22:48:12 -0700752 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700753 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700754 }
755 }
756
Amith Yamasani04e0d262012-02-14 11:50:53 -0800757 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
758 String accountType, String authToken) {
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700759 if (authToken == null || accountType == null) {
760 return;
761 }
Fred Quintana33269202009-04-20 16:05:10 -0700762 Cursor cursor = db.rawQuery(
763 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
764 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
765 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
766 + " FROM " + TABLE_ACCOUNTS
767 + " JOIN " + TABLE_AUTHTOKENS
768 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
769 + " = " + AUTHTOKENS_ACCOUNTS_ID
770 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
771 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
772 new String[]{authToken, accountType});
773 try {
774 while (cursor.moveToNext()) {
775 long authTokenId = cursor.getLong(0);
776 String accountName = cursor.getString(1);
777 String authTokenType = cursor.getString(2);
778 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800779 writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType),
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800780 authTokenType, null);
Fred Quintana60307342009-03-24 22:48:12 -0700781 }
Fred Quintana33269202009-04-20 16:05:10 -0700782 } finally {
783 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -0700784 }
785 }
786
Amith Yamasani04e0d262012-02-14 11:50:53 -0800787 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
788 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -0700789 if (account == null || type == null) {
790 return false;
791 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800792 cancelNotification(getSigninRequiredNotificationId(accounts, account));
793 synchronized (accounts.cacheLock) {
794 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800795 db.beginTransaction();
796 try {
797 long accountId = getAccountIdLocked(db, account);
798 if (accountId < 0) {
799 return false;
800 }
801 db.delete(TABLE_AUTHTOKENS,
802 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
803 new String[]{type});
804 ContentValues values = new ContentValues();
805 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
806 values.put(AUTHTOKENS_TYPE, type);
807 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
808 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
809 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800810 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800811 return true;
812 }
Fred Quintana33269202009-04-20 16:05:10 -0700813 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800814 } finally {
815 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -0700816 }
Fred Quintana60307342009-03-24 22:48:12 -0700817 }
818 }
819
Fred Quintanaa698f422009-04-08 19:14:54 -0700820 public String peekAuthToken(Account account, String authTokenType) {
Fred Quintana56285a62010-12-02 14:20:51 -0800821 if (Log.isLoggable(TAG, Log.VERBOSE)) {
822 Log.v(TAG, "peekAuthToken: " + account
823 + ", authTokenType " + authTokenType
824 + ", caller's uid " + Binder.getCallingUid()
825 + ", pid " + Binder.getCallingPid());
826 }
Fred Quintana382601f2010-03-25 12:25:10 -0700827 if (account == null) throw new IllegalArgumentException("account is null");
828 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700829 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800830 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700831 long identityToken = clearCallingIdentity();
832 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800833 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700834 } finally {
835 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700836 }
Fred Quintana60307342009-03-24 22:48:12 -0700837 }
838
Fred Quintanaa698f422009-04-08 19:14:54 -0700839 public void setAuthToken(Account account, String authTokenType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -0800840 if (Log.isLoggable(TAG, Log.VERBOSE)) {
841 Log.v(TAG, "setAuthToken: " + account
842 + ", authTokenType " + authTokenType
843 + ", caller's uid " + Binder.getCallingUid()
844 + ", pid " + Binder.getCallingPid());
845 }
Fred Quintana382601f2010-03-25 12:25:10 -0700846 if (account == null) throw new IllegalArgumentException("account is null");
847 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700848 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800849 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700850 long identityToken = clearCallingIdentity();
851 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800852 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700853 } finally {
854 restoreCallingIdentity(identityToken);
855 }
Fred Quintana60307342009-03-24 22:48:12 -0700856 }
857
Fred Quintanaa698f422009-04-08 19:14:54 -0700858 public void setPassword(Account account, String password) {
Fred Quintana56285a62010-12-02 14:20:51 -0800859 if (Log.isLoggable(TAG, Log.VERBOSE)) {
860 Log.v(TAG, "setAuthToken: " + account
861 + ", caller's uid " + Binder.getCallingUid()
862 + ", pid " + Binder.getCallingPid());
863 }
Fred Quintana382601f2010-03-25 12:25:10 -0700864 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700865 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800866 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700867 long identityToken = clearCallingIdentity();
868 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800869 setPasswordInternal(accounts, account, password);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700870 } finally {
871 restoreCallingIdentity(identityToken);
872 }
Fred Quintana60307342009-03-24 22:48:12 -0700873 }
874
Amith Yamasani04e0d262012-02-14 11:50:53 -0800875 private void setPasswordInternal(UserAccounts accounts, Account account, String password) {
Fred Quintana31957f12009-10-21 13:43:10 -0700876 if (account == null) {
877 return;
878 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800879 synchronized (accounts.cacheLock) {
880 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800881 db.beginTransaction();
882 try {
883 final ContentValues values = new ContentValues();
884 values.put(ACCOUNTS_PASSWORD, password);
885 final long accountId = getAccountIdLocked(db, account);
886 if (accountId >= 0) {
887 final String[] argsAccountId = {String.valueOf(accountId)};
888 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
889 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800890 accounts.authTokenCache.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800891 db.setTransactionSuccessful();
Costin Manolachef5ffe892011-01-19 09:35:32 -0800892 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800893 } finally {
894 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -0800895 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800896 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanad4a9d6c2010-02-24 12:07:53 -0800897 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700898 }
899
Amith Yamasani04e0d262012-02-14 11:50:53 -0800900 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -0800901 Log.i(TAG, "the accounts changed, sending broadcast of "
902 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -0700903 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -0700904 }
905
Fred Quintanaa698f422009-04-08 19:14:54 -0700906 public void clearPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800907 if (Log.isLoggable(TAG, Log.VERBOSE)) {
908 Log.v(TAG, "clearPassword: " + account
909 + ", caller's uid " + Binder.getCallingUid()
910 + ", pid " + Binder.getCallingPid());
911 }
Fred Quintana382601f2010-03-25 12:25:10 -0700912 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700913 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800914 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700915 long identityToken = clearCallingIdentity();
916 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800917 setPasswordInternal(accounts, account, null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700918 } finally {
919 restoreCallingIdentity(identityToken);
920 }
Fred Quintana60307342009-03-24 22:48:12 -0700921 }
922
Fred Quintanaa698f422009-04-08 19:14:54 -0700923 public void setUserData(Account account, String key, String value) {
Fred Quintana56285a62010-12-02 14:20:51 -0800924 if (Log.isLoggable(TAG, Log.VERBOSE)) {
925 Log.v(TAG, "setUserData: " + account
926 + ", key " + key
927 + ", caller's uid " + Binder.getCallingUid()
928 + ", pid " + Binder.getCallingPid());
929 }
Fred Quintana382601f2010-03-25 12:25:10 -0700930 if (key == null) throw new IllegalArgumentException("key is null");
931 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700932 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800933 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700934 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700935 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800936 setUserdataInternal(accounts, account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -0700937 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700938 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700939 }
940 }
941
Amith Yamasani04e0d262012-02-14 11:50:53 -0800942 private void setUserdataInternal(UserAccounts accounts, Account account, String key,
943 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -0700944 if (account == null || key == null) {
945 return;
946 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800947 synchronized (accounts.cacheLock) {
948 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800949 db.beginTransaction();
950 try {
951 long accountId = getAccountIdLocked(db, account);
952 if (accountId < 0) {
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700953 return;
954 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800955 long extrasId = getExtrasIdLocked(db, accountId, key);
956 if (extrasId < 0 ) {
957 extrasId = insertExtraLocked(db, accountId, key, value);
958 if (extrasId < 0) {
959 return;
960 }
961 } else {
962 ContentValues values = new ContentValues();
963 values.put(EXTRAS_VALUE, value);
964 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
965 return;
966 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700967
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800968 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800969 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800970 db.setTransactionSuccessful();
971 } finally {
972 db.endTransaction();
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700973 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -0700974 }
975 }
976
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700977 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -0800978 if (result == null) {
979 Log.e(TAG, "the result is unexpectedly null", new Exception());
980 }
981 if (Log.isLoggable(TAG, Log.VERBOSE)) {
982 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
983 + response);
984 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700985 try {
986 response.onResult(result);
987 } catch (RemoteException e) {
988 // if the caller is dead then there is no one to care about remote
989 // exceptions
990 if (Log.isLoggable(TAG, Log.VERBOSE)) {
991 Log.v(TAG, "failure while notifying response", e);
992 }
993 }
994 }
995
Fred Quintanad9640ec2012-05-23 12:37:00 -0700996 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
997 final String authTokenType)
998 throws RemoteException {
999 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08001000 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1001
Fred Quintanad9640ec2012-05-23 12:37:00 -07001002 final int callingUid = getCallingUid();
1003 clearCallingIdentity();
1004 if (callingUid != android.os.Process.SYSTEM_UID) {
1005 throw new SecurityException("can only call from system");
1006 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001007 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
Costin Manolache5f383ad92010-12-02 16:44:46 -08001008 long identityToken = clearCallingIdentity();
1009 try {
Fred Quintanad9640ec2012-05-23 12:37:00 -07001010 new Session(accounts, response, accountType, false,
Costin Manolache5f383ad92010-12-02 16:44:46 -08001011 false /* stripAuthTokenFromResult */) {
1012 protected String toDebugString(long now) {
1013 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07001014 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08001015 + ", authTokenType " + authTokenType;
1016 }
1017
1018 public void run() throws RemoteException {
1019 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1020 }
1021
1022 public void onResult(Bundle result) {
1023 if (result != null) {
1024 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1025 Bundle bundle = new Bundle();
1026 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1027 super.onResult(bundle);
1028 return;
1029 } else {
1030 super.onResult(result);
1031 }
1032 }
1033 }.bind();
1034 } finally {
1035 restoreCallingIdentity(identityToken);
1036 }
1037 }
1038
Fred Quintanaa698f422009-04-08 19:14:54 -07001039 public void getAuthToken(IAccountManagerResponse response, final Account account,
1040 final String authTokenType, final boolean notifyOnAuthFailure,
Costin Manolachec6684f92011-01-14 11:25:39 -08001041 final boolean expectActivityLaunch, Bundle loginOptionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08001042 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1043 Log.v(TAG, "getAuthToken: " + account
1044 + ", response " + response
1045 + ", authTokenType " + authTokenType
1046 + ", notifyOnAuthFailure " + notifyOnAuthFailure
1047 + ", expectActivityLaunch " + expectActivityLaunch
1048 + ", caller's uid " + Binder.getCallingUid()
1049 + ", pid " + Binder.getCallingPid());
1050 }
Fred Quintana382601f2010-03-25 12:25:10 -07001051 if (response == null) throw new IllegalArgumentException("response is null");
1052 if (account == null) throw new IllegalArgumentException("account is null");
1053 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001054 checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001055 UserAccounts accounts = getUserAccountsForCaller();
Costin Manolachea40c6302010-12-13 14:50:45 -08001056 AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
1057 mAuthenticatorCache.getServiceInfo(
1058 AuthenticatorDescription.newKey(account.type));
1059 final boolean customTokens =
1060 authenticatorInfo != null && authenticatorInfo.type.customTokens;
1061
1062 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001063 final int callerUid = Binder.getCallingUid();
Costin Manolachea40c6302010-12-13 14:50:45 -08001064 final boolean permissionGranted = customTokens ||
1065 permissionIsGranted(account, authTokenType, callerUid);
1066
Costin Manolachec6684f92011-01-14 11:25:39 -08001067 final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
1068 loginOptionsIn;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001069 // let authenticator know the identity of the caller
1070 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1071 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
1072 if (notifyOnAuthFailure) {
1073 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08001074 }
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001075
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001076 long identityToken = clearCallingIdentity();
1077 try {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001078 // if the caller has permission, do the peek. otherwise go the more expensive
1079 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08001080 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001081 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001082 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001083 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001084 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1085 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1086 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001087 onResult(response, result);
1088 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001089 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001090 }
1091
Amith Yamasani04e0d262012-02-14 11:50:53 -08001092 new Session(accounts, response, account.type, expectActivityLaunch,
Fred Quintana8570f742010-02-18 10:32:54 -08001093 false /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001094 protected String toDebugString(long now) {
1095 if (loginOptions != null) loginOptions.keySet();
1096 return super.toDebugString(now) + ", getAuthToken"
1097 + ", " + account
1098 + ", authTokenType " + authTokenType
1099 + ", loginOptions " + loginOptions
1100 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
1101 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001102
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001103 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001104 // If the caller doesn't have permission then create and return the
1105 // "grant permission" intent instead of the "getAuthToken" intent.
1106 if (!permissionGranted) {
1107 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1108 } else {
1109 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
1110 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001111 }
1112
1113 public void onResult(Bundle result) {
1114 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001115 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001116 Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
1117 new AccountAuthenticatorResponse(this),
1118 authTokenType,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001119 result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001120 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001121 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001122 onResult(bundle);
1123 return;
1124 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001125 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001126 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001127 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1128 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001129 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001130 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001131 "the type and name should not be empty");
1132 return;
1133 }
Costin Manolachea40c6302010-12-13 14:50:45 -08001134 if (!customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001135 saveAuthTokenToDatabase(mAccounts, new Account(name, type),
Costin Manolachea40c6302010-12-13 14:50:45 -08001136 authTokenType, authToken);
1137 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001138 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001139
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001140 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08001141 if (intent != null && notifyOnAuthFailure && !customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001142 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001143 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001144 intent);
1145 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001146 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001147 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07001148 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001149 }.bind();
1150 } finally {
1151 restoreCallingIdentity(identityToken);
1152 }
Fred Quintana60307342009-03-24 22:48:12 -07001153 }
1154
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001155 private void createNoCredentialsPermissionNotification(Account account, Intent intent) {
1156 int uid = intent.getIntExtra(
1157 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
1158 String authTokenType = intent.getStringExtra(
1159 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
1160 String authTokenLabel = intent.getStringExtra(
1161 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);
1162
1163 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
1164 0 /* when */);
Eric Fischeree452ee2009-08-31 17:58:06 -07001165 final String titleAndSubtitle =
1166 mContext.getString(R.string.permission_request_notification_with_subtitle,
1167 account.name);
1168 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07001169 String title = titleAndSubtitle;
1170 String subtitle = "";
1171 if (index > 0) {
1172 title = titleAndSubtitle.substring(0, index);
1173 subtitle = titleAndSubtitle.substring(index + 1);
1174 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001175 n.setLatestEventInfo(mContext,
Eric Fischeree452ee2009-08-31 17:58:06 -07001176 title, subtitle,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001177 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
Fred Quintana56285a62010-12-02 14:20:51 -08001178 installNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001179 }
1180
Costin Manolache5f383ad92010-12-02 16:44:46 -08001181 String getAccountLabel(String accountType) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001182 RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo =
Costin Manolache5f383ad92010-12-02 16:44:46 -08001183 mAuthenticatorCache.getServiceInfo(
1184 AuthenticatorDescription.newKey(accountType));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001185 if (serviceInfo == null) {
Costin Manolache5f383ad92010-12-02 16:44:46 -08001186 throw new IllegalArgumentException("unknown account type: " + accountType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001187 }
1188
1189 final Context authContext;
1190 try {
1191 authContext = mContext.createPackageContext(
Costin Manolache5f383ad92010-12-02 16:44:46 -08001192 serviceInfo.type.packageName, 0);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001193 } catch (PackageManager.NameNotFoundException e) {
Costin Manolache5f383ad92010-12-02 16:44:46 -08001194 throw new IllegalArgumentException("unknown account type: " + accountType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001195 }
Brian Carlstrom46703b02011-04-06 15:41:29 -07001196 try {
1197 return authContext.getString(serviceInfo.type.labelId);
1198 } catch (Resources.NotFoundException e) {
1199 throw new IllegalArgumentException("unknown account type: " + accountType);
1200 }
Costin Manolache5f383ad92010-12-02 16:44:46 -08001201 }
1202
1203 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
1204 AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001205
1206 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07001207 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08001208 // Since it was set in Eclair+ we can't change it without breaking apps using
1209 // the intent from a non-Activity context.
1210 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001211 intent.addCategory(
1212 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08001213
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001214 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001215 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
1216 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001217 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001218
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001219 return intent;
1220 }
1221
1222 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
1223 int uid) {
1224 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001225 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001226 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001227 final Pair<Pair<Account, String>, Integer> key =
1228 new Pair<Pair<Account, String>, Integer>(
1229 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001230 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001231 if (id == null) {
1232 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001233 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001234 }
1235 }
1236 return id;
1237 }
1238
Amith Yamasani04e0d262012-02-14 11:50:53 -08001239 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001240 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001241 synchronized (accounts.signinRequiredNotificationIds) {
1242 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001243 if (id == null) {
1244 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001245 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001246 }
1247 }
1248 return id;
1249 }
1250
Fred Quintana33269202009-04-20 16:05:10 -07001251 public void addAcount(final IAccountManagerResponse response, final String accountType,
1252 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001253 final boolean expectActivityLaunch, final Bundle optionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08001254 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1255 Log.v(TAG, "addAccount: accountType " + accountType
1256 + ", response " + response
1257 + ", authTokenType " + authTokenType
1258 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
1259 + ", expectActivityLaunch " + expectActivityLaunch
1260 + ", caller's uid " + Binder.getCallingUid()
1261 + ", pid " + Binder.getCallingPid());
1262 }
Fred Quintana382601f2010-03-25 12:25:10 -07001263 if (response == null) throw new IllegalArgumentException("response is null");
1264 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001265 checkManageAccountsPermission();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001266
Amith Yamasani04e0d262012-02-14 11:50:53 -08001267 UserAccounts accounts = getUserAccountsForCaller();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001268 final int pid = Binder.getCallingPid();
1269 final int uid = Binder.getCallingUid();
1270 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
1271 options.putInt(AccountManager.KEY_CALLER_UID, uid);
1272 options.putInt(AccountManager.KEY_CALLER_PID, pid);
1273
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001274 long identityToken = clearCallingIdentity();
1275 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001276 new Session(accounts, response, accountType, expectActivityLaunch,
Fred Quintana8570f742010-02-18 10:32:54 -08001277 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001278 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07001279 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07001280 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001281 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001282
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001283 protected String toDebugString(long now) {
1284 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07001285 + ", accountType " + accountType
1286 + ", requiredFeatures "
1287 + (requiredFeatures != null
1288 ? TextUtils.join(",", requiredFeatures)
1289 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001290 }
1291 }.bind();
1292 } finally {
1293 restoreCallingIdentity(identityToken);
1294 }
Fred Quintana60307342009-03-24 22:48:12 -07001295 }
1296
Fred Quintanaa698f422009-04-08 19:14:54 -07001297 public void confirmCredentials(IAccountManagerResponse response,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001298 final Account account, final Bundle options, final boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08001299 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1300 Log.v(TAG, "confirmCredentials: " + account
1301 + ", response " + response
1302 + ", expectActivityLaunch " + expectActivityLaunch
1303 + ", caller's uid " + Binder.getCallingUid()
1304 + ", pid " + Binder.getCallingPid());
1305 }
Fred Quintana382601f2010-03-25 12:25:10 -07001306 if (response == null) throw new IllegalArgumentException("response is null");
1307 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001308 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001309 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001310 long identityToken = clearCallingIdentity();
1311 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001312 new Session(accounts, response, account.type, expectActivityLaunch,
Fred Quintana8570f742010-02-18 10:32:54 -08001313 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001314 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001315 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001316 }
1317 protected String toDebugString(long now) {
1318 return super.toDebugString(now) + ", confirmCredentials"
1319 + ", " + account;
1320 }
1321 }.bind();
1322 } finally {
1323 restoreCallingIdentity(identityToken);
1324 }
Fred Quintana60307342009-03-24 22:48:12 -07001325 }
1326
Fred Quintanaa698f422009-04-08 19:14:54 -07001327 public void updateCredentials(IAccountManagerResponse response, final Account account,
1328 final String authTokenType, final boolean expectActivityLaunch,
1329 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08001330 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1331 Log.v(TAG, "updateCredentials: " + account
1332 + ", response " + response
1333 + ", authTokenType " + authTokenType
1334 + ", expectActivityLaunch " + expectActivityLaunch
1335 + ", caller's uid " + Binder.getCallingUid()
1336 + ", pid " + Binder.getCallingPid());
1337 }
Fred Quintana382601f2010-03-25 12:25:10 -07001338 if (response == null) throw new IllegalArgumentException("response is null");
1339 if (account == null) throw new IllegalArgumentException("account is null");
1340 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001341 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001342 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001343 long identityToken = clearCallingIdentity();
1344 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001345 new Session(accounts, response, account.type, expectActivityLaunch,
Fred Quintana8570f742010-02-18 10:32:54 -08001346 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001347 public void run() throws RemoteException {
1348 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
1349 }
1350 protected String toDebugString(long now) {
1351 if (loginOptions != null) loginOptions.keySet();
1352 return super.toDebugString(now) + ", updateCredentials"
1353 + ", " + account
1354 + ", authTokenType " + authTokenType
1355 + ", loginOptions " + loginOptions;
1356 }
1357 }.bind();
1358 } finally {
1359 restoreCallingIdentity(identityToken);
1360 }
Fred Quintana60307342009-03-24 22:48:12 -07001361 }
1362
Fred Quintanaa698f422009-04-08 19:14:54 -07001363 public void editProperties(IAccountManagerResponse response, final String accountType,
1364 final boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08001365 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1366 Log.v(TAG, "editProperties: accountType " + accountType
1367 + ", response " + response
1368 + ", expectActivityLaunch " + expectActivityLaunch
1369 + ", caller's uid " + Binder.getCallingUid()
1370 + ", pid " + Binder.getCallingPid());
1371 }
Fred Quintana382601f2010-03-25 12:25:10 -07001372 if (response == null) throw new IllegalArgumentException("response is null");
1373 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001374 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001375 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001376 long identityToken = clearCallingIdentity();
1377 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001378 new Session(accounts, response, accountType, expectActivityLaunch,
Fred Quintana8570f742010-02-18 10:32:54 -08001379 true /* stripAuthTokenFromResult */) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001380 public void run() throws RemoteException {
1381 mAuthenticator.editProperties(this, mAccountType);
1382 }
1383 protected String toDebugString(long now) {
1384 return super.toDebugString(now) + ", editProperties"
1385 + ", accountType " + accountType;
1386 }
1387 }.bind();
1388 } finally {
1389 restoreCallingIdentity(identityToken);
1390 }
Fred Quintana60307342009-03-24 22:48:12 -07001391 }
1392
Fred Quintana33269202009-04-20 16:05:10 -07001393 private class GetAccountsByTypeAndFeatureSession extends Session {
1394 private final String[] mFeatures;
1395 private volatile Account[] mAccountsOfType = null;
1396 private volatile ArrayList<Account> mAccountsWithFeatures = null;
1397 private volatile int mCurrentAccount = 0;
1398
Amith Yamasani04e0d262012-02-14 11:50:53 -08001399 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
1400 IAccountManagerResponse response, String type, String[] features) {
1401 super(accounts, response, type, false /* expectActivityLaunch */,
Fred Quintana8570f742010-02-18 10:32:54 -08001402 true /* stripAuthTokenFromResult */);
Fred Quintana33269202009-04-20 16:05:10 -07001403 mFeatures = features;
1404 }
1405
1406 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001407 synchronized (mAccounts.cacheLock) {
1408 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001409 }
Fred Quintana33269202009-04-20 16:05:10 -07001410 // check whether each account matches the requested features
1411 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
1412 mCurrentAccount = 0;
1413
1414 checkAccount();
1415 }
1416
1417 public void checkAccount() {
1418 if (mCurrentAccount >= mAccountsOfType.length) {
1419 sendResult();
1420 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001421 }
Fred Quintana33269202009-04-20 16:05:10 -07001422
Fred Quintana29e94b82010-03-10 12:11:51 -08001423 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
1424 if (accountAuthenticator == null) {
1425 // It is possible that the authenticator has died, which is indicated by
1426 // mAuthenticator being set to null. If this happens then just abort.
1427 // There is no need to send back a result or error in this case since
1428 // that already happened when mAuthenticator was cleared.
1429 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1430 Log.v(TAG, "checkAccount: aborting session since we are no longer"
1431 + " connected to the authenticator, " + toDebugString());
1432 }
1433 return;
1434 }
Fred Quintana33269202009-04-20 16:05:10 -07001435 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08001436 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07001437 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001438 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07001439 }
1440 }
1441
1442 public void onResult(Bundle result) {
1443 mNumResults++;
1444 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001445 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07001446 return;
1447 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001448 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07001449 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
1450 }
1451 mCurrentAccount++;
1452 checkAccount();
1453 }
1454
1455 public void sendResult() {
1456 IAccountManagerResponse response = getResponseAndClose();
1457 if (response != null) {
1458 try {
1459 Account[] accounts = new Account[mAccountsWithFeatures.size()];
1460 for (int i = 0; i < accounts.length; i++) {
1461 accounts[i] = mAccountsWithFeatures.get(i);
1462 }
Fred Quintana56285a62010-12-02 14:20:51 -08001463 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1464 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1465 + response);
1466 }
Fred Quintana33269202009-04-20 16:05:10 -07001467 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001468 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07001469 response.onResult(result);
1470 } catch (RemoteException e) {
1471 // if the caller is dead then there is no one to care about remote exceptions
1472 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1473 Log.v(TAG, "failure while notifying response", e);
1474 }
1475 }
1476 }
1477 }
1478
1479
1480 protected String toDebugString(long now) {
1481 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
1482 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1483 }
1484 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001485
Amith Yamasani04e0d262012-02-14 11:50:53 -08001486 /**
1487 * Returns the accounts for a specific user
1488 * @hide
1489 */
1490 public Account[] getAccounts(int userId) {
1491 checkReadAccountsPermission();
1492 UserAccounts accounts = getUserAccounts(userId);
1493 long identityToken = clearCallingIdentity();
1494 try {
1495 synchronized (accounts.cacheLock) {
1496 return getAccountsFromCacheLocked(accounts, null);
1497 }
1498 } finally {
1499 restoreCallingIdentity(identityToken);
1500 }
1501 }
1502
Amith Yamasanif29f2362012-04-05 18:29:52 -07001503 /**
1504 * Returns all the accounts qualified by user.
1505 * @hide
1506 */
1507 public AccountAndUser[] getAllAccounts() {
1508 ArrayList<AccountAndUser> allAccounts = new ArrayList<AccountAndUser>();
1509 List<UserInfo> users = getAllUsers();
1510 if (users == null) return new AccountAndUser[0];
1511
1512 synchronized(mUsers) {
1513 for (UserInfo user : users) {
1514 UserAccounts userAccounts = getUserAccounts(user.id);
1515 if (userAccounts == null) continue;
1516 synchronized (userAccounts.cacheLock) {
1517 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null);
1518 for (int a = 0; a < accounts.length; a++) {
1519 allAccounts.add(new AccountAndUser(accounts[a], user.id));
1520 }
1521 }
1522 }
1523 }
1524 AccountAndUser[] accountsArray = new AccountAndUser[allAccounts.size()];
1525 return allAccounts.toArray(accountsArray);
1526 }
1527
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001528 public Account[] getAccounts(String type) {
Fred Quintana56285a62010-12-02 14:20:51 -08001529 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1530 Log.v(TAG, "getAccounts: accountType " + type
1531 + ", caller's uid " + Binder.getCallingUid()
1532 + ", pid " + Binder.getCallingPid());
1533 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001534 checkReadAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001535 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001536 long identityToken = clearCallingIdentity();
1537 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001538 synchronized (accounts.cacheLock) {
1539 return getAccountsFromCacheLocked(accounts, type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001540 }
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001541 } finally {
1542 restoreCallingIdentity(identityToken);
1543 }
1544 }
1545
1546 public void getAccountsByFeatures(IAccountManagerResponse response,
Fred Quintana33269202009-04-20 16:05:10 -07001547 String type, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -08001548 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1549 Log.v(TAG, "getAccounts: accountType " + type
1550 + ", response " + response
1551 + ", features " + stringArrayToString(features)
1552 + ", caller's uid " + Binder.getCallingUid()
1553 + ", pid " + Binder.getCallingPid());
1554 }
Fred Quintana382601f2010-03-25 12:25:10 -07001555 if (response == null) throw new IllegalArgumentException("response is null");
1556 if (type == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001557 checkReadAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001558 UserAccounts userAccounts = getUserAccountsForCaller();
Fred Quintana33269202009-04-20 16:05:10 -07001559 long identityToken = clearCallingIdentity();
1560 try {
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001561 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001562 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001563 synchronized (userAccounts.cacheLock) {
1564 accounts = getAccountsFromCacheLocked(userAccounts, type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001565 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001566 Bundle result = new Bundle();
1567 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
1568 onResult(response, result);
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001569 return;
1570 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001571 new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features).bind();
Fred Quintana33269202009-04-20 16:05:10 -07001572 } finally {
1573 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001574 }
1575 }
1576
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001577 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07001578 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb02009-08-15 21:45:26 -07001579 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07001580 try {
1581 if (cursor.moveToNext()) {
1582 return cursor.getLong(0);
1583 }
1584 return -1;
1585 } finally {
1586 cursor.close();
1587 }
1588 }
1589
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001590 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07001591 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
1592 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
1593 new String[]{key}, null, null, null);
1594 try {
1595 if (cursor.moveToNext()) {
1596 return cursor.getLong(0);
1597 }
1598 return -1;
1599 } finally {
1600 cursor.close();
1601 }
1602 }
1603
Fred Quintanaa698f422009-04-08 19:14:54 -07001604 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07001605 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07001606 IAccountManagerResponse mResponse;
1607 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07001608 final boolean mExpectActivityLaunch;
1609 final long mCreationTime;
1610
Fred Quintana33269202009-04-20 16:05:10 -07001611 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07001612 private int mNumRequestContinued = 0;
1613 private int mNumErrors = 0;
1614
Fred Quintana60307342009-03-24 22:48:12 -07001615
1616 IAccountAuthenticator mAuthenticator = null;
1617
Fred Quintana8570f742010-02-18 10:32:54 -08001618 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001619 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08001620
Amith Yamasani04e0d262012-02-14 11:50:53 -08001621 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Fred Quintana8570f742010-02-18 10:32:54 -08001622 boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
Fred Quintana60307342009-03-24 22:48:12 -07001623 super();
Fred Quintanaa698f422009-04-08 19:14:54 -07001624 if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07001625 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001626 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08001627 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07001628 mResponse = response;
1629 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07001630 mExpectActivityLaunch = expectActivityLaunch;
1631 mCreationTime = SystemClock.elapsedRealtime();
1632 synchronized (mSessions) {
1633 mSessions.put(toString(), this);
1634 }
1635 try {
1636 response.asBinder().linkToDeath(this, 0 /* flags */);
1637 } catch (RemoteException e) {
1638 mResponse = null;
1639 binderDied();
1640 }
Fred Quintana60307342009-03-24 22:48:12 -07001641 }
1642
Fred Quintanaa698f422009-04-08 19:14:54 -07001643 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07001644 if (mResponse == null) {
1645 // this session has already been closed
1646 return null;
1647 }
Fred Quintana60307342009-03-24 22:48:12 -07001648 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07001649 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07001650 return response;
1651 }
1652
Fred Quintanaa698f422009-04-08 19:14:54 -07001653 private void close() {
1654 synchronized (mSessions) {
1655 if (mSessions.remove(toString()) == null) {
1656 // the session was already closed, so bail out now
1657 return;
1658 }
1659 }
1660 if (mResponse != null) {
1661 // stop listening for response deaths
1662 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
1663
1664 // clear this so that we don't accidentally send any further results
1665 mResponse = null;
1666 }
1667 cancelTimeout();
1668 unbind();
1669 }
1670
1671 public void binderDied() {
1672 mResponse = null;
1673 close();
1674 }
1675
1676 protected String toDebugString() {
1677 return toDebugString(SystemClock.elapsedRealtime());
1678 }
1679
1680 protected String toDebugString(long now) {
1681 return "Session: expectLaunch " + mExpectActivityLaunch
1682 + ", connected " + (mAuthenticator != null)
1683 + ", stats (" + mNumResults + "/" + mNumRequestContinued
1684 + "/" + mNumErrors + ")"
1685 + ", lifetime " + ((now - mCreationTime) / 1000.0);
1686 }
1687
Fred Quintana60307342009-03-24 22:48:12 -07001688 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001689 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1690 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
1691 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001692 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001693 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001694 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07001695 }
1696 }
1697
1698 private void unbind() {
1699 if (mAuthenticator != null) {
1700 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07001701 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07001702 }
1703 }
1704
1705 public void scheduleTimeout() {
1706 mMessageHandler.sendMessageDelayed(
1707 mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
1708 }
1709
1710 public void cancelTimeout() {
1711 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
1712 }
1713
Fred Quintanab839afc2009-10-14 15:57:28 -07001714 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07001715 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07001716 try {
1717 run();
1718 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001719 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07001720 "remote exception");
1721 }
Fred Quintana60307342009-03-24 22:48:12 -07001722 }
1723
Fred Quintanab839afc2009-10-14 15:57:28 -07001724 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001725 mAuthenticator = null;
1726 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001727 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001728 try {
1729 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
1730 "disconnected");
1731 } catch (RemoteException e) {
1732 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1733 Log.v(TAG, "Session.onServiceDisconnected: "
1734 + "caught RemoteException while responding", e);
1735 }
1736 }
Fred Quintana60307342009-03-24 22:48:12 -07001737 }
1738 }
1739
Fred Quintanab839afc2009-10-14 15:57:28 -07001740 public abstract void run() throws RemoteException;
1741
Fred Quintana60307342009-03-24 22:48:12 -07001742 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07001743 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001744 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001745 try {
1746 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
1747 "timeout");
1748 } catch (RemoteException e) {
1749 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1750 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
1751 e);
1752 }
1753 }
Fred Quintana60307342009-03-24 22:48:12 -07001754 }
1755 }
1756
Fred Quintanaa698f422009-04-08 19:14:54 -07001757 public void onResult(Bundle result) {
1758 mNumResults++;
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001759 if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
1760 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1761 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001762 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
1763 Account account = new Account(accountName, accountType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001764 cancelNotification(getSigninRequiredNotificationId(mAccounts, account));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001765 }
Fred Quintana60307342009-03-24 22:48:12 -07001766 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001767 IAccountManagerResponse response;
1768 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001769 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001770 response = mResponse;
1771 } else {
1772 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07001773 }
Fred Quintana60307342009-03-24 22:48:12 -07001774 if (response != null) {
1775 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07001776 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001777 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1778 Log.v(TAG, getClass().getSimpleName()
1779 + " calling onError() on response " + response);
1780 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001781 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07001782 "null bundle returned");
1783 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08001784 if (mStripAuthTokenFromResult) {
1785 result.remove(AccountManager.KEY_AUTHTOKEN);
1786 }
Fred Quintana56285a62010-12-02 14:20:51 -08001787 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1788 Log.v(TAG, getClass().getSimpleName()
1789 + " calling onResult() on response " + response);
1790 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001791 response.onResult(result);
1792 }
Fred Quintana60307342009-03-24 22:48:12 -07001793 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001794 // if the caller is dead then there is no one to care about remote exceptions
1795 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1796 Log.v(TAG, "failure while notifying response", e);
1797 }
Fred Quintana60307342009-03-24 22:48:12 -07001798 }
1799 }
1800 }
Fred Quintana60307342009-03-24 22:48:12 -07001801
Fred Quintanaa698f422009-04-08 19:14:54 -07001802 public void onRequestContinued() {
1803 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07001804 }
1805
1806 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001807 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07001808 IAccountManagerResponse response = getResponseAndClose();
1809 if (response != null) {
1810 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08001811 Log.v(TAG, getClass().getSimpleName()
1812 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07001813 }
1814 try {
1815 response.onError(errorCode, errorMessage);
1816 } catch (RemoteException e) {
1817 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1818 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
1819 }
1820 }
1821 } else {
1822 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1823 Log.v(TAG, "Session.onError: already closed");
1824 }
Fred Quintana60307342009-03-24 22:48:12 -07001825 }
1826 }
Fred Quintanab839afc2009-10-14 15:57:28 -07001827
1828 /**
1829 * find the component name for the authenticator and initiate a bind
1830 * if no authenticator or the bind fails then return false, otherwise return true
1831 */
1832 private boolean bindToAuthenticator(String authenticatorType) {
1833 AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
1834 mAuthenticatorCache.getServiceInfo(
1835 AuthenticatorDescription.newKey(authenticatorType));
1836 if (authenticatorInfo == null) {
1837 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1838 Log.v(TAG, "there is no authenticator for " + authenticatorType
1839 + ", bailing out");
1840 }
1841 return false;
1842 }
1843
1844 Intent intent = new Intent();
1845 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
1846 intent.setComponent(authenticatorInfo.componentName);
1847 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1848 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
1849 }
Amith Yamasani34daa752012-03-19 16:14:23 -07001850 if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE, mAccounts.userId)) {
Fred Quintanab839afc2009-10-14 15:57:28 -07001851 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1852 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
1853 }
1854 return false;
1855 }
1856
1857
1858 return true;
1859 }
Fred Quintana60307342009-03-24 22:48:12 -07001860 }
1861
1862 private class MessageHandler extends Handler {
1863 MessageHandler(Looper looper) {
1864 super(looper);
1865 }
Costin Manolache3348f142009-09-29 18:58:36 -07001866
Fred Quintana60307342009-03-24 22:48:12 -07001867 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07001868 switch (msg.what) {
1869 case MESSAGE_TIMED_OUT:
1870 Session session = (Session)msg.obj;
1871 session.onTimedOut();
1872 break;
1873
1874 default:
1875 throw new IllegalStateException("unhandled message: " + msg.what);
1876 }
1877 }
1878 }
1879
Amith Yamasani04e0d262012-02-14 11:50:53 -08001880 private static String getDatabaseName(int userId) {
1881 File systemDir = Environment.getSystemSecureDirectory();
1882 File databaseFile = new File(systemDir, "users/" + userId + "/" + DATABASE_NAME);
1883 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07001884 // Migrate old file, if it exists, to the new location.
1885 // Make sure the new file doesn't already exist. A dummy file could have been
1886 // accidentally created in the old location, causing the new one to become corrupted
1887 // as well.
Amith Yamasani04e0d262012-02-14 11:50:53 -08001888 File oldFile = new File(systemDir, DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07001889 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07001890 // Check for use directory; create if it doesn't exist, else renameTo will fail
1891 File userDir = new File(systemDir, "users/" + userId);
1892 if (!userDir.exists()) {
1893 if (!userDir.mkdirs()) {
1894 throw new IllegalStateException("User dir cannot be created: " + userDir);
1895 }
1896 }
1897 if (!oldFile.renameTo(databaseFile)) {
1898 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
1899 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001900 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08001901 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001902 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08001903 }
1904
Amith Yamasani04e0d262012-02-14 11:50:53 -08001905 static class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08001906
Amith Yamasani04e0d262012-02-14 11:50:53 -08001907 public DatabaseHelper(Context context, int userId) {
1908 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
Fred Quintana60307342009-03-24 22:48:12 -07001909 }
1910
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001911 /**
1912 * This call needs to be made while the mCacheLock is held. The way to
1913 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
1914 * @param db The database.
1915 */
Fred Quintana60307342009-03-24 22:48:12 -07001916 @Override
1917 public void onCreate(SQLiteDatabase db) {
1918 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
1919 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1920 + ACCOUNTS_NAME + " TEXT NOT NULL, "
1921 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
1922 + ACCOUNTS_PASSWORD + " TEXT, "
1923 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
1924
1925 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
1926 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1927 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
1928 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
1929 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
1930 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
1931
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001932 createGrantsTable(db);
1933
Fred Quintana60307342009-03-24 22:48:12 -07001934 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
1935 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
1936 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
1937 + EXTRAS_KEY + " TEXT NOT NULL, "
1938 + EXTRAS_VALUE + " TEXT, "
1939 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
1940
1941 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
1942 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
1943 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07001944
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001945 createAccountsDeletionTrigger(db);
1946 }
1947
1948 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001949 db.execSQL(""
1950 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
1951 + " BEGIN"
1952 + " DELETE FROM " + TABLE_AUTHTOKENS
1953 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
1954 + " DELETE FROM " + TABLE_EXTRAS
1955 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001956 + " DELETE FROM " + TABLE_GRANTS
1957 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07001958 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07001959 }
1960
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001961 private void createGrantsTable(SQLiteDatabase db) {
1962 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
1963 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
1964 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
1965 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
1966 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
1967 + "," + GRANTS_GRANTEE_UID + "))");
1968 }
1969
Fred Quintana60307342009-03-24 22:48:12 -07001970 @Override
1971 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07001972 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07001973
Fred Quintanaa698f422009-04-08 19:14:54 -07001974 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001975 // no longer need to do anything since the work is done
1976 // when upgrading from version 2
1977 oldVersion++;
1978 }
1979
1980 if (oldVersion == 2) {
1981 createGrantsTable(db);
1982 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
1983 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07001984 oldVersion++;
1985 }
Costin Manolache3348f142009-09-29 18:58:36 -07001986
1987 if (oldVersion == 3) {
1988 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
1989 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
1990 oldVersion++;
1991 }
Fred Quintana60307342009-03-24 22:48:12 -07001992 }
1993
1994 @Override
1995 public void onOpen(SQLiteDatabase db) {
1996 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
1997 }
1998 }
1999
Fred Quintana60307342009-03-24 22:48:12 -07002000 public IBinder onBind(Intent intent) {
2001 return asBinder();
2002 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002003
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002004 /**
2005 * Searches array of arguments for the specified string
2006 * @param args array of argument strings
2007 * @param value value to search for
2008 * @return true if the value is contained in the array
2009 */
2010 private static boolean scanArgs(String[] args, String value) {
2011 if (args != null) {
2012 for (String arg : args) {
2013 if (value.equals(arg)) {
2014 return true;
2015 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002016 }
2017 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002018 return false;
2019 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002020
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002021 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07002022 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2023 != PackageManager.PERMISSION_GRANTED) {
2024 fout.println("Permission Denial: can't dump AccountsManager from from pid="
2025 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
2026 + " without permission " + android.Manifest.permission.DUMP);
2027 return;
2028 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002029 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Kenny Root3abd75b2011-09-29 11:00:41 -07002030
Amith Yamasani04e0d262012-02-14 11:50:53 -08002031 fout = new IndentingPrintWriter(fout, " ");
2032 int size = mUsers.size();
2033 for (int i = 0; i < size; i++) {
2034 fout.println("User " + mUsers.keyAt(i) + ":");
2035 ((IndentingPrintWriter) fout).increaseIndent();
2036 dumpUser(mUsers.valueAt(i), fd, fout, args, isCheckinRequest);
2037 ((IndentingPrintWriter) fout).decreaseIndent();
2038 if (i < size - 1) {
2039 fout.println();
2040 }
2041 }
2042 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002043
Amith Yamasani04e0d262012-02-14 11:50:53 -08002044 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
2045 String[] args, boolean isCheckinRequest) {
2046 synchronized (userAccounts.cacheLock) {
2047 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002048
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002049 if (isCheckinRequest) {
2050 // This is a checkin request. *Only* upload the account types and the count of each.
2051 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
2052 null, null, ACCOUNTS_TYPE, null, null);
2053 try {
2054 while (cursor.moveToNext()) {
2055 // print type,count
2056 fout.println(cursor.getString(0) + "," + cursor.getString(1));
2057 }
2058 } finally {
2059 if (cursor != null) {
2060 cursor.close();
2061 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002062 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002063 } else {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002064 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002065 fout.println("Accounts: " + accounts.length);
2066 for (Account account : accounts) {
2067 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002068 }
Fred Quintana307da1a2010-01-21 14:24:20 -08002069
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002070 fout.println();
2071 synchronized (mSessions) {
2072 final long now = SystemClock.elapsedRealtime();
2073 fout.println("Active Sessions: " + mSessions.size());
2074 for (Session session : mSessions.values()) {
2075 fout.println(" " + session.toDebugString(now));
2076 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002077 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002078
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002079 fout.println();
2080 mAuthenticatorCache.dump(fd, fout, args);
2081 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07002082 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002083 }
2084
Amith Yamasani04e0d262012-02-14 11:50:53 -08002085 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
2086 Intent intent) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002087 long identityToken = clearCallingIdentity();
2088 try {
2089 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2090 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
2091 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002092
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002093 if (intent.getComponent() != null &&
2094 GrantCredentialsPermissionActivity.class.getName().equals(
2095 intent.getComponent().getClassName())) {
2096 createNoCredentialsPermissionNotification(account, intent);
2097 } else {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002098 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07002099 intent.addCategory(String.valueOf(notificationId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002100 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
2101 0 /* when */);
Fred Quintana33f889a2009-09-14 17:31:26 -07002102 final String notificationTitleFormat =
2103 mContext.getText(R.string.notification_title).toString();
2104 n.setLatestEventInfo(mContext,
2105 String.format(notificationTitleFormat, account.name),
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002106 message, PendingIntent.getActivity(
2107 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
Fred Quintana56285a62010-12-02 14:20:51 -08002108 installNotification(notificationId, n);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002109 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002110 } finally {
2111 restoreCallingIdentity(identityToken);
2112 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002113 }
2114
Fred Quintana56285a62010-12-02 14:20:51 -08002115 protected void installNotification(final int notificationId, final Notification n) {
2116 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
2117 .notify(notificationId, n);
2118 }
2119
2120 protected void cancelNotification(int id) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002121 long identityToken = clearCallingIdentity();
2122 try {
2123 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002124 .cancel(id);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002125 } finally {
2126 restoreCallingIdentity(identityToken);
2127 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002128 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002129
Fred Quintanab38eb142010-02-24 13:40:54 -08002130 /** Succeeds if any of the specified permissions are granted. */
2131 private void checkBinderPermission(String... permissions) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002132 final int uid = Binder.getCallingUid();
Fred Quintanab38eb142010-02-24 13:40:54 -08002133
2134 for (String perm : permissions) {
2135 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
2136 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08002137 Log.v(TAG, " caller uid " + uid + " has " + perm);
Fred Quintanab38eb142010-02-24 13:40:54 -08002138 }
2139 return;
2140 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002141 }
Fred Quintanab38eb142010-02-24 13:40:54 -08002142
2143 String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
Fred Quintana56285a62010-12-02 14:20:51 -08002144 Log.w(TAG, " " + msg);
Fred Quintanab38eb142010-02-24 13:40:54 -08002145 throw new SecurityException(msg);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002146 }
2147
Fred Quintana7be59642009-08-24 18:29:25 -07002148 private boolean inSystemImage(int callerUid) {
Fred Quintana56285a62010-12-02 14:20:51 -08002149 String[] packages = mPackageManager.getPackagesForUid(callerUid);
Fred Quintana7be59642009-08-24 18:29:25 -07002150 for (String name : packages) {
2151 try {
Fred Quintana56285a62010-12-02 14:20:51 -08002152 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
2153 if (packageInfo != null
2154 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07002155 return true;
2156 }
2157 } catch (PackageManager.NameNotFoundException e) {
2158 return false;
2159 }
2160 }
2161 return false;
2162 }
2163
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002164 private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
Fred Quintanab839afc2009-10-14 15:57:28 -07002165 final boolean inSystemImage = inSystemImage(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07002166 final boolean fromAuthenticator = account != null
2167 && hasAuthenticatorUid(account.type, callerUid);
2168 final boolean hasExplicitGrants = account != null
Amith Yamasani04e0d262012-02-14 11:50:53 -08002169 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002170 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2171 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08002172 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002173 + ": is authenticator? " + fromAuthenticator
2174 + ", has explicit permission? " + hasExplicitGrants);
2175 }
Fred Quintanab839afc2009-10-14 15:57:28 -07002176 return fromAuthenticator || hasExplicitGrants || inSystemImage;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002177 }
2178
Fred Quintana1a231912009-10-15 11:31:30 -07002179 private boolean hasAuthenticatorUid(String accountType, int callingUid) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002180 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
2181 mAuthenticatorCache.getAllServices()) {
2182 if (serviceInfo.type.type.equals(accountType)) {
Fred Quintanaffd0cb02009-08-15 21:45:26 -07002183 return (serviceInfo.uid == callingUid) ||
Fred Quintana56285a62010-12-02 14:20:51 -08002184 (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
Fred Quintanaffd0cb02009-08-15 21:45:26 -07002185 == PackageManager.SIGNATURE_MATCH);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002186 }
2187 }
2188 return false;
2189 }
2190
Amith Yamasani04e0d262012-02-14 11:50:53 -08002191 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
2192 int callerUid) {
2193 if (callerUid == android.os.Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002194 return true;
2195 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002196 UserAccounts accounts = getUserAccountsForCaller();
2197 synchronized (accounts.cacheLock) {
2198 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
2199 String[] args = { String.valueOf(callerUid), authTokenType,
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002200 account.name, account.type};
2201 final boolean permissionGranted =
2202 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
2203 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
2204 // TODO: Skip this check when running automated tests. Replace this
2205 // with a more general solution.
2206 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08002207 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002208 + " but ignoring since device is in test harness.");
2209 return true;
2210 }
2211 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002212 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002213 }
2214
2215 private void checkCallingUidAgainstAuthenticator(Account account) {
2216 final int uid = Binder.getCallingUid();
Fred Quintana31957f12009-10-21 13:43:10 -07002217 if (account == null || !hasAuthenticatorUid(account.type, uid)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002218 String msg = "caller uid " + uid + " is different than the authenticator's uid";
2219 Log.w(TAG, msg);
2220 throw new SecurityException(msg);
2221 }
2222 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2223 Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
2224 }
2225 }
2226
2227 private void checkAuthenticateAccountsPermission(Account account) {
2228 checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
2229 checkCallingUidAgainstAuthenticator(account);
2230 }
2231
2232 private void checkReadAccountsPermission() {
2233 checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
2234 }
2235
2236 private void checkManageAccountsPermission() {
2237 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
2238 }
2239
Fred Quintanab38eb142010-02-24 13:40:54 -08002240 private void checkManageAccountsOrUseCredentialsPermissions() {
2241 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
2242 Manifest.permission.USE_CREDENTIALS);
2243 }
2244
Fred Quintanad9640ec2012-05-23 12:37:00 -07002245 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
2246 throws RemoteException {
2247 final int callingUid = getCallingUid();
2248
2249 if (callingUid != android.os.Process.SYSTEM_UID) {
2250 throw new SecurityException();
2251 }
2252
2253 if (value) {
2254 grantAppPermission(account, authTokenType, uid);
2255 } else {
2256 revokeAppPermission(account, authTokenType, uid);
2257 }
2258 }
2259
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002260 /**
2261 * Allow callers with the given uid permission to get credentials for account/authTokenType.
2262 * <p>
2263 * Although this is public it can only be accessed via the AccountManagerService object
2264 * which is in the system. This means we don't need to protect it with permissions.
2265 * @hide
2266 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07002267 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07002268 if (account == null || authTokenType == null) {
2269 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07002270 return;
2271 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002272 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002273 synchronized (accounts.cacheLock) {
2274 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002275 db.beginTransaction();
2276 try {
2277 long accountId = getAccountIdLocked(db, account);
2278 if (accountId >= 0) {
2279 ContentValues values = new ContentValues();
2280 values.put(GRANTS_ACCOUNTS_ID, accountId);
2281 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
2282 values.put(GRANTS_GRANTEE_UID, uid);
2283 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
2284 db.setTransactionSuccessful();
2285 }
2286 } finally {
2287 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002288 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002289 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002290 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002291 }
2292
2293 /**
2294 * Don't allow callers with the given uid permission to get credentials for
2295 * account/authTokenType.
2296 * <p>
2297 * Although this is public it can only be accessed via the AccountManagerService object
2298 * which is in the system. This means we don't need to protect it with permissions.
2299 * @hide
2300 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07002301 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07002302 if (account == null || authTokenType == null) {
2303 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07002304 return;
2305 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002306 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002307 synchronized (accounts.cacheLock) {
2308 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002309 db.beginTransaction();
2310 try {
2311 long accountId = getAccountIdLocked(db, account);
2312 if (accountId >= 0) {
2313 db.delete(TABLE_GRANTS,
2314 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
2315 + GRANTS_GRANTEE_UID + "=?",
2316 new String[]{String.valueOf(accountId), authTokenType,
2317 String.valueOf(uid)});
2318 db.setTransactionSuccessful();
2319 }
2320 } finally {
2321 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002322 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002323 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002324 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002325 }
Fred Quintana56285a62010-12-02 14:20:51 -08002326
2327 static final private String stringArrayToString(String[] value) {
2328 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
2329 }
2330
Amith Yamasani04e0d262012-02-14 11:50:53 -08002331 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
2332 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002333 if (oldAccountsForType != null) {
2334 ArrayList<Account> newAccountsList = new ArrayList<Account>();
2335 for (Account curAccount : oldAccountsForType) {
2336 if (!curAccount.equals(account)) {
2337 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08002338 }
2339 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002340 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002341 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002342 } else {
2343 Account[] newAccountsForType = new Account[newAccountsList.size()];
2344 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002345 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002346 }
Fred Quintana56285a62010-12-02 14:20:51 -08002347 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002348 accounts.userDataCache.remove(account);
2349 accounts.authTokenCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08002350 }
2351
2352 /**
2353 * This assumes that the caller has already checked that the account is not already present.
2354 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08002355 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
2356 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002357 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
2358 Account[] newAccountsForType = new Account[oldLength + 1];
2359 if (accountsForType != null) {
2360 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08002361 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002362 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002363 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08002364 }
2365
Amith Yamasani04e0d262012-02-14 11:50:53 -08002366 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002367 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002368 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002369 if (accounts == null) {
2370 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08002371 } else {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002372 return Arrays.copyOf(accounts, accounts.length);
Fred Quintana56285a62010-12-02 14:20:51 -08002373 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002374 } else {
2375 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002376 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002377 totalLength += accounts.length;
2378 }
2379 if (totalLength == 0) {
2380 return EMPTY_ACCOUNT_ARRAY;
2381 }
2382 Account[] accounts = new Account[totalLength];
2383 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002384 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002385 System.arraycopy(accountsOfType, 0, accounts, totalLength,
2386 accountsOfType.length);
2387 totalLength += accountsOfType.length;
2388 }
2389 return accounts;
Fred Quintana56285a62010-12-02 14:20:51 -08002390 }
2391 }
2392
Amith Yamasani04e0d262012-02-14 11:50:53 -08002393 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
2394 Account account, String key, String value) {
2395 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002396 if (userDataForAccount == null) {
2397 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002398 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002399 }
2400 if (value == null) {
2401 userDataForAccount.remove(key);
2402 } else {
2403 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08002404 }
2405 }
2406
Amith Yamasani04e0d262012-02-14 11:50:53 -08002407 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
2408 Account account, String key, String value) {
2409 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002410 if (authTokensForAccount == null) {
2411 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002412 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002413 }
2414 if (value == null) {
2415 authTokensForAccount.remove(key);
2416 } else {
2417 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08002418 }
2419 }
2420
Amith Yamasani04e0d262012-02-14 11:50:53 -08002421 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
2422 String authTokenType) {
2423 synchronized (accounts.cacheLock) {
2424 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08002425 if (authTokensForAccount == null) {
2426 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08002427 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002428 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002429 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08002430 }
2431 return authTokensForAccount.get(authTokenType);
2432 }
2433 }
2434
Amith Yamasani04e0d262012-02-14 11:50:53 -08002435 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
2436 synchronized (accounts.cacheLock) {
2437 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08002438 if (userDataForAccount == null) {
2439 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08002440 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002441 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002442 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08002443 }
2444 return userDataForAccount.get(key);
2445 }
2446 }
2447
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002448 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
2449 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08002450 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08002451 Cursor cursor = db.query(TABLE_EXTRAS,
2452 COLUMNS_EXTRAS_KEY_AND_VALUE,
2453 SELECTION_USERDATA_BY_ACCOUNT,
2454 new String[]{account.name, account.type},
2455 null, null, null);
2456 try {
2457 while (cursor.moveToNext()) {
2458 final String tmpkey = cursor.getString(0);
2459 final String value = cursor.getString(1);
2460 userDataForAccount.put(tmpkey, value);
2461 }
2462 } finally {
2463 cursor.close();
2464 }
2465 return userDataForAccount;
2466 }
2467
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002468 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
2469 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08002470 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08002471 Cursor cursor = db.query(TABLE_AUTHTOKENS,
2472 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
2473 SELECTION_AUTHTOKENS_BY_ACCOUNT,
2474 new String[]{account.name, account.type},
2475 null, null, null);
2476 try {
2477 while (cursor.moveToNext()) {
2478 final String type = cursor.getString(0);
2479 final String authToken = cursor.getString(1);
2480 authTokensForAccount.put(type, authToken);
2481 }
2482 } finally {
2483 cursor.close();
2484 }
2485 return authTokensForAccount;
2486 }
Fred Quintana60307342009-03-24 22:48:12 -07002487}