• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.accounts;
18 
19 import android.Manifest;
20 import android.accounts.Account;
21 import android.accounts.AccountAndUser;
22 import android.accounts.AccountAuthenticatorResponse;
23 import android.accounts.AccountManager;
24 import android.accounts.AuthenticatorDescription;
25 import android.accounts.CantAddAccountActivity;
26 import android.accounts.GrantCredentialsPermissionActivity;
27 import android.accounts.IAccountAuthenticator;
28 import android.accounts.IAccountAuthenticatorResponse;
29 import android.accounts.IAccountManager;
30 import android.accounts.IAccountManagerResponse;
31 import android.app.ActivityManager;
32 import android.app.ActivityManagerNative;
33 import android.app.AppGlobals;
34 import android.app.Notification;
35 import android.app.NotificationManager;
36 import android.app.PendingIntent;
37 import android.content.BroadcastReceiver;
38 import android.content.ComponentName;
39 import android.content.ContentValues;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.ServiceConnection;
44 import android.content.pm.ApplicationInfo;
45 import android.content.pm.PackageInfo;
46 import android.content.pm.PackageManager;
47 import android.content.pm.PackageManager.NameNotFoundException;
48 import android.content.pm.RegisteredServicesCache;
49 import android.content.pm.RegisteredServicesCacheListener;
50 import android.content.pm.UserInfo;
51 import android.database.Cursor;
52 import android.database.DatabaseUtils;
53 import android.database.sqlite.SQLiteDatabase;
54 import android.database.sqlite.SQLiteOpenHelper;
55 import android.os.Binder;
56 import android.os.Bundle;
57 import android.os.Environment;
58 import android.os.Handler;
59 import android.os.HandlerThread;
60 import android.os.IBinder;
61 import android.os.Looper;
62 import android.os.Message;
63 import android.os.Process;
64 import android.os.RemoteException;
65 import android.os.SystemClock;
66 import android.os.UserHandle;
67 import android.os.UserManager;
68 import android.text.TextUtils;
69 import android.util.Log;
70 import android.util.Pair;
71 import android.util.Slog;
72 import android.util.SparseArray;
73 
74 import com.android.internal.R;
75 import com.android.internal.util.ArrayUtils;
76 import com.android.internal.util.IndentingPrintWriter;
77 import com.google.android.collect.Lists;
78 import com.google.android.collect.Sets;
79 
80 import java.io.File;
81 import java.io.FileDescriptor;
82 import java.io.PrintWriter;
83 import java.util.ArrayList;
84 import java.util.Arrays;
85 import java.util.Collection;
86 import java.util.HashMap;
87 import java.util.HashSet;
88 import java.util.LinkedHashMap;
89 import java.util.List;
90 import java.util.Map;
91 import java.util.concurrent.atomic.AtomicInteger;
92 import java.util.concurrent.atomic.AtomicReference;
93 
94 /**
95  * A system service that provides  account, password, and authtoken management for all
96  * accounts on the device. Some of these calls are implemented with the help of the corresponding
97  * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
98  * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
99  *    AccountManager accountManager = AccountManager.get(context);
100  * @hide
101  */
102 public class AccountManagerService
103         extends IAccountManager.Stub
104         implements RegisteredServicesCacheListener<AuthenticatorDescription> {
105     private static final String TAG = "AccountManagerService";
106 
107     private static final int TIMEOUT_DELAY_MS = 1000 * 60;
108     private static final String DATABASE_NAME = "accounts.db";
109     private static final int DATABASE_VERSION = 5;
110 
111     private final Context mContext;
112 
113     private final PackageManager mPackageManager;
114     private UserManager mUserManager;
115 
116     private HandlerThread mMessageThread;
117     private final MessageHandler mMessageHandler;
118 
119     // Messages that can be sent on mHandler
120     private static final int MESSAGE_TIMED_OUT = 3;
121     private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
122 
123     private final IAccountAuthenticatorCache mAuthenticatorCache;
124 
125     private static final String TABLE_ACCOUNTS = "accounts";
126     private static final String ACCOUNTS_ID = "_id";
127     private static final String ACCOUNTS_NAME = "name";
128     private static final String ACCOUNTS_TYPE = "type";
129     private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
130     private static final String ACCOUNTS_PASSWORD = "password";
131 
132     private static final String TABLE_AUTHTOKENS = "authtokens";
133     private static final String AUTHTOKENS_ID = "_id";
134     private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
135     private static final String AUTHTOKENS_TYPE = "type";
136     private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
137 
138     private static final String TABLE_GRANTS = "grants";
139     private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
140     private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
141     private static final String GRANTS_GRANTEE_UID = "uid";
142 
143     private static final String TABLE_EXTRAS = "extras";
144     private static final String EXTRAS_ID = "_id";
145     private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
146     private static final String EXTRAS_KEY = "key";
147     private static final String EXTRAS_VALUE = "value";
148 
149     private static final String TABLE_META = "meta";
150     private static final String META_KEY = "key";
151     private static final String META_VALUE = "value";
152 
153     private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
154 
155     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
156             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
157     private static final Intent ACCOUNTS_CHANGED_INTENT;
158 
159     private static final String COUNT_OF_MATCHING_GRANTS = ""
160             + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
161             + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
162             + " AND " + GRANTS_GRANTEE_UID + "=?"
163             + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
164             + " AND " + ACCOUNTS_NAME + "=?"
165             + " AND " + ACCOUNTS_TYPE + "=?";
166 
167     private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
168             AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
169     private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
170             AUTHTOKENS_AUTHTOKEN};
171 
172     private static final String SELECTION_USERDATA_BY_ACCOUNT =
173             EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
174     private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
175 
176     private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
177     private final AtomicInteger mNotificationIds = new AtomicInteger(1);
178 
179     static class UserAccounts {
180         private final int userId;
181         private final DatabaseHelper openHelper;
182         private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
183                 credentialsPermissionNotificationIds =
184                 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
185         private final HashMap<Account, Integer> signinRequiredNotificationIds =
186                 new HashMap<Account, Integer>();
187         private final Object cacheLock = new Object();
188         /** protected by the {@link #cacheLock} */
189         private final HashMap<String, Account[]> accountCache =
190                 new LinkedHashMap<String, Account[]>();
191         /** protected by the {@link #cacheLock} */
192         private HashMap<Account, HashMap<String, String>> userDataCache =
193                 new HashMap<Account, HashMap<String, String>>();
194         /** protected by the {@link #cacheLock} */
195         private HashMap<Account, HashMap<String, String>> authTokenCache =
196                 new HashMap<Account, HashMap<String, String>>();
197 
UserAccounts(Context context, int userId)198         UserAccounts(Context context, int userId) {
199             this.userId = userId;
200             synchronized (cacheLock) {
201                 openHelper = new DatabaseHelper(context, userId);
202             }
203         }
204     }
205 
206     private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
207 
208     private static AtomicReference<AccountManagerService> sThis =
209             new AtomicReference<AccountManagerService>();
210     private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
211 
212     static {
213         ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
214         ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
215     }
216 
217 
218     /**
219      * This should only be called by system code. One should only call this after the service
220      * has started.
221      * @return a reference to the AccountManagerService instance
222      * @hide
223      */
getSingleton()224     public static AccountManagerService getSingleton() {
225         return sThis.get();
226     }
227 
AccountManagerService(Context context)228     public AccountManagerService(Context context) {
229         this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
230     }
231 
AccountManagerService(Context context, PackageManager packageManager, IAccountAuthenticatorCache authenticatorCache)232     public AccountManagerService(Context context, PackageManager packageManager,
233             IAccountAuthenticatorCache authenticatorCache) {
234         mContext = context;
235         mPackageManager = packageManager;
236 
237         mMessageThread = new HandlerThread("AccountManagerService");
238         mMessageThread.start();
239         mMessageHandler = new MessageHandler(mMessageThread.getLooper());
240 
241         mAuthenticatorCache = authenticatorCache;
242         mAuthenticatorCache.setListener(this, null /* Handler */);
243 
244         sThis.set(this);
245 
246         IntentFilter intentFilter = new IntentFilter();
247         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
248         intentFilter.addDataScheme("package");
249         mContext.registerReceiver(new BroadcastReceiver() {
250             @Override
251             public void onReceive(Context context1, Intent intent) {
252                 purgeOldGrantsAll();
253             }
254         }, intentFilter);
255 
256         IntentFilter userFilter = new IntentFilter();
257         userFilter.addAction(Intent.ACTION_USER_REMOVED);
258         userFilter.addAction(Intent.ACTION_USER_STARTED);
259         mContext.registerReceiverAsUser(new BroadcastReceiver() {
260             @Override
261             public void onReceive(Context context, Intent intent) {
262                 String action = intent.getAction();
263                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
264                     onUserRemoved(intent);
265                 } else if (Intent.ACTION_USER_STARTED.equals(action)) {
266                     onUserStarted(intent);
267                 }
268             }
269         }, UserHandle.ALL, userFilter, null, null);
270     }
271 
systemReady()272     public void systemReady() {
273     }
274 
getUserManager()275     private UserManager getUserManager() {
276         if (mUserManager == null) {
277             mUserManager = UserManager.get(mContext);
278         }
279         return mUserManager;
280     }
281 
initUser(int userId)282     private UserAccounts initUser(int userId) {
283         synchronized (mUsers) {
284             UserAccounts accounts = mUsers.get(userId);
285             if (accounts == null) {
286                 accounts = new UserAccounts(mContext, userId);
287                 mUsers.append(userId, accounts);
288                 purgeOldGrants(accounts);
289                 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
290             }
291             return accounts;
292         }
293     }
294 
purgeOldGrantsAll()295     private void purgeOldGrantsAll() {
296         synchronized (mUsers) {
297             for (int i = 0; i < mUsers.size(); i++) {
298                 purgeOldGrants(mUsers.valueAt(i));
299             }
300         }
301     }
302 
purgeOldGrants(UserAccounts accounts)303     private void purgeOldGrants(UserAccounts accounts) {
304         synchronized (accounts.cacheLock) {
305             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
306             final Cursor cursor = db.query(TABLE_GRANTS,
307                     new String[]{GRANTS_GRANTEE_UID},
308                     null, null, GRANTS_GRANTEE_UID, null, null);
309             try {
310                 while (cursor.moveToNext()) {
311                     final int uid = cursor.getInt(0);
312                     final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
313                     if (packageExists) {
314                         continue;
315                     }
316                     Log.d(TAG, "deleting grants for UID " + uid
317                             + " because its package is no longer installed");
318                     db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
319                             new String[]{Integer.toString(uid)});
320                 }
321             } finally {
322                 cursor.close();
323             }
324         }
325     }
326 
327     /**
328      * Validate internal set of accounts against installed authenticators for
329      * given user. Clears cached authenticators before validating.
330      */
validateAccounts(int userId)331     public void validateAccounts(int userId) {
332         final UserAccounts accounts = getUserAccounts(userId);
333 
334         // Invalidate user-specific cache to make sure we catch any
335         // removed authenticators.
336         validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
337     }
338 
339     /**
340      * Validate internal set of accounts against installed authenticators for
341      * given user. Clear cached authenticators before validating when requested.
342      */
validateAccountsInternal( UserAccounts accounts, boolean invalidateAuthenticatorCache)343     private void validateAccountsInternal(
344             UserAccounts accounts, boolean invalidateAuthenticatorCache) {
345         if (invalidateAuthenticatorCache) {
346             mAuthenticatorCache.invalidateCache(accounts.userId);
347         }
348 
349         final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
350         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
351                 mAuthenticatorCache.getAllServices(accounts.userId)) {
352             knownAuth.add(service.type);
353         }
354 
355         synchronized (accounts.cacheLock) {
356             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
357             boolean accountDeleted = false;
358             Cursor cursor = db.query(TABLE_ACCOUNTS,
359                     new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
360                     null, null, null, null, null);
361             try {
362                 accounts.accountCache.clear();
363                 final HashMap<String, ArrayList<String>> accountNamesByType =
364                         new LinkedHashMap<String, ArrayList<String>>();
365                 while (cursor.moveToNext()) {
366                     final long accountId = cursor.getLong(0);
367                     final String accountType = cursor.getString(1);
368                     final String accountName = cursor.getString(2);
369 
370                     if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
371                         Slog.w(TAG, "deleting account " + accountName + " because type "
372                                 + accountType + " no longer has a registered authenticator");
373                         db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
374                         accountDeleted = true;
375                         final Account account = new Account(accountName, accountType);
376                         accounts.userDataCache.remove(account);
377                         accounts.authTokenCache.remove(account);
378                     } else {
379                         ArrayList<String> accountNames = accountNamesByType.get(accountType);
380                         if (accountNames == null) {
381                             accountNames = new ArrayList<String>();
382                             accountNamesByType.put(accountType, accountNames);
383                         }
384                         accountNames.add(accountName);
385                     }
386                 }
387                 for (Map.Entry<String, ArrayList<String>> cur
388                         : accountNamesByType.entrySet()) {
389                     final String accountType = cur.getKey();
390                     final ArrayList<String> accountNames = cur.getValue();
391                     final Account[] accountsForType = new Account[accountNames.size()];
392                     int i = 0;
393                     for (String accountName : accountNames) {
394                         accountsForType[i] = new Account(accountName, accountType);
395                         ++i;
396                     }
397                     accounts.accountCache.put(accountType, accountsForType);
398                 }
399             } finally {
400                 cursor.close();
401                 if (accountDeleted) {
402                     sendAccountsChangedBroadcast(accounts.userId);
403                 }
404             }
405         }
406     }
407 
getUserAccountsForCaller()408     private UserAccounts getUserAccountsForCaller() {
409         return getUserAccounts(UserHandle.getCallingUserId());
410     }
411 
getUserAccounts(int userId)412     protected UserAccounts getUserAccounts(int userId) {
413         synchronized (mUsers) {
414             UserAccounts accounts = mUsers.get(userId);
415             if (accounts == null) {
416                 accounts = initUser(userId);
417                 mUsers.append(userId, accounts);
418             }
419             return accounts;
420         }
421     }
422 
onUserRemoved(Intent intent)423     private void onUserRemoved(Intent intent) {
424         int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
425         if (userId < 1) return;
426 
427         UserAccounts accounts;
428         synchronized (mUsers) {
429             accounts = mUsers.get(userId);
430             mUsers.remove(userId);
431         }
432         if (accounts == null) {
433             File dbFile = new File(getDatabaseName(userId));
434             dbFile.delete();
435             return;
436         }
437 
438         synchronized (accounts.cacheLock) {
439             accounts.openHelper.close();
440             File dbFile = new File(getDatabaseName(userId));
441             dbFile.delete();
442         }
443     }
444 
onUserStarted(Intent intent)445     private void onUserStarted(Intent intent) {
446         int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
447         if (userId < 1) return;
448 
449         // Check if there's a shared account that needs to be created as an account
450         Account[] sharedAccounts = getSharedAccountsAsUser(userId);
451         if (sharedAccounts == null || sharedAccounts.length == 0) return;
452         Account[] accounts = getAccountsAsUser(null, userId);
453         for (Account sa : sharedAccounts) {
454             if (ArrayUtils.contains(accounts, sa)) continue;
455             // Account doesn't exist. Copy it now.
456             copyAccountToUser(sa, UserHandle.USER_OWNER, userId);
457         }
458     }
459 
460     @Override
onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed)461     public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
462         validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
463     }
464 
getPassword(Account account)465     public String getPassword(Account account) {
466         if (Log.isLoggable(TAG, Log.VERBOSE)) {
467             Log.v(TAG, "getPassword: " + account
468                     + ", caller's uid " + Binder.getCallingUid()
469                     + ", pid " + Binder.getCallingPid());
470         }
471         if (account == null) throw new IllegalArgumentException("account is null");
472         checkAuthenticateAccountsPermission(account);
473 
474         UserAccounts accounts = getUserAccountsForCaller();
475         long identityToken = clearCallingIdentity();
476         try {
477             return readPasswordInternal(accounts, account);
478         } finally {
479             restoreCallingIdentity(identityToken);
480         }
481     }
482 
readPasswordInternal(UserAccounts accounts, Account account)483     private String readPasswordInternal(UserAccounts accounts, Account account) {
484         if (account == null) {
485             return null;
486         }
487 
488         synchronized (accounts.cacheLock) {
489             final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
490             Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
491                     ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
492                     new String[]{account.name, account.type}, null, null, null);
493             try {
494                 if (cursor.moveToNext()) {
495                     return cursor.getString(0);
496                 }
497                 return null;
498             } finally {
499                 cursor.close();
500             }
501         }
502     }
503 
getUserData(Account account, String key)504     public String getUserData(Account account, String key) {
505         if (Log.isLoggable(TAG, Log.VERBOSE)) {
506             Log.v(TAG, "getUserData: " + account
507                     + ", key " + key
508                     + ", caller's uid " + Binder.getCallingUid()
509                     + ", pid " + Binder.getCallingPid());
510         }
511         if (account == null) throw new IllegalArgumentException("account is null");
512         if (key == null) throw new IllegalArgumentException("key is null");
513         checkAuthenticateAccountsPermission(account);
514         UserAccounts accounts = getUserAccountsForCaller();
515         long identityToken = clearCallingIdentity();
516         try {
517             return readUserDataInternal(accounts, account, key);
518         } finally {
519             restoreCallingIdentity(identityToken);
520         }
521     }
522 
getAuthenticatorTypes()523     public AuthenticatorDescription[] getAuthenticatorTypes() {
524         if (Log.isLoggable(TAG, Log.VERBOSE)) {
525             Log.v(TAG, "getAuthenticatorTypes: "
526                     + "caller's uid " + Binder.getCallingUid()
527                     + ", pid " + Binder.getCallingPid());
528         }
529         final int userId = UserHandle.getCallingUserId();
530         final long identityToken = clearCallingIdentity();
531         try {
532             Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
533                     authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
534             AuthenticatorDescription[] types =
535                     new AuthenticatorDescription[authenticatorCollection.size()];
536             int i = 0;
537             for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
538                     : authenticatorCollection) {
539                 types[i] = authenticator.type;
540                 i++;
541             }
542             return types;
543         } finally {
544             restoreCallingIdentity(identityToken);
545         }
546     }
547 
548     @Override
addAccountExplicitly(Account account, String password, Bundle extras)549     public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
550         if (Log.isLoggable(TAG, Log.VERBOSE)) {
551             Log.v(TAG, "addAccountExplicitly: " + account
552                     + ", caller's uid " + Binder.getCallingUid()
553                     + ", pid " + Binder.getCallingPid());
554         }
555         if (account == null) throw new IllegalArgumentException("account is null");
556         checkAuthenticateAccountsPermission(account);
557         /*
558          * Child users are not allowed to add accounts. Only the accounts that are
559          * shared by the parent profile can be added to child profile.
560          *
561          * TODO: Only allow accounts that were shared to be added by
562          *     a limited user.
563          */
564 
565         UserAccounts accounts = getUserAccountsForCaller();
566         // fails if the account already exists
567         long identityToken = clearCallingIdentity();
568         try {
569             return addAccountInternal(accounts, account, password, extras, false);
570         } finally {
571             restoreCallingIdentity(identityToken);
572         }
573     }
574 
copyAccountToUser(final Account account, int userFrom, int userTo)575     private boolean copyAccountToUser(final Account account, int userFrom, int userTo) {
576         final UserAccounts fromAccounts = getUserAccounts(userFrom);
577         final UserAccounts toAccounts = getUserAccounts(userTo);
578         if (fromAccounts == null || toAccounts == null) {
579             return false;
580         }
581 
582         long identityToken = clearCallingIdentity();
583         try {
584             new Session(fromAccounts, null, account.type, false,
585                     false /* stripAuthTokenFromResult */) {
586                 protected String toDebugString(long now) {
587                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
588                             + ", " + account.type;
589                 }
590 
591                 public void run() throws RemoteException {
592                     mAuthenticator.getAccountCredentialsForCloning(this, account);
593                 }
594 
595                 public void onResult(Bundle result) {
596                     if (result != null) {
597                         if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
598                             // Create a Session for the target user and pass in the bundle
599                             completeCloningAccount(result, account, toAccounts);
600                         }
601                         return;
602                     } else {
603                         super.onResult(result);
604                     }
605                 }
606             }.bind();
607         } finally {
608             restoreCallingIdentity(identityToken);
609         }
610         return true;
611     }
612 
completeCloningAccount(final Bundle result, final Account account, final UserAccounts targetUser)613     void completeCloningAccount(final Bundle result, final Account account,
614             final UserAccounts targetUser) {
615         long id = clearCallingIdentity();
616         try {
617             new Session(targetUser, null, account.type, false,
618                     false /* stripAuthTokenFromResult */) {
619                 protected String toDebugString(long now) {
620                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
621                             + ", " + account.type;
622                 }
623 
624                 public void run() throws RemoteException {
625                     // Confirm that the owner's account still exists before this step.
626                     UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER);
627                     synchronized (owner.cacheLock) {
628                         Account[] ownerAccounts = getAccounts(UserHandle.USER_OWNER);
629                         for (Account acc : ownerAccounts) {
630                             if (acc.equals(account)) {
631                                 mAuthenticator.addAccountFromCredentials(this, account, result);
632                                 break;
633                             }
634                         }
635                     }
636                 }
637 
638                 public void onResult(Bundle result) {
639                     if (result != null) {
640                         if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
641                             // TODO: Anything?
642                         } else {
643                             // TODO: Show error notification
644                             // TODO: Should we remove the shadow account to avoid retries?
645                         }
646                         return;
647                     } else {
648                         super.onResult(result);
649                     }
650                 }
651 
652                 public void onError(int errorCode, String errorMessage) {
653                     super.onError(errorCode,  errorMessage);
654                     // TODO: Show error notification to user
655                     // TODO: Should we remove the shadow account so that it doesn't keep trying?
656                 }
657 
658             }.bind();
659         } finally {
660             restoreCallingIdentity(id);
661         }
662     }
663 
addAccountInternal(UserAccounts accounts, Account account, String password, Bundle extras, boolean restricted)664     private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
665             Bundle extras, boolean restricted) {
666         if (account == null) {
667             return false;
668         }
669         synchronized (accounts.cacheLock) {
670             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
671             db.beginTransaction();
672             try {
673                 long numMatches = DatabaseUtils.longForQuery(db,
674                         "select count(*) from " + TABLE_ACCOUNTS
675                                 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
676                         new String[]{account.name, account.type});
677                 if (numMatches > 0) {
678                     Log.w(TAG, "insertAccountIntoDatabase: " + account
679                             + ", skipping since the account already exists");
680                     return false;
681                 }
682                 ContentValues values = new ContentValues();
683                 values.put(ACCOUNTS_NAME, account.name);
684                 values.put(ACCOUNTS_TYPE, account.type);
685                 values.put(ACCOUNTS_PASSWORD, password);
686                 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
687                 if (accountId < 0) {
688                     Log.w(TAG, "insertAccountIntoDatabase: " + account
689                             + ", skipping the DB insert failed");
690                     return false;
691                 }
692                 if (extras != null) {
693                     for (String key : extras.keySet()) {
694                         final String value = extras.getString(key);
695                         if (insertExtraLocked(db, accountId, key, value) < 0) {
696                             Log.w(TAG, "insertAccountIntoDatabase: " + account
697                                     + ", skipping since insertExtra failed for key " + key);
698                             return false;
699                         }
700                     }
701                 }
702                 db.setTransactionSuccessful();
703                 insertAccountIntoCacheLocked(accounts, account);
704             } finally {
705                 db.endTransaction();
706             }
707             sendAccountsChangedBroadcast(accounts.userId);
708         }
709         if (accounts.userId == UserHandle.USER_OWNER) {
710             addAccountToLimitedUsers(account);
711         }
712         return true;
713     }
714 
715     /**
716      * Adds the account to all limited users as shared accounts. If the user is currently
717      * running, then clone the account too.
718      * @param account the account to share with limited users
719      */
addAccountToLimitedUsers(Account account)720     private void addAccountToLimitedUsers(Account account) {
721         List<UserInfo> users = getUserManager().getUsers();
722         for (UserInfo user : users) {
723             if (user.isRestricted()) {
724                 addSharedAccountAsUser(account, user.id);
725                 try {
726                     if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) {
727                         mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
728                                 MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id,
729                                 account));
730                     }
731                 } catch (RemoteException re) {
732                     // Shouldn't happen
733                 }
734             }
735         }
736     }
737 
insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value)738     private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
739         ContentValues values = new ContentValues();
740         values.put(EXTRAS_KEY, key);
741         values.put(EXTRAS_ACCOUNTS_ID, accountId);
742         values.put(EXTRAS_VALUE, value);
743         return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
744     }
745 
hasFeatures(IAccountManagerResponse response, Account account, String[] features)746     public void hasFeatures(IAccountManagerResponse response,
747             Account account, String[] features) {
748         if (Log.isLoggable(TAG, Log.VERBOSE)) {
749             Log.v(TAG, "hasFeatures: " + account
750                     + ", response " + response
751                     + ", features " + stringArrayToString(features)
752                     + ", caller's uid " + Binder.getCallingUid()
753                     + ", pid " + Binder.getCallingPid());
754         }
755         if (response == null) throw new IllegalArgumentException("response is null");
756         if (account == null) throw new IllegalArgumentException("account is null");
757         if (features == null) throw new IllegalArgumentException("features is null");
758         checkReadAccountsPermission();
759         UserAccounts accounts = getUserAccountsForCaller();
760         long identityToken = clearCallingIdentity();
761         try {
762             new TestFeaturesSession(accounts, response, account, features).bind();
763         } finally {
764             restoreCallingIdentity(identityToken);
765         }
766     }
767 
768     private class TestFeaturesSession extends Session {
769         private final String[] mFeatures;
770         private final Account mAccount;
771 
TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, Account account, String[] features)772         public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
773                 Account account, String[] features) {
774             super(accounts, response, account.type, false /* expectActivityLaunch */,
775                     true /* stripAuthTokenFromResult */);
776             mFeatures = features;
777             mAccount = account;
778         }
779 
run()780         public void run() throws RemoteException {
781             try {
782                 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
783             } catch (RemoteException e) {
784                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
785             }
786         }
787 
onResult(Bundle result)788         public void onResult(Bundle result) {
789             IAccountManagerResponse response = getResponseAndClose();
790             if (response != null) {
791                 try {
792                     if (result == null) {
793                         response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
794                         return;
795                     }
796                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
797                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
798                                 + response);
799                     }
800                     final Bundle newResult = new Bundle();
801                     newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
802                             result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
803                     response.onResult(newResult);
804                 } catch (RemoteException e) {
805                     // if the caller is dead then there is no one to care about remote exceptions
806                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
807                         Log.v(TAG, "failure while notifying response", e);
808                     }
809                 }
810             }
811         }
812 
toDebugString(long now)813         protected String toDebugString(long now) {
814             return super.toDebugString(now) + ", hasFeatures"
815                     + ", " + mAccount
816                     + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
817         }
818     }
819 
removeAccount(IAccountManagerResponse response, Account account)820     public void removeAccount(IAccountManagerResponse response, Account account) {
821         if (Log.isLoggable(TAG, Log.VERBOSE)) {
822             Log.v(TAG, "removeAccount: " + account
823                     + ", response " + response
824                     + ", caller's uid " + Binder.getCallingUid()
825                     + ", pid " + Binder.getCallingPid());
826         }
827         if (response == null) throw new IllegalArgumentException("response is null");
828         if (account == null) throw new IllegalArgumentException("account is null");
829         checkManageAccountsPermission();
830         UserHandle user = Binder.getCallingUserHandle();
831         UserAccounts accounts = getUserAccountsForCaller();
832         if (!canUserModifyAccounts(Binder.getCallingUid())) {
833             try {
834                 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
835                         "User cannot modify accounts");
836             } catch (RemoteException re) {
837             }
838         }
839 
840         long identityToken = clearCallingIdentity();
841 
842         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
843         synchronized(accounts.credentialsPermissionNotificationIds) {
844             for (Pair<Pair<Account, String>, Integer> pair:
845                 accounts.credentialsPermissionNotificationIds.keySet()) {
846                 if (account.equals(pair.first.first)) {
847                     int id = accounts.credentialsPermissionNotificationIds.get(pair);
848                     cancelNotification(id, user);
849                 }
850             }
851         }
852 
853         try {
854             new RemoveAccountSession(accounts, response, account).bind();
855         } finally {
856             restoreCallingIdentity(identityToken);
857         }
858     }
859 
860     private class RemoveAccountSession extends Session {
861         final Account mAccount;
RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, Account account)862         public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
863                 Account account) {
864             super(accounts, response, account.type, false /* expectActivityLaunch */,
865                     true /* stripAuthTokenFromResult */);
866             mAccount = account;
867         }
868 
toDebugString(long now)869         protected String toDebugString(long now) {
870             return super.toDebugString(now) + ", removeAccount"
871                     + ", account " + mAccount;
872         }
873 
run()874         public void run() throws RemoteException {
875             mAuthenticator.getAccountRemovalAllowed(this, mAccount);
876         }
877 
onResult(Bundle result)878         public void onResult(Bundle result) {
879             if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
880                     && !result.containsKey(AccountManager.KEY_INTENT)) {
881                 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
882                 if (removalAllowed) {
883                     removeAccountInternal(mAccounts, mAccount);
884                 }
885                 IAccountManagerResponse response = getResponseAndClose();
886                 if (response != null) {
887                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
888                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
889                                 + response);
890                     }
891                     Bundle result2 = new Bundle();
892                     result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
893                     try {
894                         response.onResult(result2);
895                     } catch (RemoteException e) {
896                         // ignore
897                     }
898                 }
899             }
900             super.onResult(result);
901         }
902     }
903 
904     /* For testing */
removeAccountInternal(Account account)905     protected void removeAccountInternal(Account account) {
906         removeAccountInternal(getUserAccountsForCaller(), account);
907     }
908 
removeAccountInternal(UserAccounts accounts, Account account)909     private void removeAccountInternal(UserAccounts accounts, Account account) {
910         synchronized (accounts.cacheLock) {
911             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
912             db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
913                     new String[]{account.name, account.type});
914             removeAccountFromCacheLocked(accounts, account);
915             sendAccountsChangedBroadcast(accounts.userId);
916         }
917         if (accounts.userId == UserHandle.USER_OWNER) {
918             // Owner's account was removed, remove from any users that are sharing
919             // this account.
920             long id = Binder.clearCallingIdentity();
921             try {
922                 List<UserInfo> users = mUserManager.getUsers(true);
923                 for (UserInfo user : users) {
924                     if (!user.isPrimary() && user.isRestricted()) {
925                         removeSharedAccountAsUser(account, user.id);
926                     }
927                 }
928             } finally {
929                 Binder.restoreCallingIdentity(id);
930             }
931         }
932     }
933 
934     @Override
invalidateAuthToken(String accountType, String authToken)935     public void invalidateAuthToken(String accountType, String authToken) {
936         if (Log.isLoggable(TAG, Log.VERBOSE)) {
937             Log.v(TAG, "invalidateAuthToken: accountType " + accountType
938                     + ", caller's uid " + Binder.getCallingUid()
939                     + ", pid " + Binder.getCallingPid());
940         }
941         if (accountType == null) throw new IllegalArgumentException("accountType is null");
942         if (authToken == null) throw new IllegalArgumentException("authToken is null");
943         checkManageAccountsOrUseCredentialsPermissions();
944         UserAccounts accounts = getUserAccountsForCaller();
945         long identityToken = clearCallingIdentity();
946         try {
947             synchronized (accounts.cacheLock) {
948                 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
949                 db.beginTransaction();
950                 try {
951                     invalidateAuthTokenLocked(accounts, db, accountType, authToken);
952                     db.setTransactionSuccessful();
953                 } finally {
954                     db.endTransaction();
955                 }
956             }
957         } finally {
958             restoreCallingIdentity(identityToken);
959         }
960     }
961 
invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, String accountType, String authToken)962     private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
963             String accountType, String authToken) {
964         if (authToken == null || accountType == null) {
965             return;
966         }
967         Cursor cursor = db.rawQuery(
968                 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
969                         + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
970                         + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
971                         + " FROM " + TABLE_ACCOUNTS
972                         + " JOIN " + TABLE_AUTHTOKENS
973                         + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
974                         + " = " + AUTHTOKENS_ACCOUNTS_ID
975                         + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
976                         + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
977                 new String[]{authToken, accountType});
978         try {
979             while (cursor.moveToNext()) {
980                 long authTokenId = cursor.getLong(0);
981                 String accountName = cursor.getString(1);
982                 String authTokenType = cursor.getString(2);
983                 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
984                 writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType),
985                         authTokenType, null);
986             }
987         } finally {
988             cursor.close();
989         }
990     }
991 
saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, String authToken)992     private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
993             String authToken) {
994         if (account == null || type == null) {
995             return false;
996         }
997         cancelNotification(getSigninRequiredNotificationId(accounts, account),
998                 new UserHandle(accounts.userId));
999         synchronized (accounts.cacheLock) {
1000             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1001             db.beginTransaction();
1002             try {
1003                 long accountId = getAccountIdLocked(db, account);
1004                 if (accountId < 0) {
1005                     return false;
1006                 }
1007                 db.delete(TABLE_AUTHTOKENS,
1008                         AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1009                         new String[]{type});
1010                 ContentValues values = new ContentValues();
1011                 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1012                 values.put(AUTHTOKENS_TYPE, type);
1013                 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1014                 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1015                     db.setTransactionSuccessful();
1016                     writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
1017                     return true;
1018                 }
1019                 return false;
1020             } finally {
1021                 db.endTransaction();
1022             }
1023         }
1024     }
1025 
peekAuthToken(Account account, String authTokenType)1026     public String peekAuthToken(Account account, String authTokenType) {
1027         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1028             Log.v(TAG, "peekAuthToken: " + account
1029                     + ", authTokenType " + authTokenType
1030                     + ", caller's uid " + Binder.getCallingUid()
1031                     + ", pid " + Binder.getCallingPid());
1032         }
1033         if (account == null) throw new IllegalArgumentException("account is null");
1034         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1035         checkAuthenticateAccountsPermission(account);
1036         UserAccounts accounts = getUserAccountsForCaller();
1037         long identityToken = clearCallingIdentity();
1038         try {
1039             return readAuthTokenInternal(accounts, account, authTokenType);
1040         } finally {
1041             restoreCallingIdentity(identityToken);
1042         }
1043     }
1044 
setAuthToken(Account account, String authTokenType, String authToken)1045     public void setAuthToken(Account account, String authTokenType, String authToken) {
1046         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1047             Log.v(TAG, "setAuthToken: " + account
1048                     + ", authTokenType " + authTokenType
1049                     + ", caller's uid " + Binder.getCallingUid()
1050                     + ", pid " + Binder.getCallingPid());
1051         }
1052         if (account == null) throw new IllegalArgumentException("account is null");
1053         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1054         checkAuthenticateAccountsPermission(account);
1055         UserAccounts accounts = getUserAccountsForCaller();
1056         long identityToken = clearCallingIdentity();
1057         try {
1058             saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
1059         } finally {
1060             restoreCallingIdentity(identityToken);
1061         }
1062     }
1063 
setPassword(Account account, String password)1064     public void setPassword(Account account, String password) {
1065         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1066             Log.v(TAG, "setAuthToken: " + account
1067                     + ", caller's uid " + Binder.getCallingUid()
1068                     + ", pid " + Binder.getCallingPid());
1069         }
1070         if (account == null) throw new IllegalArgumentException("account is null");
1071         checkAuthenticateAccountsPermission(account);
1072         UserAccounts accounts = getUserAccountsForCaller();
1073         long identityToken = clearCallingIdentity();
1074         try {
1075             setPasswordInternal(accounts, account, password);
1076         } finally {
1077             restoreCallingIdentity(identityToken);
1078         }
1079     }
1080 
setPasswordInternal(UserAccounts accounts, Account account, String password)1081     private void setPasswordInternal(UserAccounts accounts, Account account, String password) {
1082         if (account == null) {
1083             return;
1084         }
1085         synchronized (accounts.cacheLock) {
1086             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1087             db.beginTransaction();
1088             try {
1089                 final ContentValues values = new ContentValues();
1090                 values.put(ACCOUNTS_PASSWORD, password);
1091                 final long accountId = getAccountIdLocked(db, account);
1092                 if (accountId >= 0) {
1093                     final String[] argsAccountId = {String.valueOf(accountId)};
1094                     db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1095                     db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
1096                     accounts.authTokenCache.remove(account);
1097                     db.setTransactionSuccessful();
1098                 }
1099             } finally {
1100                 db.endTransaction();
1101             }
1102             sendAccountsChangedBroadcast(accounts.userId);
1103         }
1104     }
1105 
sendAccountsChangedBroadcast(int userId)1106     private void sendAccountsChangedBroadcast(int userId) {
1107         Log.i(TAG, "the accounts changed, sending broadcast of "
1108                 + ACCOUNTS_CHANGED_INTENT.getAction());
1109         mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
1110     }
1111 
clearPassword(Account account)1112     public void clearPassword(Account account) {
1113         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1114             Log.v(TAG, "clearPassword: " + account
1115                     + ", caller's uid " + Binder.getCallingUid()
1116                     + ", pid " + Binder.getCallingPid());
1117         }
1118         if (account == null) throw new IllegalArgumentException("account is null");
1119         checkManageAccountsPermission();
1120         UserAccounts accounts = getUserAccountsForCaller();
1121         long identityToken = clearCallingIdentity();
1122         try {
1123             setPasswordInternal(accounts, account, null);
1124         } finally {
1125             restoreCallingIdentity(identityToken);
1126         }
1127     }
1128 
setUserData(Account account, String key, String value)1129     public void setUserData(Account account, String key, String value) {
1130         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1131             Log.v(TAG, "setUserData: " + account
1132                     + ", key " + key
1133                     + ", caller's uid " + Binder.getCallingUid()
1134                     + ", pid " + Binder.getCallingPid());
1135         }
1136         if (key == null) throw new IllegalArgumentException("key is null");
1137         if (account == null) throw new IllegalArgumentException("account is null");
1138         checkAuthenticateAccountsPermission(account);
1139         UserAccounts accounts = getUserAccountsForCaller();
1140         long identityToken = clearCallingIdentity();
1141         try {
1142             setUserdataInternal(accounts, account, key, value);
1143         } finally {
1144             restoreCallingIdentity(identityToken);
1145         }
1146     }
1147 
setUserdataInternal(UserAccounts accounts, Account account, String key, String value)1148     private void setUserdataInternal(UserAccounts accounts, Account account, String key,
1149             String value) {
1150         if (account == null || key == null) {
1151             return;
1152         }
1153         synchronized (accounts.cacheLock) {
1154             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1155             db.beginTransaction();
1156             try {
1157                 long accountId = getAccountIdLocked(db, account);
1158                 if (accountId < 0) {
1159                     return;
1160                 }
1161                 long extrasId = getExtrasIdLocked(db, accountId, key);
1162                 if (extrasId < 0 ) {
1163                     extrasId = insertExtraLocked(db, accountId, key, value);
1164                     if (extrasId < 0) {
1165                         return;
1166                     }
1167                 } else {
1168                     ContentValues values = new ContentValues();
1169                     values.put(EXTRAS_VALUE, value);
1170                     if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1171                         return;
1172                     }
1173 
1174                 }
1175                 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
1176                 db.setTransactionSuccessful();
1177             } finally {
1178                 db.endTransaction();
1179             }
1180         }
1181     }
1182 
onResult(IAccountManagerResponse response, Bundle result)1183     private void onResult(IAccountManagerResponse response, Bundle result) {
1184         if (result == null) {
1185             Log.e(TAG, "the result is unexpectedly null", new Exception());
1186         }
1187         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1188             Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1189                     + response);
1190         }
1191         try {
1192             response.onResult(result);
1193         } catch (RemoteException e) {
1194             // if the caller is dead then there is no one to care about remote
1195             // exceptions
1196             if (Log.isLoggable(TAG, Log.VERBOSE)) {
1197                 Log.v(TAG, "failure while notifying response", e);
1198             }
1199         }
1200     }
1201 
getAuthTokenLabel(IAccountManagerResponse response, final String accountType, final String authTokenType)1202     public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1203                                   final String authTokenType)
1204             throws RemoteException {
1205         if (accountType == null) throw new IllegalArgumentException("accountType is null");
1206         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1207 
1208         final int callingUid = getCallingUid();
1209         clearCallingIdentity();
1210         if (callingUid != Process.SYSTEM_UID) {
1211             throw new SecurityException("can only call from system");
1212         }
1213         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
1214         long identityToken = clearCallingIdentity();
1215         try {
1216             new Session(accounts, response, accountType, false,
1217                     false /* stripAuthTokenFromResult */) {
1218                 protected String toDebugString(long now) {
1219                     return super.toDebugString(now) + ", getAuthTokenLabel"
1220                             + ", " + accountType
1221                             + ", authTokenType " + authTokenType;
1222                 }
1223 
1224                 public void run() throws RemoteException {
1225                     mAuthenticator.getAuthTokenLabel(this, authTokenType);
1226                 }
1227 
1228                 public void onResult(Bundle result) {
1229                     if (result != null) {
1230                         String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1231                         Bundle bundle = new Bundle();
1232                         bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1233                         super.onResult(bundle);
1234                         return;
1235                     } else {
1236                         super.onResult(result);
1237                     }
1238                 }
1239             }.bind();
1240         } finally {
1241             restoreCallingIdentity(identityToken);
1242         }
1243     }
1244 
getAuthToken(IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, final boolean expectActivityLaunch, Bundle loginOptionsIn)1245     public void getAuthToken(IAccountManagerResponse response, final Account account,
1246             final String authTokenType, final boolean notifyOnAuthFailure,
1247             final boolean expectActivityLaunch, Bundle loginOptionsIn) {
1248         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1249             Log.v(TAG, "getAuthToken: " + account
1250                     + ", response " + response
1251                     + ", authTokenType " + authTokenType
1252                     + ", notifyOnAuthFailure " + notifyOnAuthFailure
1253                     + ", expectActivityLaunch " + expectActivityLaunch
1254                     + ", caller's uid " + Binder.getCallingUid()
1255                     + ", pid " + Binder.getCallingPid());
1256         }
1257         if (response == null) throw new IllegalArgumentException("response is null");
1258         if (account == null) throw new IllegalArgumentException("account is null");
1259         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1260         checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
1261         final UserAccounts accounts = getUserAccountsForCaller();
1262         final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
1263         authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1264                 AuthenticatorDescription.newKey(account.type), accounts.userId);
1265         final boolean customTokens =
1266             authenticatorInfo != null && authenticatorInfo.type.customTokens;
1267 
1268         // Check to see that the app is authorized to access the account, in case it's a
1269         // restricted account.
1270         if (!ArrayUtils.contains(getAccounts((String) null), account)) {
1271             throw new IllegalArgumentException("no such account");
1272         }
1273         // skip the check if customTokens
1274         final int callerUid = Binder.getCallingUid();
1275         final boolean permissionGranted = customTokens ||
1276             permissionIsGranted(account, authTokenType, callerUid);
1277 
1278         final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
1279             loginOptionsIn;
1280         // let authenticator know the identity of the caller
1281         loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1282         loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
1283         if (notifyOnAuthFailure) {
1284             loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
1285         }
1286 
1287         long identityToken = clearCallingIdentity();
1288         try {
1289             // if the caller has permission, do the peek. otherwise go the more expensive
1290             // route of starting a Session
1291             if (!customTokens && permissionGranted) {
1292                 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
1293                 if (authToken != null) {
1294                     Bundle result = new Bundle();
1295                     result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1296                     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1297                     result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
1298                     onResult(response, result);
1299                     return;
1300                 }
1301             }
1302 
1303             new Session(accounts, response, account.type, expectActivityLaunch,
1304                     false /* stripAuthTokenFromResult */) {
1305                 protected String toDebugString(long now) {
1306                     if (loginOptions != null) loginOptions.keySet();
1307                     return super.toDebugString(now) + ", getAuthToken"
1308                             + ", " + account
1309                             + ", authTokenType " + authTokenType
1310                             + ", loginOptions " + loginOptions
1311                             + ", notifyOnAuthFailure " + notifyOnAuthFailure;
1312                 }
1313 
1314                 public void run() throws RemoteException {
1315                     // If the caller doesn't have permission then create and return the
1316                     // "grant permission" intent instead of the "getAuthToken" intent.
1317                     if (!permissionGranted) {
1318                         mAuthenticator.getAuthTokenLabel(this, authTokenType);
1319                     } else {
1320                         mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
1321                     }
1322                 }
1323 
1324                 public void onResult(Bundle result) {
1325                     if (result != null) {
1326                         if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
1327                             Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
1328                                     new AccountAuthenticatorResponse(this),
1329                                     authTokenType,
1330                                     result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL));
1331                             Bundle bundle = new Bundle();
1332                             bundle.putParcelable(AccountManager.KEY_INTENT, intent);
1333                             onResult(bundle);
1334                             return;
1335                         }
1336                         String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
1337                         if (authToken != null) {
1338                             String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1339                             String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
1340                             if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
1341                                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
1342                                         "the type and name should not be empty");
1343                                 return;
1344                             }
1345                             if (!customTokens) {
1346                                 saveAuthTokenToDatabase(mAccounts, new Account(name, type),
1347                                         authTokenType, authToken);
1348                             }
1349                         }
1350 
1351                         Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
1352                         if (intent != null && notifyOnAuthFailure && !customTokens) {
1353                             doNotification(mAccounts,
1354                                     account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
1355                                     intent, accounts.userId);
1356                         }
1357                     }
1358                     super.onResult(result);
1359                 }
1360             }.bind();
1361         } finally {
1362             restoreCallingIdentity(identityToken);
1363         }
1364     }
1365 
createNoCredentialsPermissionNotification(Account account, Intent intent, int userId)1366     private void createNoCredentialsPermissionNotification(Account account, Intent intent,
1367             int userId) {
1368         int uid = intent.getIntExtra(
1369                 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
1370         String authTokenType = intent.getStringExtra(
1371                 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
1372         String authTokenLabel = intent.getStringExtra(
1373                 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);
1374 
1375         Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
1376                 0 /* when */);
1377         final String titleAndSubtitle =
1378                 mContext.getString(R.string.permission_request_notification_with_subtitle,
1379                 account.name);
1380         final int index = titleAndSubtitle.indexOf('\n');
1381         String title = titleAndSubtitle;
1382         String subtitle = "";
1383         if (index > 0) {
1384             title = titleAndSubtitle.substring(0, index);
1385             subtitle = titleAndSubtitle.substring(index + 1);
1386         }
1387         UserHandle user = new UserHandle(userId);
1388         n.setLatestEventInfo(mContext, title, subtitle,
1389                 PendingIntent.getActivityAsUser(mContext, 0, intent,
1390                         PendingIntent.FLAG_CANCEL_CURRENT, null, user));
1391         installNotification(getCredentialPermissionNotificationId(
1392                 account, authTokenType, uid), n, user);
1393     }
1394 
newGrantCredentialsPermissionIntent(Account account, int uid, AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel)1395     private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
1396             AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
1397 
1398         Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
1399         // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
1400         // Since it was set in Eclair+ we can't change it without breaking apps using
1401         // the intent from a non-Activity context.
1402         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1403         intent.addCategory(
1404                 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
1405 
1406         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
1407         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
1408         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
1409         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
1410 
1411         return intent;
1412     }
1413 
getCredentialPermissionNotificationId(Account account, String authTokenType, int uid)1414     private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
1415             int uid) {
1416         Integer id;
1417         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
1418         synchronized (accounts.credentialsPermissionNotificationIds) {
1419             final Pair<Pair<Account, String>, Integer> key =
1420                     new Pair<Pair<Account, String>, Integer>(
1421                             new Pair<Account, String>(account, authTokenType), uid);
1422             id = accounts.credentialsPermissionNotificationIds.get(key);
1423             if (id == null) {
1424                 id = mNotificationIds.incrementAndGet();
1425                 accounts.credentialsPermissionNotificationIds.put(key, id);
1426             }
1427         }
1428         return id;
1429     }
1430 
getSigninRequiredNotificationId(UserAccounts accounts, Account account)1431     private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
1432         Integer id;
1433         synchronized (accounts.signinRequiredNotificationIds) {
1434             id = accounts.signinRequiredNotificationIds.get(account);
1435             if (id == null) {
1436                 id = mNotificationIds.incrementAndGet();
1437                 accounts.signinRequiredNotificationIds.put(account, id);
1438             }
1439         }
1440         return id;
1441     }
1442 
addAccount(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn)1443     public void addAccount(final IAccountManagerResponse response, final String accountType,
1444             final String authTokenType, final String[] requiredFeatures,
1445             final boolean expectActivityLaunch, final Bundle optionsIn) {
1446         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1447             Log.v(TAG, "addAccount: accountType " + accountType
1448                     + ", response " + response
1449                     + ", authTokenType " + authTokenType
1450                     + ", requiredFeatures " + stringArrayToString(requiredFeatures)
1451                     + ", expectActivityLaunch " + expectActivityLaunch
1452                     + ", caller's uid " + Binder.getCallingUid()
1453                     + ", pid " + Binder.getCallingPid());
1454         }
1455         if (response == null) throw new IllegalArgumentException("response is null");
1456         if (accountType == null) throw new IllegalArgumentException("accountType is null");
1457         checkManageAccountsPermission();
1458 
1459         // Is user disallowed from modifying accounts?
1460         if (!canUserModifyAccounts(Binder.getCallingUid())) {
1461             try {
1462                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1463                         "User is not allowed to add an account!");
1464             } catch (RemoteException re) {
1465             }
1466             Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
1467             cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1468             long identityToken = clearCallingIdentity();
1469             try {
1470                 mContext.startActivityAsUser(cantAddAccount, UserHandle.CURRENT);
1471             } finally {
1472                 restoreCallingIdentity(identityToken);
1473             }
1474             return;
1475         }
1476 
1477         UserAccounts accounts = getUserAccountsForCaller();
1478         final int pid = Binder.getCallingPid();
1479         final int uid = Binder.getCallingUid();
1480         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
1481         options.putInt(AccountManager.KEY_CALLER_UID, uid);
1482         options.putInt(AccountManager.KEY_CALLER_PID, pid);
1483 
1484         long identityToken = clearCallingIdentity();
1485         try {
1486             new Session(accounts, response, accountType, expectActivityLaunch,
1487                     true /* stripAuthTokenFromResult */) {
1488                 public void run() throws RemoteException {
1489                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
1490                             options);
1491                 }
1492 
1493                 protected String toDebugString(long now) {
1494                     return super.toDebugString(now) + ", addAccount"
1495                             + ", accountType " + accountType
1496                             + ", requiredFeatures "
1497                             + (requiredFeatures != null
1498                               ? TextUtils.join(",", requiredFeatures)
1499                               : null);
1500                 }
1501             }.bind();
1502         } finally {
1503             restoreCallingIdentity(identityToken);
1504         }
1505     }
1506 
1507     @Override
confirmCredentialsAsUser(IAccountManagerResponse response, final Account account, final Bundle options, final boolean expectActivityLaunch, int userId)1508     public void confirmCredentialsAsUser(IAccountManagerResponse response,
1509             final Account account, final Bundle options, final boolean expectActivityLaunch,
1510             int userId) {
1511         // Only allow the system process to read accounts of other users
1512         if (userId != UserHandle.getCallingUserId()
1513                 && Binder.getCallingUid() != Process.myUid()) {
1514             throw new SecurityException("User " + UserHandle.getCallingUserId()
1515                     + " trying to confirm account credentials for " + userId);
1516         }
1517 
1518         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1519             Log.v(TAG, "confirmCredentials: " + account
1520                     + ", response " + response
1521                     + ", expectActivityLaunch " + expectActivityLaunch
1522                     + ", caller's uid " + Binder.getCallingUid()
1523                     + ", pid " + Binder.getCallingPid());
1524         }
1525         if (response == null) throw new IllegalArgumentException("response is null");
1526         if (account == null) throw new IllegalArgumentException("account is null");
1527         checkManageAccountsPermission();
1528         UserAccounts accounts = getUserAccounts(userId);
1529         long identityToken = clearCallingIdentity();
1530         try {
1531             new Session(accounts, response, account.type, expectActivityLaunch,
1532                     true /* stripAuthTokenFromResult */) {
1533                 public void run() throws RemoteException {
1534                     mAuthenticator.confirmCredentials(this, account, options);
1535                 }
1536                 protected String toDebugString(long now) {
1537                     return super.toDebugString(now) + ", confirmCredentials"
1538                             + ", " + account;
1539                 }
1540             }.bind();
1541         } finally {
1542             restoreCallingIdentity(identityToken);
1543         }
1544     }
1545 
updateCredentials(IAccountManagerResponse response, final Account account, final String authTokenType, final boolean expectActivityLaunch, final Bundle loginOptions)1546     public void updateCredentials(IAccountManagerResponse response, final Account account,
1547             final String authTokenType, final boolean expectActivityLaunch,
1548             final Bundle loginOptions) {
1549         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1550             Log.v(TAG, "updateCredentials: " + account
1551                     + ", response " + response
1552                     + ", authTokenType " + authTokenType
1553                     + ", expectActivityLaunch " + expectActivityLaunch
1554                     + ", caller's uid " + Binder.getCallingUid()
1555                     + ", pid " + Binder.getCallingPid());
1556         }
1557         if (response == null) throw new IllegalArgumentException("response is null");
1558         if (account == null) throw new IllegalArgumentException("account is null");
1559         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1560         checkManageAccountsPermission();
1561         UserAccounts accounts = getUserAccountsForCaller();
1562         long identityToken = clearCallingIdentity();
1563         try {
1564             new Session(accounts, response, account.type, expectActivityLaunch,
1565                     true /* stripAuthTokenFromResult */) {
1566                 public void run() throws RemoteException {
1567                     mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
1568                 }
1569                 protected String toDebugString(long now) {
1570                     if (loginOptions != null) loginOptions.keySet();
1571                     return super.toDebugString(now) + ", updateCredentials"
1572                             + ", " + account
1573                             + ", authTokenType " + authTokenType
1574                             + ", loginOptions " + loginOptions;
1575                 }
1576             }.bind();
1577         } finally {
1578             restoreCallingIdentity(identityToken);
1579         }
1580     }
1581 
editProperties(IAccountManagerResponse response, final String accountType, final boolean expectActivityLaunch)1582     public void editProperties(IAccountManagerResponse response, final String accountType,
1583             final boolean expectActivityLaunch) {
1584         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1585             Log.v(TAG, "editProperties: accountType " + accountType
1586                     + ", response " + response
1587                     + ", expectActivityLaunch " + expectActivityLaunch
1588                     + ", caller's uid " + Binder.getCallingUid()
1589                     + ", pid " + Binder.getCallingPid());
1590         }
1591         if (response == null) throw new IllegalArgumentException("response is null");
1592         if (accountType == null) throw new IllegalArgumentException("accountType is null");
1593         checkManageAccountsPermission();
1594         UserAccounts accounts = getUserAccountsForCaller();
1595         long identityToken = clearCallingIdentity();
1596         try {
1597             new Session(accounts, response, accountType, expectActivityLaunch,
1598                     true /* stripAuthTokenFromResult */) {
1599                 public void run() throws RemoteException {
1600                     mAuthenticator.editProperties(this, mAccountType);
1601                 }
1602                 protected String toDebugString(long now) {
1603                     return super.toDebugString(now) + ", editProperties"
1604                             + ", accountType " + accountType;
1605                 }
1606             }.bind();
1607         } finally {
1608             restoreCallingIdentity(identityToken);
1609         }
1610     }
1611 
1612     private class GetAccountsByTypeAndFeatureSession extends Session {
1613         private final String[] mFeatures;
1614         private volatile Account[] mAccountsOfType = null;
1615         private volatile ArrayList<Account> mAccountsWithFeatures = null;
1616         private volatile int mCurrentAccount = 0;
1617         private int mCallingUid;
1618 
GetAccountsByTypeAndFeatureSession(UserAccounts accounts, IAccountManagerResponse response, String type, String[] features, int callingUid)1619         public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
1620                 IAccountManagerResponse response, String type, String[] features, int callingUid) {
1621             super(accounts, response, type, false /* expectActivityLaunch */,
1622                     true /* stripAuthTokenFromResult */);
1623             mCallingUid = callingUid;
1624             mFeatures = features;
1625         }
1626 
run()1627         public void run() throws RemoteException {
1628             synchronized (mAccounts.cacheLock) {
1629                 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
1630                         null);
1631             }
1632             // check whether each account matches the requested features
1633             mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
1634             mCurrentAccount = 0;
1635 
1636             checkAccount();
1637         }
1638 
checkAccount()1639         public void checkAccount() {
1640             if (mCurrentAccount >= mAccountsOfType.length) {
1641                 sendResult();
1642                 return;
1643             }
1644 
1645             final IAccountAuthenticator accountAuthenticator = mAuthenticator;
1646             if (accountAuthenticator == null) {
1647                 // It is possible that the authenticator has died, which is indicated by
1648                 // mAuthenticator being set to null. If this happens then just abort.
1649                 // There is no need to send back a result or error in this case since
1650                 // that already happened when mAuthenticator was cleared.
1651                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1652                     Log.v(TAG, "checkAccount: aborting session since we are no longer"
1653                             + " connected to the authenticator, " + toDebugString());
1654                 }
1655                 return;
1656             }
1657             try {
1658                 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
1659             } catch (RemoteException e) {
1660                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1661             }
1662         }
1663 
onResult(Bundle result)1664         public void onResult(Bundle result) {
1665             mNumResults++;
1666             if (result == null) {
1667                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
1668                 return;
1669             }
1670             if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1671                 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
1672             }
1673             mCurrentAccount++;
1674             checkAccount();
1675         }
1676 
sendResult()1677         public void sendResult() {
1678             IAccountManagerResponse response = getResponseAndClose();
1679             if (response != null) {
1680                 try {
1681                     Account[] accounts = new Account[mAccountsWithFeatures.size()];
1682                     for (int i = 0; i < accounts.length; i++) {
1683                         accounts[i] = mAccountsWithFeatures.get(i);
1684                     }
1685                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
1686                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1687                                 + response);
1688                     }
1689                     Bundle result = new Bundle();
1690                     result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
1691                     response.onResult(result);
1692                 } catch (RemoteException e) {
1693                     // if the caller is dead then there is no one to care about remote exceptions
1694                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
1695                         Log.v(TAG, "failure while notifying response", e);
1696                     }
1697                 }
1698             }
1699         }
1700 
1701 
toDebugString(long now)1702         protected String toDebugString(long now) {
1703             return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
1704                     + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1705         }
1706     }
1707 
1708     /**
1709      * Returns the accounts for a specific user
1710      * @hide
1711      */
getAccounts(int userId)1712     public Account[] getAccounts(int userId) {
1713         checkReadAccountsPermission();
1714         UserAccounts accounts = getUserAccounts(userId);
1715         int callingUid = Binder.getCallingUid();
1716         long identityToken = clearCallingIdentity();
1717         try {
1718             synchronized (accounts.cacheLock) {
1719                 return getAccountsFromCacheLocked(accounts, null, callingUid, null);
1720             }
1721         } finally {
1722             restoreCallingIdentity(identityToken);
1723         }
1724     }
1725 
1726     /**
1727      * Returns accounts for all running users.
1728      *
1729      * @hide
1730      */
getRunningAccounts()1731     public AccountAndUser[] getRunningAccounts() {
1732         final int[] runningUserIds;
1733         try {
1734             runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
1735         } catch (RemoteException e) {
1736             // Running in system_server; should never happen
1737             throw new RuntimeException(e);
1738         }
1739         return getAccounts(runningUserIds);
1740     }
1741 
1742     /** {@hide} */
getAllAccounts()1743     public AccountAndUser[] getAllAccounts() {
1744         final List<UserInfo> users = getUserManager().getUsers();
1745         final int[] userIds = new int[users.size()];
1746         for (int i = 0; i < userIds.length; i++) {
1747             userIds[i] = users.get(i).id;
1748         }
1749         return getAccounts(userIds);
1750     }
1751 
getAccounts(int[] userIds)1752     private AccountAndUser[] getAccounts(int[] userIds) {
1753         final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
1754         synchronized (mUsers) {
1755             for (int userId : userIds) {
1756                 UserAccounts userAccounts = getUserAccounts(userId);
1757                 if (userAccounts == null) continue;
1758                 synchronized (userAccounts.cacheLock) {
1759                     Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
1760                             Binder.getCallingUid(), null);
1761                     for (int a = 0; a < accounts.length; a++) {
1762                         runningAccounts.add(new AccountAndUser(accounts[a], userId));
1763                     }
1764                 }
1765             }
1766         }
1767 
1768         AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
1769         return runningAccounts.toArray(accountsArray);
1770     }
1771 
1772     @Override
getAccountsAsUser(String type, int userId)1773     public Account[] getAccountsAsUser(String type, int userId) {
1774         return getAccountsAsUser(type, userId, null, -1);
1775     }
1776 
getAccountsAsUser(String type, int userId, String callingPackage, int packageUid)1777     private Account[] getAccountsAsUser(String type, int userId, String callingPackage,
1778             int packageUid) {
1779         int callingUid = Binder.getCallingUid();
1780         // Only allow the system process to read accounts of other users
1781         if (userId != UserHandle.getCallingUserId()
1782                 && callingUid != Process.myUid()) {
1783             throw new SecurityException("User " + UserHandle.getCallingUserId()
1784                     + " trying to get account for " + userId);
1785         }
1786 
1787         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1788             Log.v(TAG, "getAccounts: accountType " + type
1789                     + ", caller's uid " + Binder.getCallingUid()
1790                     + ", pid " + Binder.getCallingPid());
1791         }
1792         // If the original calling app was using the framework account chooser activity, we'll
1793         // be passed in the original caller's uid here, which is what should be used for filtering.
1794         if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
1795             callingUid = packageUid;
1796         }
1797         checkReadAccountsPermission();
1798         UserAccounts accounts = getUserAccounts(userId);
1799         long identityToken = clearCallingIdentity();
1800         try {
1801             synchronized (accounts.cacheLock) {
1802                 return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
1803             }
1804         } finally {
1805             restoreCallingIdentity(identityToken);
1806         }
1807     }
1808 
1809     @Override
addSharedAccountAsUser(Account account, int userId)1810     public boolean addSharedAccountAsUser(Account account, int userId) {
1811         userId = handleIncomingUser(userId);
1812         SQLiteDatabase db = getUserAccounts(userId).openHelper.getWritableDatabase();
1813         ContentValues values = new ContentValues();
1814         values.put(ACCOUNTS_NAME, account.name);
1815         values.put(ACCOUNTS_TYPE, account.type);
1816         db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1817                 new String[] {account.name, account.type});
1818         long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
1819         if (accountId < 0) {
1820             Log.w(TAG, "insertAccountIntoDatabase: " + account
1821                     + ", skipping the DB insert failed");
1822             return false;
1823         }
1824         return true;
1825     }
1826 
1827     @Override
removeSharedAccountAsUser(Account account, int userId)1828     public boolean removeSharedAccountAsUser(Account account, int userId) {
1829         userId = handleIncomingUser(userId);
1830         UserAccounts accounts = getUserAccounts(userId);
1831         SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1832         int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1833                 new String[] {account.name, account.type});
1834         if (r > 0) {
1835             removeAccountInternal(accounts, account);
1836         }
1837         return r > 0;
1838     }
1839 
1840     @Override
getSharedAccountsAsUser(int userId)1841     public Account[] getSharedAccountsAsUser(int userId) {
1842         userId = handleIncomingUser(userId);
1843         UserAccounts accounts = getUserAccounts(userId);
1844         ArrayList<Account> accountList = new ArrayList<Account>();
1845         Cursor cursor = null;
1846         try {
1847             cursor = accounts.openHelper.getReadableDatabase()
1848                     .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
1849                     null, null, null, null, null);
1850             if (cursor != null && cursor.moveToFirst()) {
1851                 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
1852                 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
1853                 do {
1854                     accountList.add(new Account(cursor.getString(nameIndex),
1855                             cursor.getString(typeIndex)));
1856                 } while (cursor.moveToNext());
1857             }
1858         } finally {
1859             if (cursor != null) {
1860                 cursor.close();
1861             }
1862         }
1863         Account[] accountArray = new Account[accountList.size()];
1864         accountList.toArray(accountArray);
1865         return accountArray;
1866     }
1867 
1868     @Override
getAccounts(String type)1869     public Account[] getAccounts(String type) {
1870         return getAccountsAsUser(type, UserHandle.getCallingUserId());
1871     }
1872 
1873     @Override
getAccountsForPackage(String packageName, int uid)1874     public Account[] getAccountsForPackage(String packageName, int uid) {
1875         int callingUid = Binder.getCallingUid();
1876         if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
1877             throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
1878                     + callingUid + " with uid=" + uid);
1879         }
1880         return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid);
1881     }
1882 
1883     @Override
getAccountsByTypeForPackage(String type, String packageName)1884     public Account[] getAccountsByTypeForPackage(String type, String packageName) {
1885         checkBinderPermission(android.Manifest.permission.INTERACT_ACROSS_USERS);
1886         int packageUid = -1;
1887         try {
1888             packageUid = AppGlobals.getPackageManager().getPackageUid(
1889                     packageName, UserHandle.getCallingUserId());
1890         } catch (RemoteException re) {
1891             Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
1892             return new Account[0];
1893         }
1894         return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid);
1895     }
1896 
getAccountsByFeatures(IAccountManagerResponse response, String type, String[] features)1897     public void getAccountsByFeatures(IAccountManagerResponse response,
1898             String type, String[] features) {
1899         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1900             Log.v(TAG, "getAccounts: accountType " + type
1901                     + ", response " + response
1902                     + ", features " + stringArrayToString(features)
1903                     + ", caller's uid " + Binder.getCallingUid()
1904                     + ", pid " + Binder.getCallingPid());
1905         }
1906         if (response == null) throw new IllegalArgumentException("response is null");
1907         if (type == null) throw new IllegalArgumentException("accountType is null");
1908         checkReadAccountsPermission();
1909         UserAccounts userAccounts = getUserAccountsForCaller();
1910         int callingUid = Binder.getCallingUid();
1911         long identityToken = clearCallingIdentity();
1912         try {
1913             if (features == null || features.length == 0) {
1914                 Account[] accounts;
1915                 synchronized (userAccounts.cacheLock) {
1916                     accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
1917                 }
1918                 Bundle result = new Bundle();
1919                 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
1920                 onResult(response, result);
1921                 return;
1922             }
1923             new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features,
1924                     callingUid).bind();
1925         } finally {
1926             restoreCallingIdentity(identityToken);
1927         }
1928     }
1929 
getAccountIdLocked(SQLiteDatabase db, Account account)1930     private long getAccountIdLocked(SQLiteDatabase db, Account account) {
1931         Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
1932                 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
1933         try {
1934             if (cursor.moveToNext()) {
1935                 return cursor.getLong(0);
1936             }
1937             return -1;
1938         } finally {
1939             cursor.close();
1940         }
1941     }
1942 
getExtrasIdLocked(SQLiteDatabase db, long accountId, String key)1943     private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
1944         Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
1945                 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
1946                 new String[]{key}, null, null, null);
1947         try {
1948             if (cursor.moveToNext()) {
1949                 return cursor.getLong(0);
1950             }
1951             return -1;
1952         } finally {
1953             cursor.close();
1954         }
1955     }
1956 
1957     private abstract class Session extends IAccountAuthenticatorResponse.Stub
1958             implements IBinder.DeathRecipient, ServiceConnection {
1959         IAccountManagerResponse mResponse;
1960         final String mAccountType;
1961         final boolean mExpectActivityLaunch;
1962         final long mCreationTime;
1963 
1964         public int mNumResults = 0;
1965         private int mNumRequestContinued = 0;
1966         private int mNumErrors = 0;
1967 
1968         IAccountAuthenticator mAuthenticator = null;
1969 
1970         private final boolean mStripAuthTokenFromResult;
1971         protected final UserAccounts mAccounts;
1972 
Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch, boolean stripAuthTokenFromResult)1973         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
1974                 boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
1975             super();
1976             //if (response == null) throw new IllegalArgumentException("response is null");
1977             if (accountType == null) throw new IllegalArgumentException("accountType is null");
1978             mAccounts = accounts;
1979             mStripAuthTokenFromResult = stripAuthTokenFromResult;
1980             mResponse = response;
1981             mAccountType = accountType;
1982             mExpectActivityLaunch = expectActivityLaunch;
1983             mCreationTime = SystemClock.elapsedRealtime();
1984             synchronized (mSessions) {
1985                 mSessions.put(toString(), this);
1986             }
1987             if (response != null) {
1988                 try {
1989                     response.asBinder().linkToDeath(this, 0 /* flags */);
1990                 } catch (RemoteException e) {
1991                     mResponse = null;
1992                     binderDied();
1993                 }
1994             }
1995         }
1996 
getResponseAndClose()1997         IAccountManagerResponse getResponseAndClose() {
1998             if (mResponse == null) {
1999                 // this session has already been closed
2000                 return null;
2001             }
2002             IAccountManagerResponse response = mResponse;
2003             close(); // this clears mResponse so we need to save the response before this call
2004             return response;
2005         }
2006 
close()2007         private void close() {
2008             synchronized (mSessions) {
2009                 if (mSessions.remove(toString()) == null) {
2010                     // the session was already closed, so bail out now
2011                     return;
2012                 }
2013             }
2014             if (mResponse != null) {
2015                 // stop listening for response deaths
2016                 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
2017 
2018                 // clear this so that we don't accidentally send any further results
2019                 mResponse = null;
2020             }
2021             cancelTimeout();
2022             unbind();
2023         }
2024 
binderDied()2025         public void binderDied() {
2026             mResponse = null;
2027             close();
2028         }
2029 
toDebugString()2030         protected String toDebugString() {
2031             return toDebugString(SystemClock.elapsedRealtime());
2032         }
2033 
toDebugString(long now)2034         protected String toDebugString(long now) {
2035             return "Session: expectLaunch " + mExpectActivityLaunch
2036                     + ", connected " + (mAuthenticator != null)
2037                     + ", stats (" + mNumResults + "/" + mNumRequestContinued
2038                     + "/" + mNumErrors + ")"
2039                     + ", lifetime " + ((now - mCreationTime) / 1000.0);
2040         }
2041 
bind()2042         void bind() {
2043             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2044                 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
2045             }
2046             if (!bindToAuthenticator(mAccountType)) {
2047                 Log.d(TAG, "bind attempt failed for " + toDebugString());
2048                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
2049             }
2050         }
2051 
unbind()2052         private void unbind() {
2053             if (mAuthenticator != null) {
2054                 mAuthenticator = null;
2055                 mContext.unbindService(this);
2056             }
2057         }
2058 
scheduleTimeout()2059         public void scheduleTimeout() {
2060             mMessageHandler.sendMessageDelayed(
2061                     mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
2062         }
2063 
cancelTimeout()2064         public void cancelTimeout() {
2065             mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
2066         }
2067 
onServiceConnected(ComponentName name, IBinder service)2068         public void onServiceConnected(ComponentName name, IBinder service) {
2069             mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
2070             try {
2071                 run();
2072             } catch (RemoteException e) {
2073                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
2074                         "remote exception");
2075             }
2076         }
2077 
onServiceDisconnected(ComponentName name)2078         public void onServiceDisconnected(ComponentName name) {
2079             mAuthenticator = null;
2080             IAccountManagerResponse response = getResponseAndClose();
2081             if (response != null) {
2082                 try {
2083                     response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
2084                             "disconnected");
2085                 } catch (RemoteException e) {
2086                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2087                         Log.v(TAG, "Session.onServiceDisconnected: "
2088                                 + "caught RemoteException while responding", e);
2089                     }
2090                 }
2091             }
2092         }
2093 
run()2094         public abstract void run() throws RemoteException;
2095 
onTimedOut()2096         public void onTimedOut() {
2097             IAccountManagerResponse response = getResponseAndClose();
2098             if (response != null) {
2099                 try {
2100                     response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
2101                             "timeout");
2102                 } catch (RemoteException e) {
2103                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2104                         Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
2105                                 e);
2106                     }
2107                 }
2108             }
2109         }
2110 
onResult(Bundle result)2111         public void onResult(Bundle result) {
2112             mNumResults++;
2113             if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
2114                 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2115                 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
2116                 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2117                     Account account = new Account(accountName, accountType);
2118                     cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
2119                             new UserHandle(mAccounts.userId));
2120                 }
2121             }
2122             IAccountManagerResponse response;
2123             if (mExpectActivityLaunch && result != null
2124                     && result.containsKey(AccountManager.KEY_INTENT)) {
2125                 response = mResponse;
2126             } else {
2127                 response = getResponseAndClose();
2128             }
2129             if (response != null) {
2130                 try {
2131                     if (result == null) {
2132                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2133                             Log.v(TAG, getClass().getSimpleName()
2134                                     + " calling onError() on response " + response);
2135                         }
2136                         response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
2137                                 "null bundle returned");
2138                     } else {
2139                         if (mStripAuthTokenFromResult) {
2140                             result.remove(AccountManager.KEY_AUTHTOKEN);
2141                         }
2142                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2143                             Log.v(TAG, getClass().getSimpleName()
2144                                     + " calling onResult() on response " + response);
2145                         }
2146                         response.onResult(result);
2147                     }
2148                 } catch (RemoteException e) {
2149                     // if the caller is dead then there is no one to care about remote exceptions
2150                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2151                         Log.v(TAG, "failure while notifying response", e);
2152                     }
2153                 }
2154             }
2155         }
2156 
onRequestContinued()2157         public void onRequestContinued() {
2158             mNumRequestContinued++;
2159         }
2160 
onError(int errorCode, String errorMessage)2161         public void onError(int errorCode, String errorMessage) {
2162             mNumErrors++;
2163             IAccountManagerResponse response = getResponseAndClose();
2164             if (response != null) {
2165                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2166                     Log.v(TAG, getClass().getSimpleName()
2167                             + " calling onError() on response " + response);
2168                 }
2169                 try {
2170                     response.onError(errorCode, errorMessage);
2171                 } catch (RemoteException e) {
2172                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2173                         Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
2174                     }
2175                 }
2176             } else {
2177                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2178                     Log.v(TAG, "Session.onError: already closed");
2179                 }
2180             }
2181         }
2182 
2183         /**
2184          * find the component name for the authenticator and initiate a bind
2185          * if no authenticator or the bind fails then return false, otherwise return true
2186          */
bindToAuthenticator(String authenticatorType)2187         private boolean bindToAuthenticator(String authenticatorType) {
2188             final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
2189             authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2190                     AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
2191             if (authenticatorInfo == null) {
2192                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2193                     Log.v(TAG, "there is no authenticator for " + authenticatorType
2194                             + ", bailing out");
2195                 }
2196                 return false;
2197             }
2198 
2199             Intent intent = new Intent();
2200             intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
2201             intent.setComponent(authenticatorInfo.componentName);
2202             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2203                 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
2204             }
2205             if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
2206                     new UserHandle(mAccounts.userId))) {
2207                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2208                     Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
2209                 }
2210                 return false;
2211             }
2212 
2213 
2214             return true;
2215         }
2216     }
2217 
2218     private class MessageHandler extends Handler {
MessageHandler(Looper looper)2219         MessageHandler(Looper looper) {
2220             super(looper);
2221         }
2222 
handleMessage(Message msg)2223         public void handleMessage(Message msg) {
2224             switch (msg.what) {
2225                 case MESSAGE_TIMED_OUT:
2226                     Session session = (Session)msg.obj;
2227                     session.onTimedOut();
2228                     break;
2229 
2230                 case MESSAGE_COPY_SHARED_ACCOUNT:
2231                     copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2);
2232                     break;
2233 
2234                 default:
2235                     throw new IllegalStateException("unhandled message: " + msg.what);
2236             }
2237         }
2238     }
2239 
getDatabaseName(int userId)2240     private static String getDatabaseName(int userId) {
2241         File systemDir = Environment.getSystemSecureDirectory();
2242         File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
2243         if (userId == 0) {
2244             // Migrate old file, if it exists, to the new location.
2245             // Make sure the new file doesn't already exist. A dummy file could have been
2246             // accidentally created in the old location, causing the new one to become corrupted
2247             // as well.
2248             File oldFile = new File(systemDir, DATABASE_NAME);
2249             if (oldFile.exists() && !databaseFile.exists()) {
2250                 // Check for use directory; create if it doesn't exist, else renameTo will fail
2251                 File userDir = Environment.getUserSystemDirectory(userId);
2252                 if (!userDir.exists()) {
2253                     if (!userDir.mkdirs()) {
2254                         throw new IllegalStateException("User dir cannot be created: " + userDir);
2255                     }
2256                 }
2257                 if (!oldFile.renameTo(databaseFile)) {
2258                     throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
2259                 }
2260             }
2261         }
2262         return databaseFile.getPath();
2263     }
2264 
2265     static class DatabaseHelper extends SQLiteOpenHelper {
2266 
DatabaseHelper(Context context, int userId)2267         public DatabaseHelper(Context context, int userId) {
2268             super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
2269         }
2270 
2271         /**
2272          * This call needs to be made while the mCacheLock is held. The way to
2273          * ensure this is to get the lock any time a method is called ont the DatabaseHelper
2274          * @param db The database.
2275          */
2276         @Override
onCreate(SQLiteDatabase db)2277         public void onCreate(SQLiteDatabase db) {
2278             db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
2279                     + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
2280                     + ACCOUNTS_NAME + " TEXT NOT NULL, "
2281                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
2282                     + ACCOUNTS_PASSWORD + " TEXT, "
2283                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
2284 
2285             db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
2286                     + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
2287                     + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
2288                     + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
2289                     + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
2290                     + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
2291 
2292             createGrantsTable(db);
2293 
2294             db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
2295                     + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
2296                     + EXTRAS_ACCOUNTS_ID + " INTEGER, "
2297                     + EXTRAS_KEY + " TEXT NOT NULL, "
2298                     + EXTRAS_VALUE + " TEXT, "
2299                     + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
2300 
2301             db.execSQL("CREATE TABLE " + TABLE_META + " ( "
2302                     + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
2303                     + META_VALUE + " TEXT)");
2304 
2305             createSharedAccountsTable(db);
2306 
2307             createAccountsDeletionTrigger(db);
2308         }
2309 
createSharedAccountsTable(SQLiteDatabase db)2310         private void createSharedAccountsTable(SQLiteDatabase db) {
2311             db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
2312                     + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
2313                     + ACCOUNTS_NAME + " TEXT NOT NULL, "
2314                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
2315                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
2316         }
2317 
createAccountsDeletionTrigger(SQLiteDatabase db)2318         private void createAccountsDeletionTrigger(SQLiteDatabase db) {
2319             db.execSQL(""
2320                     + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
2321                     + " BEGIN"
2322                     + "   DELETE FROM " + TABLE_AUTHTOKENS
2323                     + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
2324                     + "   DELETE FROM " + TABLE_EXTRAS
2325                     + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
2326                     + "   DELETE FROM " + TABLE_GRANTS
2327                     + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
2328                     + " END");
2329         }
2330 
createGrantsTable(SQLiteDatabase db)2331         private void createGrantsTable(SQLiteDatabase db) {
2332             db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
2333                     + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
2334                     + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
2335                     + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
2336                     + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
2337                     +   "," + GRANTS_GRANTEE_UID + "))");
2338         }
2339 
2340         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)2341         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
2342             Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
2343 
2344             if (oldVersion == 1) {
2345                 // no longer need to do anything since the work is done
2346                 // when upgrading from version 2
2347                 oldVersion++;
2348             }
2349 
2350             if (oldVersion == 2) {
2351                 createGrantsTable(db);
2352                 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
2353                 createAccountsDeletionTrigger(db);
2354                 oldVersion++;
2355             }
2356 
2357             if (oldVersion == 3) {
2358                 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
2359                         " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
2360                 oldVersion++;
2361             }
2362 
2363             if (oldVersion == 4) {
2364                 createSharedAccountsTable(db);
2365                 oldVersion++;
2366             }
2367 
2368             if (oldVersion != newVersion) {
2369                 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
2370             }
2371         }
2372 
2373         @Override
onOpen(SQLiteDatabase db)2374         public void onOpen(SQLiteDatabase db) {
2375             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
2376         }
2377     }
2378 
onBind(Intent intent)2379     public IBinder onBind(Intent intent) {
2380         return asBinder();
2381     }
2382 
2383     /**
2384      * Searches array of arguments for the specified string
2385      * @param args array of argument strings
2386      * @param value value to search for
2387      * @return true if the value is contained in the array
2388      */
scanArgs(String[] args, String value)2389     private static boolean scanArgs(String[] args, String value) {
2390         if (args != null) {
2391             for (String arg : args) {
2392                 if (value.equals(arg)) {
2393                     return true;
2394                 }
2395             }
2396         }
2397         return false;
2398     }
2399 
2400     @Override
dump(FileDescriptor fd, PrintWriter fout, String[] args)2401     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
2402         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2403                 != PackageManager.PERMISSION_GRANTED) {
2404             fout.println("Permission Denial: can't dump AccountsManager from from pid="
2405                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
2406                     + " without permission " + android.Manifest.permission.DUMP);
2407             return;
2408         }
2409         final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
2410         final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
2411 
2412         final List<UserInfo> users = getUserManager().getUsers();
2413         for (UserInfo user : users) {
2414             ipw.println("User " + user + ":");
2415             ipw.increaseIndent();
2416             dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
2417             ipw.println();
2418             ipw.decreaseIndent();
2419         }
2420     }
2421 
dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, String[] args, boolean isCheckinRequest)2422     private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
2423             String[] args, boolean isCheckinRequest) {
2424         synchronized (userAccounts.cacheLock) {
2425             final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
2426 
2427             if (isCheckinRequest) {
2428                 // This is a checkin request. *Only* upload the account types and the count of each.
2429                 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
2430                         null, null, ACCOUNTS_TYPE, null, null);
2431                 try {
2432                     while (cursor.moveToNext()) {
2433                         // print type,count
2434                         fout.println(cursor.getString(0) + "," + cursor.getString(1));
2435                     }
2436                 } finally {
2437                     if (cursor != null) {
2438                         cursor.close();
2439                     }
2440                 }
2441             } else {
2442                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
2443                         Process.myUid(), null);
2444                 fout.println("Accounts: " + accounts.length);
2445                 for (Account account : accounts) {
2446                     fout.println("  " + account);
2447                 }
2448 
2449                 fout.println();
2450                 synchronized (mSessions) {
2451                     final long now = SystemClock.elapsedRealtime();
2452                     fout.println("Active Sessions: " + mSessions.size());
2453                     for (Session session : mSessions.values()) {
2454                         fout.println("  " + session.toDebugString(now));
2455                     }
2456                 }
2457 
2458                 fout.println();
2459                 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
2460             }
2461         }
2462     }
2463 
doNotification(UserAccounts accounts, Account account, CharSequence message, Intent intent, int userId)2464     private void doNotification(UserAccounts accounts, Account account, CharSequence message,
2465             Intent intent, int userId) {
2466         long identityToken = clearCallingIdentity();
2467         try {
2468             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2469                 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
2470             }
2471 
2472             if (intent.getComponent() != null &&
2473                     GrantCredentialsPermissionActivity.class.getName().equals(
2474                             intent.getComponent().getClassName())) {
2475                 createNoCredentialsPermissionNotification(account, intent, userId);
2476             } else {
2477                 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
2478                 intent.addCategory(String.valueOf(notificationId));
2479                 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
2480                         0 /* when */);
2481                 UserHandle user = new UserHandle(userId);
2482                 final String notificationTitleFormat =
2483                         mContext.getText(R.string.notification_title).toString();
2484                 n.setLatestEventInfo(mContext,
2485                         String.format(notificationTitleFormat, account.name),
2486                         message, PendingIntent.getActivityAsUser(
2487                         mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
2488                         null, user));
2489                 installNotification(notificationId, n, user);
2490             }
2491         } finally {
2492             restoreCallingIdentity(identityToken);
2493         }
2494     }
2495 
installNotification(final int notificationId, final Notification n, UserHandle user)2496     protected void installNotification(final int notificationId, final Notification n,
2497             UserHandle user) {
2498         ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
2499                 .notifyAsUser(null, notificationId, n, user);
2500     }
2501 
cancelNotification(int id, UserHandle user)2502     protected void cancelNotification(int id, UserHandle user) {
2503         long identityToken = clearCallingIdentity();
2504         try {
2505             ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
2506                 .cancelAsUser(null, id, user);
2507         } finally {
2508             restoreCallingIdentity(identityToken);
2509         }
2510     }
2511 
2512     /** Succeeds if any of the specified permissions are granted. */
checkBinderPermission(String... permissions)2513     private void checkBinderPermission(String... permissions) {
2514         final int uid = Binder.getCallingUid();
2515 
2516         for (String perm : permissions) {
2517             if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
2518                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2519                     Log.v(TAG, "  caller uid " + uid + " has " + perm);
2520                 }
2521                 return;
2522             }
2523         }
2524 
2525         String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
2526         Log.w(TAG, "  " + msg);
2527         throw new SecurityException(msg);
2528     }
2529 
handleIncomingUser(int userId)2530     private int handleIncomingUser(int userId) {
2531         try {
2532             return ActivityManagerNative.getDefault().handleIncomingUser(
2533                     Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
2534         } catch (RemoteException re) {
2535             // Shouldn't happen, local.
2536         }
2537         return userId;
2538     }
2539 
inSystemImage(int callingUid)2540     private boolean inSystemImage(int callingUid) {
2541         final int callingUserId = UserHandle.getUserId(callingUid);
2542 
2543         final PackageManager userPackageManager;
2544         try {
2545             userPackageManager = mContext.createPackageContextAsUser(
2546                     "android", 0, new UserHandle(callingUserId)).getPackageManager();
2547         } catch (NameNotFoundException e) {
2548             return false;
2549         }
2550 
2551         String[] packages = userPackageManager.getPackagesForUid(callingUid);
2552         for (String name : packages) {
2553             try {
2554                 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
2555                 if (packageInfo != null
2556                         && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
2557                     return true;
2558                 }
2559             } catch (PackageManager.NameNotFoundException e) {
2560                 return false;
2561             }
2562         }
2563         return false;
2564     }
2565 
permissionIsGranted(Account account, String authTokenType, int callerUid)2566     private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
2567         final boolean inSystemImage = inSystemImage(callerUid);
2568         final boolean fromAuthenticator = account != null
2569                 && hasAuthenticatorUid(account.type, callerUid);
2570         final boolean hasExplicitGrants = account != null
2571                 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
2572         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2573             Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
2574                     + callerUid + ", " + account
2575                     + ": is authenticator? " + fromAuthenticator
2576                     + ", has explicit permission? " + hasExplicitGrants);
2577         }
2578         return fromAuthenticator || hasExplicitGrants || inSystemImage;
2579     }
2580 
hasAuthenticatorUid(String accountType, int callingUid)2581     private boolean hasAuthenticatorUid(String accountType, int callingUid) {
2582         final int callingUserId = UserHandle.getUserId(callingUid);
2583         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
2584                 mAuthenticatorCache.getAllServices(callingUserId)) {
2585             if (serviceInfo.type.type.equals(accountType)) {
2586                 return (serviceInfo.uid == callingUid) ||
2587                         (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
2588                                 == PackageManager.SIGNATURE_MATCH);
2589             }
2590         }
2591         return false;
2592     }
2593 
hasExplicitlyGrantedPermission(Account account, String authTokenType, int callerUid)2594     private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
2595             int callerUid) {
2596         if (callerUid == Process.SYSTEM_UID) {
2597             return true;
2598         }
2599         UserAccounts accounts = getUserAccountsForCaller();
2600         synchronized (accounts.cacheLock) {
2601             final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
2602             String[] args = { String.valueOf(callerUid), authTokenType,
2603                     account.name, account.type};
2604             final boolean permissionGranted =
2605                     DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
2606             if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
2607                 // TODO: Skip this check when running automated tests. Replace this
2608                 // with a more general solution.
2609                 Log.d(TAG, "no credentials permission for usage of " + account + ", "
2610                         + authTokenType + " by uid " + callerUid
2611                         + " but ignoring since device is in test harness.");
2612                 return true;
2613             }
2614             return permissionGranted;
2615         }
2616     }
2617 
checkCallingUidAgainstAuthenticator(Account account)2618     private void checkCallingUidAgainstAuthenticator(Account account) {
2619         final int uid = Binder.getCallingUid();
2620         if (account == null || !hasAuthenticatorUid(account.type, uid)) {
2621             String msg = "caller uid " + uid + " is different than the authenticator's uid";
2622             Log.w(TAG, msg);
2623             throw new SecurityException(msg);
2624         }
2625         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2626             Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
2627         }
2628     }
2629 
checkAuthenticateAccountsPermission(Account account)2630     private void checkAuthenticateAccountsPermission(Account account) {
2631         checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
2632         checkCallingUidAgainstAuthenticator(account);
2633     }
2634 
checkReadAccountsPermission()2635     private void checkReadAccountsPermission() {
2636         checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
2637     }
2638 
checkManageAccountsPermission()2639     private void checkManageAccountsPermission() {
2640         checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
2641     }
2642 
checkManageAccountsOrUseCredentialsPermissions()2643     private void checkManageAccountsOrUseCredentialsPermissions() {
2644         checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
2645                 Manifest.permission.USE_CREDENTIALS);
2646     }
2647 
canUserModifyAccounts(int callingUid)2648     private boolean canUserModifyAccounts(int callingUid) {
2649         if (callingUid != Process.myUid()) {
2650             if (getUserManager().getUserRestrictions(
2651                     new UserHandle(UserHandle.getUserId(callingUid)))
2652                     .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
2653                 return false;
2654             }
2655         }
2656         return true;
2657     }
2658 
updateAppPermission(Account account, String authTokenType, int uid, boolean value)2659     public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
2660             throws RemoteException {
2661         final int callingUid = getCallingUid();
2662 
2663         if (callingUid != Process.SYSTEM_UID) {
2664             throw new SecurityException();
2665         }
2666 
2667         if (value) {
2668             grantAppPermission(account, authTokenType, uid);
2669         } else {
2670             revokeAppPermission(account, authTokenType, uid);
2671         }
2672     }
2673 
2674     /**
2675      * Allow callers with the given uid permission to get credentials for account/authTokenType.
2676      * <p>
2677      * Although this is public it can only be accessed via the AccountManagerService object
2678      * which is in the system. This means we don't need to protect it with permissions.
2679      * @hide
2680      */
grantAppPermission(Account account, String authTokenType, int uid)2681     private void grantAppPermission(Account account, String authTokenType, int uid) {
2682         if (account == null || authTokenType == null) {
2683             Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
2684             return;
2685         }
2686         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
2687         synchronized (accounts.cacheLock) {
2688             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2689             db.beginTransaction();
2690             try {
2691                 long accountId = getAccountIdLocked(db, account);
2692                 if (accountId >= 0) {
2693                     ContentValues values = new ContentValues();
2694                     values.put(GRANTS_ACCOUNTS_ID, accountId);
2695                     values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
2696                     values.put(GRANTS_GRANTEE_UID, uid);
2697                     db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
2698                     db.setTransactionSuccessful();
2699                 }
2700             } finally {
2701                 db.endTransaction();
2702             }
2703             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
2704                     new UserHandle(accounts.userId));
2705         }
2706     }
2707 
2708     /**
2709      * Don't allow callers with the given uid permission to get credentials for
2710      * account/authTokenType.
2711      * <p>
2712      * Although this is public it can only be accessed via the AccountManagerService object
2713      * which is in the system. This means we don't need to protect it with permissions.
2714      * @hide
2715      */
revokeAppPermission(Account account, String authTokenType, int uid)2716     private void revokeAppPermission(Account account, String authTokenType, int uid) {
2717         if (account == null || authTokenType == null) {
2718             Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
2719             return;
2720         }
2721         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
2722         synchronized (accounts.cacheLock) {
2723             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2724             db.beginTransaction();
2725             try {
2726                 long accountId = getAccountIdLocked(db, account);
2727                 if (accountId >= 0) {
2728                     db.delete(TABLE_GRANTS,
2729                             GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
2730                                     + GRANTS_GRANTEE_UID + "=?",
2731                             new String[]{String.valueOf(accountId), authTokenType,
2732                                     String.valueOf(uid)});
2733                     db.setTransactionSuccessful();
2734                 }
2735             } finally {
2736                 db.endTransaction();
2737             }
2738             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
2739                     new UserHandle(accounts.userId));
2740         }
2741     }
2742 
stringArrayToString(String[] value)2743     static final private String stringArrayToString(String[] value) {
2744         return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
2745     }
2746 
removeAccountFromCacheLocked(UserAccounts accounts, Account account)2747     private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
2748         final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
2749         if (oldAccountsForType != null) {
2750             ArrayList<Account> newAccountsList = new ArrayList<Account>();
2751             for (Account curAccount : oldAccountsForType) {
2752                 if (!curAccount.equals(account)) {
2753                     newAccountsList.add(curAccount);
2754                 }
2755             }
2756             if (newAccountsList.isEmpty()) {
2757                 accounts.accountCache.remove(account.type);
2758             } else {
2759                 Account[] newAccountsForType = new Account[newAccountsList.size()];
2760                 newAccountsForType = newAccountsList.toArray(newAccountsForType);
2761                 accounts.accountCache.put(account.type, newAccountsForType);
2762             }
2763         }
2764         accounts.userDataCache.remove(account);
2765         accounts.authTokenCache.remove(account);
2766     }
2767 
2768     /**
2769      * This assumes that the caller has already checked that the account is not already present.
2770      */
insertAccountIntoCacheLocked(UserAccounts accounts, Account account)2771     private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
2772         Account[] accountsForType = accounts.accountCache.get(account.type);
2773         int oldLength = (accountsForType != null) ? accountsForType.length : 0;
2774         Account[] newAccountsForType = new Account[oldLength + 1];
2775         if (accountsForType != null) {
2776             System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
2777         }
2778         newAccountsForType[oldLength] = account;
2779         accounts.accountCache.put(account.type, newAccountsForType);
2780     }
2781 
filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, int callingUid, String callingPackage)2782     private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
2783             int callingUid, String callingPackage) {
2784         if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
2785                 || callingUid == Process.myUid()) {
2786             return unfiltered;
2787         }
2788         if (mUserManager.getUserInfo(userAccounts.userId).isRestricted()) {
2789             String[] packages = mPackageManager.getPackagesForUid(callingUid);
2790             // If any of the packages is a white listed package, return the full set,
2791             // otherwise return non-shared accounts only.
2792             // This might be a temporary way to specify a whitelist
2793             String whiteList = mContext.getResources().getString(
2794                     com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
2795             for (String packageName : packages) {
2796                 if (whiteList.contains(";" + packageName + ";")) {
2797                     return unfiltered;
2798                 }
2799             }
2800             ArrayList<Account> allowed = new ArrayList<Account>();
2801             Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
2802             if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
2803             String requiredAccountType = "";
2804             try {
2805                 // If there's an explicit callingPackage specified, check if that package
2806                 // opted in to see restricted accounts.
2807                 if (callingPackage != null) {
2808                     PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
2809                     if (pi != null && pi.restrictedAccountType != null) {
2810                         requiredAccountType = pi.restrictedAccountType;
2811                     }
2812                 } else {
2813                     // Otherwise check if the callingUid has a package that has opted in
2814                     for (String packageName : packages) {
2815                         PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
2816                         if (pi != null && pi.restrictedAccountType != null) {
2817                             requiredAccountType = pi.restrictedAccountType;
2818                             break;
2819                         }
2820                     }
2821                 }
2822             } catch (NameNotFoundException nnfe) {
2823             }
2824             for (Account account : unfiltered) {
2825                 if (account.type.equals(requiredAccountType)) {
2826                     allowed.add(account);
2827                 } else {
2828                     boolean found = false;
2829                     for (Account shared : sharedAccounts) {
2830                         if (shared.equals(account)) {
2831                             found = true;
2832                             break;
2833                         }
2834                     }
2835                     if (!found) {
2836                         allowed.add(account);
2837                     }
2838                 }
2839             }
2840             Account[] filtered = new Account[allowed.size()];
2841             allowed.toArray(filtered);
2842             return filtered;
2843         } else {
2844             return unfiltered;
2845         }
2846     }
2847 
2848     /*
2849      * packageName can be null. If not null, it should be used to filter out restricted accounts
2850      * that the package is not allowed to access.
2851      */
getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, int callingUid, String callingPackage)2852     protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
2853             int callingUid, String callingPackage) {
2854         if (accountType != null) {
2855             final Account[] accounts = userAccounts.accountCache.get(accountType);
2856             if (accounts == null) {
2857                 return EMPTY_ACCOUNT_ARRAY;
2858             } else {
2859                 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
2860                         callingUid, callingPackage);
2861             }
2862         } else {
2863             int totalLength = 0;
2864             for (Account[] accounts : userAccounts.accountCache.values()) {
2865                 totalLength += accounts.length;
2866             }
2867             if (totalLength == 0) {
2868                 return EMPTY_ACCOUNT_ARRAY;
2869             }
2870             Account[] accounts = new Account[totalLength];
2871             totalLength = 0;
2872             for (Account[] accountsOfType : userAccounts.accountCache.values()) {
2873                 System.arraycopy(accountsOfType, 0, accounts, totalLength,
2874                         accountsOfType.length);
2875                 totalLength += accountsOfType.length;
2876             }
2877             return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
2878         }
2879     }
2880 
writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, Account account, String key, String value)2881     protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
2882             Account account, String key, String value) {
2883         HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
2884         if (userDataForAccount == null) {
2885             userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
2886             accounts.userDataCache.put(account, userDataForAccount);
2887         }
2888         if (value == null) {
2889             userDataForAccount.remove(key);
2890         } else {
2891             userDataForAccount.put(key, value);
2892         }
2893     }
2894 
writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, Account account, String key, String value)2895     protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
2896             Account account, String key, String value) {
2897         HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
2898         if (authTokensForAccount == null) {
2899             authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
2900             accounts.authTokenCache.put(account, authTokensForAccount);
2901         }
2902         if (value == null) {
2903             authTokensForAccount.remove(key);
2904         } else {
2905             authTokensForAccount.put(key, value);
2906         }
2907     }
2908 
readAuthTokenInternal(UserAccounts accounts, Account account, String authTokenType)2909     protected String readAuthTokenInternal(UserAccounts accounts, Account account,
2910             String authTokenType) {
2911         synchronized (accounts.cacheLock) {
2912             HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
2913             if (authTokensForAccount == null) {
2914                 // need to populate the cache for this account
2915                 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
2916                 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
2917                 accounts.authTokenCache.put(account, authTokensForAccount);
2918             }
2919             return authTokensForAccount.get(authTokenType);
2920         }
2921     }
2922 
readUserDataInternal(UserAccounts accounts, Account account, String key)2923     protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
2924         synchronized (accounts.cacheLock) {
2925             HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
2926             if (userDataForAccount == null) {
2927                 // need to populate the cache for this account
2928                 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
2929                 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
2930                 accounts.userDataCache.put(account, userDataForAccount);
2931             }
2932             return userDataForAccount.get(key);
2933         }
2934     }
2935 
readUserDataForAccountFromDatabaseLocked( final SQLiteDatabase db, Account account)2936     protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
2937             final SQLiteDatabase db, Account account) {
2938         HashMap<String, String> userDataForAccount = new HashMap<String, String>();
2939         Cursor cursor = db.query(TABLE_EXTRAS,
2940                 COLUMNS_EXTRAS_KEY_AND_VALUE,
2941                 SELECTION_USERDATA_BY_ACCOUNT,
2942                 new String[]{account.name, account.type},
2943                 null, null, null);
2944         try {
2945             while (cursor.moveToNext()) {
2946                 final String tmpkey = cursor.getString(0);
2947                 final String value = cursor.getString(1);
2948                 userDataForAccount.put(tmpkey, value);
2949             }
2950         } finally {
2951             cursor.close();
2952         }
2953         return userDataForAccount;
2954     }
2955 
readAuthTokensForAccountFromDatabaseLocked( final SQLiteDatabase db, Account account)2956     protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
2957             final SQLiteDatabase db, Account account) {
2958         HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
2959         Cursor cursor = db.query(TABLE_AUTHTOKENS,
2960                 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
2961                 SELECTION_AUTHTOKENS_BY_ACCOUNT,
2962                 new String[]{account.name, account.type},
2963                 null, null, null);
2964         try {
2965             while (cursor.moveToNext()) {
2966                 final String type = cursor.getString(0);
2967                 final String authToken = cursor.getString(1);
2968                 authTokensForAccount.put(type, authToken);
2969             }
2970         } finally {
2971             cursor.close();
2972         }
2973         return authTokensForAccount;
2974     }
2975 }
2976