• 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.AbstractAccountAuthenticator;
21 import android.accounts.Account;
22 import android.accounts.AccountAndUser;
23 import android.accounts.AccountAuthenticatorResponse;
24 import android.accounts.AccountManager;
25 import android.accounts.AuthenticatorDescription;
26 import android.accounts.CantAddAccountActivity;
27 import android.accounts.GrantCredentialsPermissionActivity;
28 import android.accounts.IAccountAuthenticator;
29 import android.accounts.IAccountAuthenticatorResponse;
30 import android.accounts.IAccountManager;
31 import android.accounts.IAccountManagerResponse;
32 import android.annotation.NonNull;
33 import android.app.ActivityManager;
34 import android.app.ActivityManagerNative;
35 import android.app.AppGlobals;
36 import android.app.AppOpsManager;
37 import android.app.Notification;
38 import android.app.NotificationManager;
39 import android.app.PendingIntent;
40 import android.app.admin.DeviceAdminInfo;
41 import android.app.admin.DevicePolicyManager;
42 import android.app.admin.DevicePolicyManagerInternal;
43 import android.content.BroadcastReceiver;
44 import android.content.ComponentName;
45 import android.content.ContentValues;
46 import android.content.Context;
47 import android.content.Intent;
48 import android.content.IntentFilter;
49 import android.content.ServiceConnection;
50 import android.content.pm.ActivityInfo;
51 import android.content.pm.ApplicationInfo;
52 import android.content.pm.PackageInfo;
53 import android.content.pm.PackageManager;
54 import android.content.pm.PackageManager.NameNotFoundException;
55 import android.content.pm.RegisteredServicesCache;
56 import android.content.pm.RegisteredServicesCacheListener;
57 import android.content.pm.ResolveInfo;
58 import android.content.pm.Signature;
59 import android.content.pm.UserInfo;
60 import android.database.Cursor;
61 import android.database.DatabaseUtils;
62 import android.database.sqlite.SQLiteDatabase;
63 import android.database.sqlite.SQLiteOpenHelper;
64 import android.database.sqlite.SQLiteStatement;
65 import android.os.Binder;
66 import android.os.Bundle;
67 import android.os.Environment;
68 import android.os.FileUtils;
69 import android.os.Handler;
70 import android.os.IBinder;
71 import android.os.Looper;
72 import android.os.Message;
73 import android.os.Parcel;
74 import android.os.Process;
75 import android.os.RemoteException;
76 import android.os.SystemClock;
77 import android.os.UserHandle;
78 import android.os.UserManager;
79 import android.os.storage.StorageManager;
80 import android.text.TextUtils;
81 import android.util.Log;
82 import android.util.Pair;
83 import android.util.Slog;
84 import android.util.SparseArray;
85 import android.util.SparseBooleanArray;
86 
87 import com.android.internal.R;
88 import com.android.internal.annotations.VisibleForTesting;
89 import com.android.internal.util.ArrayUtils;
90 import com.android.internal.util.IndentingPrintWriter;
91 import com.android.internal.util.Preconditions;
92 import com.android.server.FgThread;
93 import com.android.server.LocalServices;
94 import com.android.server.SystemService;
95 
96 import com.google.android.collect.Lists;
97 import com.google.android.collect.Sets;
98 
99 import java.io.File;
100 import java.io.FileDescriptor;
101 import java.io.IOException;
102 import java.io.PrintWriter;
103 import java.security.GeneralSecurityException;
104 import java.security.MessageDigest;
105 import java.security.NoSuchAlgorithmException;
106 import java.text.SimpleDateFormat;
107 import java.util.ArrayList;
108 import java.util.Arrays;
109 import java.util.Collection;
110 import java.util.Date;
111 import java.util.HashMap;
112 import java.util.HashSet;
113 import java.util.Iterator;
114 import java.util.LinkedHashMap;
115 import java.util.List;
116 import java.util.Map;
117 import java.util.Map.Entry;
118 import java.util.concurrent.atomic.AtomicInteger;
119 import java.util.concurrent.atomic.AtomicReference;
120 
121 /**
122  * A system service that provides  account, password, and authtoken management for all
123  * accounts on the device. Some of these calls are implemented with the help of the corresponding
124  * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
125  * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
126  *    AccountManager accountManager = AccountManager.get(context);
127  * @hide
128  */
129 public class AccountManagerService
130         extends IAccountManager.Stub
131         implements RegisteredServicesCacheListener<AuthenticatorDescription> {
132     private static final String TAG = "AccountManagerService";
133 
134     public static class Lifecycle extends SystemService {
135         private AccountManagerService mService;
136 
Lifecycle(Context context)137         public Lifecycle(Context context) {
138             super(context);
139         }
140 
141         @Override
onStart()142         public void onStart() {
143             mService = new AccountManagerService(getContext());
144             publishBinderService(Context.ACCOUNT_SERVICE, mService);
145         }
146 
147         @Override
onBootPhase(int phase)148         public void onBootPhase(int phase) {
149             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
150                 mService.systemReady();
151             }
152         }
153 
154         @Override
onUnlockUser(int userHandle)155         public void onUnlockUser(int userHandle) {
156             mService.onUnlockUser(userHandle);
157         }
158     }
159 
160     private static final String DATABASE_NAME = "accounts.db";
161     private static final int PRE_N_DATABASE_VERSION = 9;
162     private static final int CE_DATABASE_VERSION = 10;
163     private static final int DE_DATABASE_VERSION = 1;
164 
165     private static final int MAX_DEBUG_DB_SIZE = 64;
166 
167     private final Context mContext;
168 
169     private final PackageManager mPackageManager;
170     private final AppOpsManager mAppOpsManager;
171     private UserManager mUserManager;
172 
173     private final MessageHandler mMessageHandler;
174 
175     // Messages that can be sent on mHandler
176     private static final int MESSAGE_TIMED_OUT = 3;
177     private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
178 
179     private final IAccountAuthenticatorCache mAuthenticatorCache;
180 
181     private static final String TABLE_ACCOUNTS = "accounts";
182     private static final String ACCOUNTS_ID = "_id";
183     private static final String ACCOUNTS_NAME = "name";
184     private static final String ACCOUNTS_TYPE = "type";
185     private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
186     private static final String ACCOUNTS_PASSWORD = "password";
187     private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
188     private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
189             "last_password_entry_time_millis_epoch";
190 
191     private static final String TABLE_AUTHTOKENS = "authtokens";
192     private static final String AUTHTOKENS_ID = "_id";
193     private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
194     private static final String AUTHTOKENS_TYPE = "type";
195     private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
196 
197     private static final String TABLE_GRANTS = "grants";
198     private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
199     private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
200     private static final String GRANTS_GRANTEE_UID = "uid";
201 
202     private static final String TABLE_EXTRAS = "extras";
203     private static final String EXTRAS_ID = "_id";
204     private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
205     private static final String EXTRAS_KEY = "key";
206     private static final String EXTRAS_VALUE = "value";
207 
208     private static final String TABLE_META = "meta";
209     private static final String META_KEY = "key";
210     private static final String META_VALUE = "value";
211 
212     private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
213     private static final String SHARED_ACCOUNTS_ID = "_id";
214 
215     private static final String PRE_N_DATABASE_NAME = "accounts.db";
216     private static final String CE_DATABASE_NAME = "accounts_ce.db";
217     private static final String DE_DATABASE_NAME = "accounts_de.db";
218     private static final String CE_DB_PREFIX = "ceDb.";
219     private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
220     private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
221     private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
222 
223     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
224             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
225     private static final Intent ACCOUNTS_CHANGED_INTENT;
226 
227     static {
228         ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
229         ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
230     }
231 
232     private static final String COUNT_OF_MATCHING_GRANTS = ""
233             + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
234             + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
235             + " AND " + GRANTS_GRANTEE_UID + "=?"
236             + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
237             + " AND " + ACCOUNTS_NAME + "=?"
238             + " AND " + ACCOUNTS_TYPE + "=?";
239 
240     private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
241             AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
242 
243     private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
244             AUTHTOKENS_AUTHTOKEN};
245 
246     private static final String SELECTION_USERDATA_BY_ACCOUNT =
247             EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
248     private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
249 
250     private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
251             "auth_uid_for_type:";
252     private static final String META_KEY_DELIMITER = ":";
253     private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
254 
255     private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
256     private final AtomicInteger mNotificationIds = new AtomicInteger(1);
257 
258     static class UserAccounts {
259         private final int userId;
260         private final DeDatabaseHelper openHelper;
261         private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
262                 credentialsPermissionNotificationIds =
263                 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
264         private final HashMap<Account, Integer> signinRequiredNotificationIds =
265                 new HashMap<Account, Integer>();
266         private final Object cacheLock = new Object();
267         /** protected by the {@link #cacheLock} */
268         private final HashMap<String, Account[]> accountCache =
269                 new LinkedHashMap<String, Account[]>();
270         /** protected by the {@link #cacheLock} */
271         private final HashMap<Account, HashMap<String, String>> userDataCache =
272                 new HashMap<Account, HashMap<String, String>>();
273         /** protected by the {@link #cacheLock} */
274         private final HashMap<Account, HashMap<String, String>> authTokenCache =
275                 new HashMap<Account, HashMap<String, String>>();
276 
277         /** protected by the {@link #cacheLock} */
278         private final TokenCache accountTokenCaches = new TokenCache();
279 
280         /**
281          * protected by the {@link #cacheLock}
282          *
283          * Caches the previous names associated with an account. Previous names
284          * should be cached because we expect that when an Account is renamed,
285          * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
286          * want to know if the accounts they care about have been renamed.
287          *
288          * The previous names are wrapped in an {@link AtomicReference} so that
289          * we can distinguish between those accounts with no previous names and
290          * those whose previous names haven't been cached (yet).
291          */
292         private final HashMap<Account, AtomicReference<String>> previousNameCache =
293                 new HashMap<Account, AtomicReference<String>>();
294 
295         private int debugDbInsertionPoint = -1;
296         private SQLiteStatement statementForLogging;
297 
UserAccounts(Context context, int userId, File preNDbFile, File deDbFile)298         UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
299             this.userId = userId;
300             synchronized (cacheLock) {
301                 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
302             }
303         }
304     }
305 
306     private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
307     private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
308 
309     private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
310     private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
311 
312     /**
313      * This should only be called by system code. One should only call this after the service
314      * has started.
315      * @return a reference to the AccountManagerService instance
316      * @hide
317      */
getSingleton()318     public static AccountManagerService getSingleton() {
319         return sThis.get();
320     }
321 
AccountManagerService(Context context)322     public AccountManagerService(Context context) {
323         this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
324     }
325 
AccountManagerService(Context context, PackageManager packageManager, IAccountAuthenticatorCache authenticatorCache)326     public AccountManagerService(Context context, PackageManager packageManager,
327             IAccountAuthenticatorCache authenticatorCache) {
328         mContext = context;
329         mPackageManager = packageManager;
330         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
331 
332         mMessageHandler = new MessageHandler(FgThread.get().getLooper());
333 
334         mAuthenticatorCache = authenticatorCache;
335         mAuthenticatorCache.setListener(this, null /* Handler */);
336 
337         sThis.set(this);
338 
339         IntentFilter intentFilter = new IntentFilter();
340         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
341         intentFilter.addDataScheme("package");
342         mContext.registerReceiver(new BroadcastReceiver() {
343             @Override
344             public void onReceive(Context context1, Intent intent) {
345                 // Don't delete accounts when updating a authenticator's
346                 // package.
347                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
348                     /* Purging data requires file io, don't block the main thread. This is probably
349                      * less than ideal because we are introducing a race condition where old grants
350                      * could be exercised until they are purged. But that race condition existed
351                      * anyway with the broadcast receiver.
352                      *
353                      * Ideally, we would completely clear the cache, purge data from the database,
354                      * and then rebuild the cache. All under the cache lock. But that change is too
355                      * large at this point.
356                      */
357                     Runnable r = new Runnable() {
358                         @Override
359                         public void run() {
360                             purgeOldGrantsAll();
361                         }
362                     };
363                     new Thread(r).start();
364                 }
365             }
366         }, intentFilter);
367 
368         IntentFilter userFilter = new IntentFilter();
369         userFilter.addAction(Intent.ACTION_USER_REMOVED);
370         mContext.registerReceiverAsUser(new BroadcastReceiver() {
371             @Override
372             public void onReceive(Context context, Intent intent) {
373                 String action = intent.getAction();
374                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
375                     onUserRemoved(intent);
376                 }
377             }
378         }, UserHandle.ALL, userFilter, null, null);
379     }
380 
381     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)382     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
383             throws RemoteException {
384         try {
385             return super.onTransact(code, data, reply, flags);
386         } catch (RuntimeException e) {
387             // The account manager only throws security exceptions, so let's
388             // log all others.
389             if (!(e instanceof SecurityException)) {
390                 Slog.wtf(TAG, "Account Manager Crash", e);
391             }
392             throw e;
393         }
394     }
395 
systemReady()396     public void systemReady() {
397     }
398 
getUserManager()399     private UserManager getUserManager() {
400         if (mUserManager == null) {
401             mUserManager = UserManager.get(mContext);
402         }
403         return mUserManager;
404     }
405 
406     /**
407      * Validate internal set of accounts against installed authenticators for
408      * given user. Clears cached authenticators before validating.
409      */
validateAccounts(int userId)410     public void validateAccounts(int userId) {
411         final UserAccounts accounts = getUserAccounts(userId);
412         // Invalidate user-specific cache to make sure we catch any
413         // removed authenticators.
414         validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
415     }
416 
417     /**
418      * Validate internal set of accounts against installed authenticators for
419      * given user. Clear cached authenticators before validating when requested.
420      */
validateAccountsInternal( UserAccounts accounts, boolean invalidateAuthenticatorCache)421     private void validateAccountsInternal(
422             UserAccounts accounts, boolean invalidateAuthenticatorCache) {
423         if (Log.isLoggable(TAG, Log.DEBUG)) {
424             Log.d(TAG, "validateAccountsInternal " + accounts.userId
425                     + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
426                     + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
427         }
428 
429         if (invalidateAuthenticatorCache) {
430             mAuthenticatorCache.invalidateCache(accounts.userId);
431         }
432 
433         final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
434                 mAuthenticatorCache, accounts.userId);
435         boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
436 
437         synchronized (accounts.cacheLock) {
438             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
439             boolean accountDeleted = false;
440 
441             // Get a list of stored authenticator type and UID
442             Cursor metaCursor = db.query(
443                     TABLE_META,
444                     new String[] {META_KEY, META_VALUE},
445                     SELECTION_META_BY_AUTHENTICATOR_TYPE,
446                     new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
447                     null /* groupBy */,
448                     null /* having */,
449                     META_KEY);
450             // Create a list of authenticator type whose previous uid no longer exists
451             HashSet<String> obsoleteAuthType = Sets.newHashSet();
452             try {
453                 SparseBooleanArray knownUids = null;
454                 while (metaCursor.moveToNext()) {
455                     String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
456                     String uid = metaCursor.getString(1);
457                     if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uid)) {
458                         // Should never happen.
459                         Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
460                                 + ", uid empty: " + TextUtils.isEmpty(uid));
461                         continue;
462                     }
463                     Integer knownUid = knownAuth.get(type);
464                     if (knownUid != null && uid.equals(knownUid.toString())) {
465                         // Remove it from the knownAuth list if it's unchanged.
466                         knownAuth.remove(type);
467                     } else {
468                         /*
469                          * The authenticator is presently not cached and should only be triggered
470                          * when we think an authenticator has been removed (or is being updated).
471                          * But we still want to check if any data with the associated uid is
472                          * around. This is an (imperfect) signal that the package may be updating.
473                          *
474                          * A side effect of this is that an authenticator sharing a uid with
475                          * multiple apps won't get its credentials wiped as long as some app with
476                          * that uid is still on the device. But I suspect that this is a rare case.
477                          * And it isn't clear to me how an attacker could really exploit that
478                          * feature.
479                          *
480                          * The upshot is that we don't have to worry about accounts getting
481                          * uninstalled while the authenticator's package is being updated.
482                          *
483                          */
484                         if (knownUids == null) {
485                             knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
486                         }
487                         if (!knownUids.get(Integer.parseInt(uid))) {
488                             // The authenticator is not presently available to the cache. And the
489                             // package no longer has a data directory (so we surmise it isn't updating).
490                             // So purge its data from the account databases.
491                             obsoleteAuthType.add(type);
492                             // And delete it from the TABLE_META
493                             db.delete(
494                                     TABLE_META,
495                                     META_KEY + "=? AND " + META_VALUE + "=?",
496                                     new String[] {
497                                             META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
498                                             uid}
499                                     );
500                         }
501                     }
502                 }
503             } finally {
504                 metaCursor.close();
505             }
506 
507             // Add the newly registered authenticator to TABLE_META. If old authenticators have
508             // been renabled (after being updated for example), then we just overwrite the old
509             // values.
510             Iterator<Entry<String, Integer>> iterator = knownAuth.entrySet().iterator();
511             while (iterator.hasNext()) {
512                 Entry<String, Integer> entry = iterator.next();
513                 ContentValues values = new ContentValues();
514                 values.put(META_KEY,
515                         META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
516                 values.put(META_VALUE, entry.getValue());
517                 db.insertWithOnConflict(TABLE_META, null, values, SQLiteDatabase.CONFLICT_REPLACE);
518             }
519 
520             Cursor cursor = db.query(TABLE_ACCOUNTS,
521                     new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
522                     null, null, null, null, ACCOUNTS_ID);
523             try {
524                 accounts.accountCache.clear();
525                 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
526                 while (cursor.moveToNext()) {
527                     final long accountId = cursor.getLong(0);
528                     final String accountType = cursor.getString(1);
529                     final String accountName = cursor.getString(2);
530 
531                     if (obsoleteAuthType.contains(accountType)) {
532                         Slog.w(TAG, "deleting account " + accountName + " because type "
533                                 + accountType + "'s registered authenticator no longer exist.");
534                         db.beginTransaction();
535                         try {
536                             db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
537                             // Also delete from CE table if user is unlocked; if user is currently
538                             // locked the account will be removed later by syncDeCeAccountsLocked
539                             if (userUnlocked) {
540                                 db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
541                             }
542                             db.setTransactionSuccessful();
543                         } finally {
544                             db.endTransaction();
545                         }
546                         accountDeleted = true;
547 
548                         logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
549                                 accountId, accounts);
550 
551                         final Account account = new Account(accountName, accountType);
552                         accounts.userDataCache.remove(account);
553                         accounts.authTokenCache.remove(account);
554                         accounts.accountTokenCaches.remove(account);
555                     } else {
556                         ArrayList<String> accountNames = accountNamesByType.get(accountType);
557                         if (accountNames == null) {
558                             accountNames = new ArrayList<String>();
559                             accountNamesByType.put(accountType, accountNames);
560                         }
561                         accountNames.add(accountName);
562                     }
563                 }
564                 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
565                     final String accountType = cur.getKey();
566                     final ArrayList<String> accountNames = cur.getValue();
567                     final Account[] accountsForType = new Account[accountNames.size()];
568                     for (int i = 0; i < accountsForType.length; i++) {
569                         accountsForType[i] = new Account(accountNames.get(i), accountType);
570                     }
571                     accounts.accountCache.put(accountType, accountsForType);
572                 }
573             } finally {
574                 cursor.close();
575                 if (accountDeleted) {
576                     sendAccountsChangedBroadcast(accounts.userId);
577                 }
578             }
579         }
580     }
581 
getUidsOfInstalledOrUpdatedPackagesAsUser(int userId)582     private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
583         // Get the UIDs of all apps that might have data on the device. We want
584         // to preserve user data if the app might otherwise be storing data.
585         List<PackageInfo> pkgsWithData =
586                 mPackageManager.getInstalledPackagesAsUser(
587                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
588         SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
589         for (PackageInfo pkgInfo : pkgsWithData) {
590             if (pkgInfo.applicationInfo != null
591                     && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
592                 knownUids.put(pkgInfo.applicationInfo.uid, true);
593             }
594         }
595         return knownUids;
596     }
597 
getAuthenticatorTypeAndUIDForUser( Context context, int userId)598     private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
599             Context context,
600             int userId) {
601         AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
602         return getAuthenticatorTypeAndUIDForUser(authCache, userId);
603     }
604 
getAuthenticatorTypeAndUIDForUser( IAccountAuthenticatorCache authCache, int userId)605     private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
606             IAccountAuthenticatorCache authCache,
607             int userId) {
608         HashMap<String, Integer> knownAuth = new HashMap<>();
609         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
610                 .getAllServices(userId)) {
611             knownAuth.put(service.type.type, service.uid);
612         }
613         return knownAuth;
614     }
615 
getUserAccountsForCaller()616     private UserAccounts getUserAccountsForCaller() {
617         return getUserAccounts(UserHandle.getCallingUserId());
618     }
619 
getUserAccounts(int userId)620     protected UserAccounts getUserAccounts(int userId) {
621         synchronized (mUsers) {
622             UserAccounts accounts = mUsers.get(userId);
623             boolean validateAccounts = false;
624             if (accounts == null) {
625                 File preNDbFile = new File(getPreNDatabaseName(userId));
626                 File deDbFile = new File(getDeDatabaseName(userId));
627                 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
628                 initializeDebugDbSizeAndCompileSqlStatementForLogging(
629                         accounts.openHelper.getWritableDatabase(), accounts);
630                 mUsers.append(userId, accounts);
631                 purgeOldGrants(accounts);
632                 validateAccounts = true;
633             }
634             // open CE database if necessary
635             if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
636                 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
637                 synchronized (accounts.cacheLock) {
638                     File preNDatabaseFile = new File(getPreNDatabaseName(userId));
639                     File ceDatabaseFile = new File(getCeDatabaseName(userId));
640                     CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
641                     accounts.openHelper.attachCeDatabase(ceDatabaseFile);
642                 }
643                 syncDeCeAccountsLocked(accounts);
644             }
645             if (validateAccounts) {
646                 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
647             }
648             return accounts;
649         }
650     }
651 
syncDeCeAccountsLocked(UserAccounts accounts)652     private void syncDeCeAccountsLocked(UserAccounts accounts) {
653         Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
654         final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
655         List<Account> accountsToRemove = CeDatabaseHelper.findCeAccountsNotInDe(db);
656         if (!accountsToRemove.isEmpty()) {
657             Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
658                     + accounts.userId + " was locked. Removing accounts from CE tables");
659             logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
660 
661             for (Account account : accountsToRemove) {
662                 removeAccountInternal(accounts, account, Process.myUid());
663             }
664         }
665     }
666 
purgeOldGrantsAll()667     private void purgeOldGrantsAll() {
668         synchronized (mUsers) {
669             for (int i = 0; i < mUsers.size(); i++) {
670                 purgeOldGrants(mUsers.valueAt(i));
671             }
672         }
673     }
674 
purgeOldGrants(UserAccounts accounts)675     private void purgeOldGrants(UserAccounts accounts) {
676         synchronized (accounts.cacheLock) {
677             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
678             final Cursor cursor = db.query(TABLE_GRANTS,
679                     new String[]{GRANTS_GRANTEE_UID},
680                     null, null, GRANTS_GRANTEE_UID, null, null);
681             try {
682                 while (cursor.moveToNext()) {
683                     final int uid = cursor.getInt(0);
684                     final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
685                     if (packageExists) {
686                         continue;
687                     }
688                     Log.d(TAG, "deleting grants for UID " + uid
689                             + " because its package is no longer installed");
690                     db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
691                             new String[]{Integer.toString(uid)});
692                 }
693             } finally {
694                 cursor.close();
695             }
696         }
697     }
698 
onUserRemoved(Intent intent)699     private void onUserRemoved(Intent intent) {
700         int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
701         if (userId < 1) return;
702 
703         UserAccounts accounts;
704         boolean userUnlocked;
705         synchronized (mUsers) {
706             accounts = mUsers.get(userId);
707             mUsers.remove(userId);
708             userUnlocked = mLocalUnlockedUsers.get(userId);
709             mLocalUnlockedUsers.delete(userId);
710         }
711         if (accounts != null) {
712             synchronized (accounts.cacheLock) {
713                 accounts.openHelper.close();
714             }
715         }
716         Log.i(TAG, "Removing database files for user " + userId);
717         File dbFile = new File(getDeDatabaseName(userId));
718 
719         deleteDbFileWarnIfFailed(dbFile);
720         // Remove CE file if user is unlocked, or FBE is not enabled
721         boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
722         if (!fbeEnabled || userUnlocked) {
723             File ceDb = new File(getCeDatabaseName(userId));
724             if (ceDb.exists()) {
725                 deleteDbFileWarnIfFailed(ceDb);
726             }
727         }
728     }
729 
deleteDbFileWarnIfFailed(File dbFile)730     private static void deleteDbFileWarnIfFailed(File dbFile) {
731         if (!SQLiteDatabase.deleteDatabase(dbFile)) {
732             Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
733         }
734     }
735 
736     @VisibleForTesting
onUserUnlocked(Intent intent)737     void onUserUnlocked(Intent intent) {
738         onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
739     }
740 
onUnlockUser(int userId)741     void onUnlockUser(int userId) {
742         if (Log.isLoggable(TAG, Log.VERBOSE)) {
743             Log.v(TAG, "onUserUnlocked " + userId);
744         }
745         synchronized (mUsers) {
746             mLocalUnlockedUsers.put(userId, true);
747         }
748         if (userId < 1) return;
749         syncSharedAccounts(userId);
750     }
751 
syncSharedAccounts(int userId)752     private void syncSharedAccounts(int userId) {
753         // Check if there's a shared account that needs to be created as an account
754         Account[] sharedAccounts = getSharedAccountsAsUser(userId);
755         if (sharedAccounts == null || sharedAccounts.length == 0) return;
756         Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
757         int parentUserId = UserManager.isSplitSystemUser()
758                 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
759                 : UserHandle.USER_SYSTEM;
760         if (parentUserId < 0) {
761             Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
762             return;
763         }
764         for (Account sa : sharedAccounts) {
765             if (ArrayUtils.contains(accounts, sa)) continue;
766             // Account doesn't exist. Copy it now.
767             copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
768         }
769     }
770 
771     @Override
onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed)772     public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
773         validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
774     }
775 
776     @Override
getPassword(Account account)777     public String getPassword(Account account) {
778         int callingUid = Binder.getCallingUid();
779         if (Log.isLoggable(TAG, Log.VERBOSE)) {
780             Log.v(TAG, "getPassword: " + account
781                     + ", caller's uid " + Binder.getCallingUid()
782                     + ", pid " + Binder.getCallingPid());
783         }
784         if (account == null) throw new IllegalArgumentException("account is null");
785         int userId = UserHandle.getCallingUserId();
786         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
787             String msg = String.format(
788                     "uid %s cannot get secrets for accounts of type: %s",
789                     callingUid,
790                     account.type);
791             throw new SecurityException(msg);
792         }
793         long identityToken = clearCallingIdentity();
794         try {
795             UserAccounts accounts = getUserAccounts(userId);
796             return readPasswordInternal(accounts, account);
797         } finally {
798             restoreCallingIdentity(identityToken);
799         }
800     }
801 
readPasswordInternal(UserAccounts accounts, Account account)802     private String readPasswordInternal(UserAccounts accounts, Account account) {
803         if (account == null) {
804             return null;
805         }
806         if (!isLocalUnlockedUser(accounts.userId)) {
807             Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
808             return null;
809         }
810 
811         synchronized (accounts.cacheLock) {
812             final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
813             return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
814                     account.type);
815         }
816     }
817 
818     @Override
getPreviousName(Account account)819     public String getPreviousName(Account account) {
820         if (Log.isLoggable(TAG, Log.VERBOSE)) {
821             Log.v(TAG, "getPreviousName: " + account
822                     + ", caller's uid " + Binder.getCallingUid()
823                     + ", pid " + Binder.getCallingPid());
824         }
825         if (account == null) throw new IllegalArgumentException("account is null");
826         int userId = UserHandle.getCallingUserId();
827         long identityToken = clearCallingIdentity();
828         try {
829             UserAccounts accounts = getUserAccounts(userId);
830             return readPreviousNameInternal(accounts, account);
831         } finally {
832             restoreCallingIdentity(identityToken);
833         }
834     }
835 
readPreviousNameInternal(UserAccounts accounts, Account account)836     private String readPreviousNameInternal(UserAccounts accounts, Account account) {
837         if  (account == null) {
838             return null;
839         }
840         synchronized (accounts.cacheLock) {
841             AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
842             if (previousNameRef == null) {
843                 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
844                 Cursor cursor = db.query(
845                         TABLE_ACCOUNTS,
846                         new String[]{ ACCOUNTS_PREVIOUS_NAME },
847                         ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
848                         new String[] { account.name, account.type },
849                         null,
850                         null,
851                         null);
852                 try {
853                     if (cursor.moveToNext()) {
854                         String previousName = cursor.getString(0);
855                         previousNameRef = new AtomicReference<>(previousName);
856                         accounts.previousNameCache.put(account, previousNameRef);
857                         return previousName;
858                     } else {
859                         return null;
860                     }
861                 } finally {
862                     cursor.close();
863                 }
864             } else {
865                 return previousNameRef.get();
866             }
867         }
868     }
869 
870     @Override
getUserData(Account account, String key)871     public String getUserData(Account account, String key) {
872         final int callingUid = Binder.getCallingUid();
873         if (Log.isLoggable(TAG, Log.VERBOSE)) {
874             String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
875                     account, key, callingUid, Binder.getCallingPid());
876             Log.v(TAG, msg);
877         }
878         if (account == null) throw new IllegalArgumentException("account is null");
879         if (key == null) throw new IllegalArgumentException("key is null");
880         int userId = UserHandle.getCallingUserId();
881         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
882             String msg = String.format(
883                     "uid %s cannot get user data for accounts of type: %s",
884                     callingUid,
885                     account.type);
886             throw new SecurityException(msg);
887         }
888         if (!isLocalUnlockedUser(userId)) {
889             Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
890             return null;
891         }
892         long identityToken = clearCallingIdentity();
893         try {
894             UserAccounts accounts = getUserAccounts(userId);
895             synchronized (accounts.cacheLock) {
896                 if (!accountExistsCacheLocked(accounts, account)) {
897                     return null;
898                 }
899                 return readUserDataInternalLocked(accounts, account, key);
900             }
901         } finally {
902             restoreCallingIdentity(identityToken);
903         }
904     }
905 
906     @Override
getAuthenticatorTypes(int userId)907     public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
908         int callingUid = Binder.getCallingUid();
909         if (Log.isLoggable(TAG, Log.VERBOSE)) {
910             Log.v(TAG, "getAuthenticatorTypes: "
911                     + "for user id " + userId
912                     + " caller's uid " + callingUid
913                     + ", pid " + Binder.getCallingPid());
914         }
915         // Only allow the system process to read accounts of other users
916         if (isCrossUser(callingUid, userId)) {
917             throw new SecurityException(
918                     String.format(
919                             "User %s tying to get authenticator types for %s" ,
920                             UserHandle.getCallingUserId(),
921                             userId));
922         }
923 
924         final long identityToken = clearCallingIdentity();
925         try {
926             return getAuthenticatorTypesInternal(userId);
927 
928         } finally {
929             restoreCallingIdentity(identityToken);
930         }
931     }
932 
933     /**
934      * Should only be called inside of a clearCallingIdentity block.
935      */
getAuthenticatorTypesInternal(int userId)936     private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
937         mAuthenticatorCache.updateServices(userId);
938         Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
939                 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
940         AuthenticatorDescription[] types =
941                 new AuthenticatorDescription[authenticatorCollection.size()];
942         int i = 0;
943         for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
944                 : authenticatorCollection) {
945             types[i] = authenticator.type;
946             i++;
947         }
948         return types;
949     }
950 
isCrossUser(int callingUid, int userId)951     private boolean isCrossUser(int callingUid, int userId) {
952         return (userId != UserHandle.getCallingUserId()
953                 && callingUid != Process.myUid()
954                 && mContext.checkCallingOrSelfPermission(
955                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
956                                 != PackageManager.PERMISSION_GRANTED);
957     }
958 
959     @Override
addAccountExplicitly(Account account, String password, Bundle extras)960     public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
961         Bundle.setDefusable(extras, true);
962         final int callingUid = Binder.getCallingUid();
963         if (Log.isLoggable(TAG, Log.VERBOSE)) {
964             Log.v(TAG, "addAccountExplicitly: " + account
965                     + ", caller's uid " + callingUid
966                     + ", pid " + Binder.getCallingPid());
967         }
968         if (account == null) throw new IllegalArgumentException("account is null");
969         int userId = UserHandle.getCallingUserId();
970         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
971             String msg = String.format(
972                     "uid %s cannot explicitly add accounts of type: %s",
973                     callingUid,
974                     account.type);
975             throw new SecurityException(msg);
976         }
977         /*
978          * Child users are not allowed to add accounts. Only the accounts that are
979          * shared by the parent profile can be added to child profile.
980          *
981          * TODO: Only allow accounts that were shared to be added by
982          *     a limited user.
983          */
984 
985         // fails if the account already exists
986         long identityToken = clearCallingIdentity();
987         try {
988             UserAccounts accounts = getUserAccounts(userId);
989             return addAccountInternal(accounts, account, password, extras, callingUid);
990         } finally {
991             restoreCallingIdentity(identityToken);
992         }
993     }
994 
995     @Override
copyAccountToUser(final IAccountManagerResponse response, final Account account, final int userFrom, int userTo)996     public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
997             final int userFrom, int userTo) {
998         int callingUid = Binder.getCallingUid();
999         if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1000             throw new SecurityException("Calling copyAccountToUser requires "
1001                     + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1002         }
1003         final UserAccounts fromAccounts = getUserAccounts(userFrom);
1004         final UserAccounts toAccounts = getUserAccounts(userTo);
1005         if (fromAccounts == null || toAccounts == null) {
1006             if (response != null) {
1007                 Bundle result = new Bundle();
1008                 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1009                 try {
1010                     response.onResult(result);
1011                 } catch (RemoteException e) {
1012                     Slog.w(TAG, "Failed to report error back to the client." + e);
1013                 }
1014             }
1015             return;
1016         }
1017 
1018         Slog.d(TAG, "Copying account " + account.name
1019                 + " from user " + userFrom + " to user " + userTo);
1020         long identityToken = clearCallingIdentity();
1021         try {
1022             new Session(fromAccounts, response, account.type, false,
1023                     false /* stripAuthTokenFromResult */, account.name,
1024                     false /* authDetailsRequired */) {
1025                 @Override
1026                 protected String toDebugString(long now) {
1027                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
1028                             + ", " + account.type;
1029                 }
1030 
1031                 @Override
1032                 public void run() throws RemoteException {
1033                     mAuthenticator.getAccountCredentialsForCloning(this, account);
1034                 }
1035 
1036                 @Override
1037                 public void onResult(Bundle result) {
1038                     Bundle.setDefusable(result, true);
1039                     if (result != null
1040                             && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1041                         // Create a Session for the target user and pass in the bundle
1042                         completeCloningAccount(response, result, account, toAccounts, userFrom);
1043                     } else {
1044                         super.onResult(result);
1045                     }
1046                 }
1047             }.bind();
1048         } finally {
1049             restoreCallingIdentity(identityToken);
1050         }
1051     }
1052 
1053     @Override
accountAuthenticated(final Account account)1054     public boolean accountAuthenticated(final Account account) {
1055         final int callingUid = Binder.getCallingUid();
1056         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1057             String msg = String.format(
1058                     "accountAuthenticated( account: %s, callerUid: %s)",
1059                     account,
1060                     callingUid);
1061             Log.v(TAG, msg);
1062         }
1063         if (account == null) {
1064             throw new IllegalArgumentException("account is null");
1065         }
1066         int userId = UserHandle.getCallingUserId();
1067         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1068             String msg = String.format(
1069                     "uid %s cannot notify authentication for accounts of type: %s",
1070                     callingUid,
1071                     account.type);
1072             throw new SecurityException(msg);
1073         }
1074 
1075         if (!canUserModifyAccounts(userId, callingUid) ||
1076                 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
1077             return false;
1078         }
1079 
1080         long identityToken = clearCallingIdentity();
1081         try {
1082             UserAccounts accounts = getUserAccounts(userId);
1083             return updateLastAuthenticatedTime(account);
1084         } finally {
1085             restoreCallingIdentity(identityToken);
1086         }
1087     }
1088 
updateLastAuthenticatedTime(Account account)1089     private boolean updateLastAuthenticatedTime(Account account) {
1090         final UserAccounts accounts = getUserAccountsForCaller();
1091         synchronized (accounts.cacheLock) {
1092             final ContentValues values = new ContentValues();
1093             values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1094             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1095             int i = db.update(
1096                     TABLE_ACCOUNTS,
1097                     values,
1098                     ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1099                     new String[] {
1100                             account.name, account.type
1101                     });
1102             if (i > 0) {
1103                 return true;
1104             }
1105         }
1106         return false;
1107     }
1108 
completeCloningAccount(IAccountManagerResponse response, final Bundle accountCredentials, final Account account, final UserAccounts targetUser, final int parentUserId)1109     private void completeCloningAccount(IAccountManagerResponse response,
1110             final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1111             final int parentUserId){
1112         Bundle.setDefusable(accountCredentials, true);
1113         long id = clearCallingIdentity();
1114         try {
1115             new Session(targetUser, response, account.type, false,
1116                     false /* stripAuthTokenFromResult */, account.name,
1117                     false /* authDetailsRequired */) {
1118                 @Override
1119                 protected String toDebugString(long now) {
1120                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
1121                             + ", " + account.type;
1122                 }
1123 
1124                 @Override
1125                 public void run() throws RemoteException {
1126                     // Confirm that the owner's account still exists before this step.
1127                     UserAccounts owner = getUserAccounts(parentUserId);
1128                     synchronized (owner.cacheLock) {
1129                         for (Account acc : getAccounts(parentUserId,
1130                                 mContext.getOpPackageName())) {
1131                             if (acc.equals(account)) {
1132                                 mAuthenticator.addAccountFromCredentials(
1133                                         this, account, accountCredentials);
1134                                 break;
1135                             }
1136                         }
1137                     }
1138                 }
1139 
1140                 @Override
1141                 public void onResult(Bundle result) {
1142                     Bundle.setDefusable(result, true);
1143                     // TODO: Anything to do if if succedded?
1144                     // TODO: If it failed: Show error notification? Should we remove the shadow
1145                     // account to avoid retries?
1146                     super.onResult(result);
1147                 }
1148 
1149                 @Override
1150                 public void onError(int errorCode, String errorMessage) {
1151                     super.onError(errorCode,  errorMessage);
1152                     // TODO: Show error notification to user
1153                     // TODO: Should we remove the shadow account so that it doesn't keep trying?
1154                 }
1155 
1156             }.bind();
1157         } finally {
1158             restoreCallingIdentity(id);
1159         }
1160     }
1161 
addAccountInternal(UserAccounts accounts, Account account, String password, Bundle extras, int callingUid)1162     private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
1163             Bundle extras, int callingUid) {
1164         Bundle.setDefusable(extras, true);
1165         if (account == null) {
1166             return false;
1167         }
1168         if (!isLocalUnlockedUser(accounts.userId)) {
1169             Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1170                     + " is locked. callingUid=" + callingUid);
1171             return false;
1172         }
1173         synchronized (accounts.cacheLock) {
1174             final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1175             db.beginTransaction();
1176             try {
1177                 long numMatches = DatabaseUtils.longForQuery(db,
1178                         "select count(*) from " + CE_TABLE_ACCOUNTS
1179                                 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1180                         new String[]{account.name, account.type});
1181                 if (numMatches > 0) {
1182                     Log.w(TAG, "insertAccountIntoDatabase: " + account
1183                             + ", skipping since the account already exists");
1184                     return false;
1185                 }
1186                 ContentValues values = new ContentValues();
1187                 values.put(ACCOUNTS_NAME, account.name);
1188                 values.put(ACCOUNTS_TYPE, account.type);
1189                 values.put(ACCOUNTS_PASSWORD, password);
1190                 long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
1191                 if (accountId < 0) {
1192                     Log.w(TAG, "insertAccountIntoDatabase: " + account
1193                             + ", skipping the DB insert failed");
1194                     return false;
1195                 }
1196                 // Insert into DE table
1197                 values = new ContentValues();
1198                 values.put(ACCOUNTS_ID, accountId);
1199                 values.put(ACCOUNTS_NAME, account.name);
1200                 values.put(ACCOUNTS_TYPE, account.type);
1201                 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
1202                         System.currentTimeMillis());
1203                 if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
1204                     Log.w(TAG, "insertAccountIntoDatabase: " + account
1205                             + ", skipping the DB insert failed");
1206                     return false;
1207                 }
1208                 if (extras != null) {
1209                     for (String key : extras.keySet()) {
1210                         final String value = extras.getString(key);
1211                         if (insertExtraLocked(db, accountId, key, value) < 0) {
1212                             Log.w(TAG, "insertAccountIntoDatabase: " + account
1213                                     + ", skipping since insertExtra failed for key " + key);
1214                             return false;
1215                         }
1216                     }
1217                 }
1218                 db.setTransactionSuccessful();
1219 
1220                 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1221                         accounts, callingUid);
1222 
1223                 insertAccountIntoCacheLocked(accounts, account);
1224             } finally {
1225                 db.endTransaction();
1226             }
1227         }
1228         if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1229             addAccountToLinkedRestrictedUsers(account, accounts.userId);
1230         }
1231 
1232         // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1233         sendAccountsChangedBroadcast(accounts.userId);
1234         return true;
1235     }
1236 
isLocalUnlockedUser(int userId)1237     private boolean isLocalUnlockedUser(int userId) {
1238         synchronized (mUsers) {
1239             return mLocalUnlockedUsers.get(userId);
1240         }
1241     }
1242 
1243     /**
1244      * Adds the account to all linked restricted users as shared accounts. If the user is currently
1245      * running, then clone the account too.
1246      * @param account the account to share with limited users
1247      *
1248      */
addAccountToLinkedRestrictedUsers(Account account, int parentUserId)1249     private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
1250         List<UserInfo> users = getUserManager().getUsers();
1251         for (UserInfo user : users) {
1252             if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
1253                 addSharedAccountAsUser(account, user.id);
1254                 if (isLocalUnlockedUser(user.id)) {
1255                     mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
1256                             MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
1257                 }
1258             }
1259         }
1260     }
1261 
insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value)1262     private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
1263         ContentValues values = new ContentValues();
1264         values.put(EXTRAS_KEY, key);
1265         values.put(EXTRAS_ACCOUNTS_ID, accountId);
1266         values.put(EXTRAS_VALUE, value);
1267         return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
1268     }
1269 
1270     @Override
hasFeatures(IAccountManagerResponse response, Account account, String[] features, String opPackageName)1271     public void hasFeatures(IAccountManagerResponse response,
1272             Account account, String[] features, String opPackageName) {
1273         int callingUid = Binder.getCallingUid();
1274         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1275             Log.v(TAG, "hasFeatures: " + account
1276                     + ", response " + response
1277                     + ", features " + stringArrayToString(features)
1278                     + ", caller's uid " + callingUid
1279                     + ", pid " + Binder.getCallingPid());
1280         }
1281         if (response == null) throw new IllegalArgumentException("response is null");
1282         if (account == null) throw new IllegalArgumentException("account is null");
1283         if (features == null) throw new IllegalArgumentException("features is null");
1284         int userId = UserHandle.getCallingUserId();
1285         checkReadAccountsPermitted(callingUid, account.type, userId,
1286                 opPackageName);
1287 
1288         long identityToken = clearCallingIdentity();
1289         try {
1290             UserAccounts accounts = getUserAccounts(userId);
1291             new TestFeaturesSession(accounts, response, account, features).bind();
1292         } finally {
1293             restoreCallingIdentity(identityToken);
1294         }
1295     }
1296 
1297     private class TestFeaturesSession extends Session {
1298         private final String[] mFeatures;
1299         private final Account mAccount;
1300 
TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, Account account, String[] features)1301         public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
1302                 Account account, String[] features) {
1303             super(accounts, response, account.type, false /* expectActivityLaunch */,
1304                     true /* stripAuthTokenFromResult */, account.name,
1305                     false /* authDetailsRequired */);
1306             mFeatures = features;
1307             mAccount = account;
1308         }
1309 
1310         @Override
run()1311         public void run() throws RemoteException {
1312             try {
1313                 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1314             } catch (RemoteException e) {
1315                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1316             }
1317         }
1318 
1319         @Override
onResult(Bundle result)1320         public void onResult(Bundle result) {
1321             Bundle.setDefusable(result, true);
1322             IAccountManagerResponse response = getResponseAndClose();
1323             if (response != null) {
1324                 try {
1325                     if (result == null) {
1326                         response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
1327                         return;
1328                     }
1329                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
1330                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1331                                 + response);
1332                     }
1333                     final Bundle newResult = new Bundle();
1334                     newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1335                             result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1336                     response.onResult(newResult);
1337                 } catch (RemoteException e) {
1338                     // if the caller is dead then there is no one to care about remote exceptions
1339                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
1340                         Log.v(TAG, "failure while notifying response", e);
1341                     }
1342                 }
1343             }
1344         }
1345 
1346         @Override
toDebugString(long now)1347         protected String toDebugString(long now) {
1348             return super.toDebugString(now) + ", hasFeatures"
1349                     + ", " + mAccount
1350                     + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1351         }
1352     }
1353 
1354     @Override
renameAccount( IAccountManagerResponse response, Account accountToRename, String newName)1355     public void renameAccount(
1356             IAccountManagerResponse response, Account accountToRename, String newName) {
1357         final int callingUid = Binder.getCallingUid();
1358         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1359             Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
1360                 + ", caller's uid " + callingUid
1361                 + ", pid " + Binder.getCallingPid());
1362         }
1363         if (accountToRename == null) throw new IllegalArgumentException("account is null");
1364         int userId = UserHandle.getCallingUserId();
1365         if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
1366             String msg = String.format(
1367                     "uid %s cannot rename accounts of type: %s",
1368                     callingUid,
1369                     accountToRename.type);
1370             throw new SecurityException(msg);
1371         }
1372         long identityToken = clearCallingIdentity();
1373         try {
1374             UserAccounts accounts = getUserAccounts(userId);
1375             Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
1376             Bundle result = new Bundle();
1377             result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1378             result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1379             try {
1380                 response.onResult(result);
1381             } catch (RemoteException e) {
1382                 Log.w(TAG, e.getMessage());
1383             }
1384         } finally {
1385             restoreCallingIdentity(identityToken);
1386         }
1387     }
1388 
renameAccountInternal( UserAccounts accounts, Account accountToRename, String newName)1389     private Account renameAccountInternal(
1390             UserAccounts accounts, Account accountToRename, String newName) {
1391         Account resultAccount = null;
1392         /*
1393          * Cancel existing notifications. Let authenticators
1394          * re-post notifications as required. But we don't know if
1395          * the authenticators have bound their notifications to
1396          * now stale account name data.
1397          *
1398          * With a rename api, we might not need to do this anymore but it
1399          * shouldn't hurt.
1400          */
1401         cancelNotification(
1402                 getSigninRequiredNotificationId(accounts, accountToRename),
1403                  new UserHandle(accounts.userId));
1404         synchronized(accounts.credentialsPermissionNotificationIds) {
1405             for (Pair<Pair<Account, String>, Integer> pair:
1406                     accounts.credentialsPermissionNotificationIds.keySet()) {
1407                 if (accountToRename.equals(pair.first.first)) {
1408                     int id = accounts.credentialsPermissionNotificationIds.get(pair);
1409                     cancelNotification(id, new UserHandle(accounts.userId));
1410                 }
1411             }
1412         }
1413         synchronized (accounts.cacheLock) {
1414             final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1415             db.beginTransaction();
1416             Account renamedAccount = new Account(newName, accountToRename.type);
1417             try {
1418                 final long accountId = getAccountIdLocked(db, accountToRename);
1419                 if (accountId >= 0) {
1420                     final ContentValues values = new ContentValues();
1421                     values.put(ACCOUNTS_NAME, newName);
1422                     final String[] argsAccountId = { String.valueOf(accountId) };
1423                     db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1424                     // Update NAME/PREVIOUS_NAME in DE accounts table
1425                     values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1426                     db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1427                     db.setTransactionSuccessful();
1428                     logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1429                             accounts);
1430                 }
1431             } finally {
1432                 db.endTransaction();
1433             }
1434             /*
1435              * Database transaction was successful. Clean up cached
1436              * data associated with the account in the user profile.
1437              */
1438             insertAccountIntoCacheLocked(accounts, renamedAccount);
1439             /*
1440              * Extract the data and token caches before removing the
1441              * old account to preserve the user data associated with
1442              * the account.
1443              */
1444             HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1445             HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1446             removeAccountFromCacheLocked(accounts, accountToRename);
1447             /*
1448              * Update the cached data associated with the renamed
1449              * account.
1450              */
1451             accounts.userDataCache.put(renamedAccount, tmpData);
1452             accounts.authTokenCache.put(renamedAccount, tmpTokens);
1453             accounts.previousNameCache.put(
1454                     renamedAccount,
1455                     new AtomicReference<String>(accountToRename.name));
1456             resultAccount = renamedAccount;
1457 
1458             int parentUserId = accounts.userId;
1459             if (canHaveProfile(parentUserId)) {
1460                 /*
1461                  * Owner or system user account was renamed, rename the account for
1462                  * those users with which the account was shared.
1463                  */
1464                 List<UserInfo> users = getUserManager().getUsers(true);
1465                 for (UserInfo user : users) {
1466                     if (user.isRestricted()
1467                             && (user.restrictedProfileParentId == parentUserId)) {
1468                         renameSharedAccountAsUser(accountToRename, newName, user.id);
1469                     }
1470                 }
1471             }
1472             sendAccountsChangedBroadcast(accounts.userId);
1473         }
1474         return resultAccount;
1475     }
1476 
canHaveProfile(final int parentUserId)1477     private boolean canHaveProfile(final int parentUserId) {
1478         final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
1479         return userInfo != null && userInfo.canHaveProfile();
1480     }
1481 
1482     @Override
removeAccount(IAccountManagerResponse response, Account account, boolean expectActivityLaunch)1483     public void removeAccount(IAccountManagerResponse response, Account account,
1484             boolean expectActivityLaunch) {
1485         removeAccountAsUser(
1486                 response,
1487                 account,
1488                 expectActivityLaunch,
1489                 UserHandle.getCallingUserId());
1490     }
1491 
1492     @Override
removeAccountAsUser(IAccountManagerResponse response, Account account, boolean expectActivityLaunch, int userId)1493     public void removeAccountAsUser(IAccountManagerResponse response, Account account,
1494             boolean expectActivityLaunch, int userId) {
1495         final int callingUid = Binder.getCallingUid();
1496         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1497             Log.v(TAG, "removeAccount: " + account
1498                     + ", response " + response
1499                     + ", caller's uid " + callingUid
1500                     + ", pid " + Binder.getCallingPid()
1501                     + ", for user id " + userId);
1502         }
1503         if (response == null) throw new IllegalArgumentException("response is null");
1504         if (account == null) throw new IllegalArgumentException("account is null");
1505         // Only allow the system process to modify accounts of other users
1506         if (isCrossUser(callingUid, userId)) {
1507             throw new SecurityException(
1508                     String.format(
1509                             "User %s tying remove account for %s" ,
1510                             UserHandle.getCallingUserId(),
1511                             userId));
1512         }
1513         /*
1514          * Only the system or authenticator should be allowed to remove accounts for that
1515          * authenticator.  This will let users remove accounts (via Settings in the system) but not
1516          * arbitrary applications (like competing authenticators).
1517          */
1518         UserHandle user = UserHandle.of(userId);
1519         if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1520                 && !isSystemUid(callingUid)) {
1521             String msg = String.format(
1522                     "uid %s cannot remove accounts of type: %s",
1523                     callingUid,
1524                     account.type);
1525             throw new SecurityException(msg);
1526         }
1527         if (!canUserModifyAccounts(userId, callingUid)) {
1528             try {
1529                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1530                         "User cannot modify accounts");
1531             } catch (RemoteException re) {
1532             }
1533             return;
1534         }
1535         if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
1536             try {
1537                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1538                         "User cannot modify accounts of this type (policy).");
1539             } catch (RemoteException re) {
1540             }
1541             return;
1542         }
1543         long identityToken = clearCallingIdentity();
1544         UserAccounts accounts = getUserAccounts(userId);
1545         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
1546         synchronized(accounts.credentialsPermissionNotificationIds) {
1547             for (Pair<Pair<Account, String>, Integer> pair:
1548                 accounts.credentialsPermissionNotificationIds.keySet()) {
1549                 if (account.equals(pair.first.first)) {
1550                     int id = accounts.credentialsPermissionNotificationIds.get(pair);
1551                     cancelNotification(id, user);
1552                 }
1553             }
1554         }
1555         SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
1556         final long accountId = getAccountIdLocked(db, account);
1557         logRecord(
1558                 db,
1559                 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
1560                 TABLE_ACCOUNTS,
1561                 accountId,
1562                 accounts,
1563                 callingUid);
1564         try {
1565             new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1566         } finally {
1567             restoreCallingIdentity(identityToken);
1568         }
1569     }
1570 
1571     @Override
removeAccountExplicitly(Account account)1572     public boolean removeAccountExplicitly(Account account) {
1573         final int callingUid = Binder.getCallingUid();
1574         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1575             Log.v(TAG, "removeAccountExplicitly: " + account
1576                     + ", caller's uid " + callingUid
1577                     + ", pid " + Binder.getCallingPid());
1578         }
1579         int userId = Binder.getCallingUserHandle().getIdentifier();
1580         if (account == null) {
1581             /*
1582              * Null accounts should result in returning false, as per
1583              * AccountManage.addAccountExplicitly(...) java doc.
1584              */
1585             Log.e(TAG, "account is null");
1586             return false;
1587         } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1588             String msg = String.format(
1589                     "uid %s cannot explicitly add accounts of type: %s",
1590                     callingUid,
1591                     account.type);
1592             throw new SecurityException(msg);
1593         }
1594         UserAccounts accounts = getUserAccountsForCaller();
1595         SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
1596         final long accountId = getAccountIdLocked(db, account);
1597         logRecord(
1598                 db,
1599                 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
1600                 TABLE_ACCOUNTS,
1601                 accountId,
1602                 accounts,
1603                 callingUid);
1604         long identityToken = clearCallingIdentity();
1605         try {
1606             return removeAccountInternal(accounts, account, callingUid);
1607         } finally {
1608             restoreCallingIdentity(identityToken);
1609         }
1610     }
1611 
1612     private class RemoveAccountSession extends Session {
1613         final Account mAccount;
RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, Account account, boolean expectActivityLaunch)1614         public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
1615                 Account account, boolean expectActivityLaunch) {
1616             super(accounts, response, account.type, expectActivityLaunch,
1617                     true /* stripAuthTokenFromResult */, account.name,
1618                     false /* authDetailsRequired */);
1619             mAccount = account;
1620         }
1621 
1622         @Override
toDebugString(long now)1623         protected String toDebugString(long now) {
1624             return super.toDebugString(now) + ", removeAccount"
1625                     + ", account " + mAccount;
1626         }
1627 
1628         @Override
run()1629         public void run() throws RemoteException {
1630             mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1631         }
1632 
1633         @Override
onResult(Bundle result)1634         public void onResult(Bundle result) {
1635             Bundle.setDefusable(result, true);
1636             if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1637                     && !result.containsKey(AccountManager.KEY_INTENT)) {
1638                 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
1639                 if (removalAllowed) {
1640                     removeAccountInternal(mAccounts, mAccount, getCallingUid());
1641                 }
1642                 IAccountManagerResponse response = getResponseAndClose();
1643                 if (response != null) {
1644                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
1645                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1646                                 + response);
1647                     }
1648                     Bundle result2 = new Bundle();
1649                     result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
1650                     try {
1651                         response.onResult(result2);
1652                     } catch (RemoteException e) {
1653                         // ignore
1654                     }
1655                 }
1656             }
1657             super.onResult(result);
1658         }
1659     }
1660 
1661     @VisibleForTesting
removeAccountInternal(Account account)1662     protected void removeAccountInternal(Account account) {
1663         removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
1664     }
1665 
removeAccountInternal(UserAccounts accounts, Account account, int callingUid)1666     private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
1667         boolean isChanged = false;
1668         boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
1669         if (!userUnlocked) {
1670             Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
1671                     + " is still locked. CE data will be removed later");
1672         }
1673         synchronized (accounts.cacheLock) {
1674             final SQLiteDatabase db = userUnlocked
1675                     ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
1676                     : accounts.openHelper.getWritableDatabase();
1677             db.beginTransaction();
1678             // Set to a dummy value, this will only be used if the database
1679             // transaction succeeds.
1680             long accountId = -1;
1681             try {
1682                 accountId = getAccountIdLocked(db, account);
1683                 if (accountId >= 0) {
1684                     db.delete(
1685                             TABLE_ACCOUNTS,
1686                             ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1687                             new String[]{ account.name, account.type });
1688                     if (userUnlocked) {
1689                         // Delete from CE table
1690                         db.delete(
1691                                 CE_TABLE_ACCOUNTS,
1692                                 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1693                                 new String[]{ account.name, account.type });
1694                     }
1695                     db.setTransactionSuccessful();
1696                     isChanged = true;
1697                 }
1698             } finally {
1699                 db.endTransaction();
1700             }
1701             if (isChanged) {
1702                 removeAccountFromCacheLocked(accounts, account);
1703                 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
1704                 sendAccountsChangedBroadcast(accounts.userId);
1705                 String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
1706                         : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
1707                 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
1708             }
1709         }
1710         long id = Binder.clearCallingIdentity();
1711         try {
1712             int parentUserId = accounts.userId;
1713             if (canHaveProfile(parentUserId)) {
1714                 // Remove from any restricted profiles that are sharing this account.
1715                 List<UserInfo> users = getUserManager().getUsers(true);
1716                 for (UserInfo user : users) {
1717                     if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
1718                         removeSharedAccountAsUser(account, user.id, callingUid);
1719                     }
1720                 }
1721             }
1722         } finally {
1723             Binder.restoreCallingIdentity(id);
1724         }
1725         return isChanged;
1726     }
1727 
1728     @Override
invalidateAuthToken(String accountType, String authToken)1729     public void invalidateAuthToken(String accountType, String authToken) {
1730         int callerUid = Binder.getCallingUid();
1731         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1732             Log.v(TAG, "invalidateAuthToken: accountType " + accountType
1733                     + ", caller's uid " + callerUid
1734                     + ", pid " + Binder.getCallingPid());
1735         }
1736         if (accountType == null) throw new IllegalArgumentException("accountType is null");
1737         if (authToken == null) throw new IllegalArgumentException("authToken is null");
1738         int userId = UserHandle.getCallingUserId();
1739         long identityToken = clearCallingIdentity();
1740         try {
1741             UserAccounts accounts = getUserAccounts(userId);
1742             synchronized (accounts.cacheLock) {
1743                 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1744                 db.beginTransaction();
1745                 try {
1746                     invalidateAuthTokenLocked(accounts, db, accountType, authToken);
1747                     invalidateCustomTokenLocked(accounts, accountType, authToken);
1748                     db.setTransactionSuccessful();
1749                 } finally {
1750                     db.endTransaction();
1751                 }
1752             }
1753         } finally {
1754             restoreCallingIdentity(identityToken);
1755         }
1756     }
1757 
invalidateCustomTokenLocked( UserAccounts accounts, String accountType, String authToken)1758     private void invalidateCustomTokenLocked(
1759             UserAccounts accounts,
1760             String accountType,
1761             String authToken) {
1762         if (authToken == null || accountType == null) {
1763             return;
1764         }
1765         // Also wipe out cached token in memory.
1766         accounts.accountTokenCaches.remove(accountType, authToken);
1767     }
1768 
invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, String accountType, String authToken)1769     private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1770             String accountType, String authToken) {
1771         if (authToken == null || accountType == null) {
1772             return;
1773         }
1774         Cursor cursor = db.rawQuery(
1775                 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1776                         + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1777                         + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1778                         + " FROM " + CE_TABLE_ACCOUNTS
1779                         + " JOIN " + CE_TABLE_AUTHTOKENS
1780                         + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1781                         + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
1782                         + " WHERE " + CE_TABLE_AUTHTOKENS + "."  + AUTHTOKENS_AUTHTOKEN
1783                         + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1784                 new String[]{authToken, accountType});
1785         try {
1786             while (cursor.moveToNext()) {
1787                 long authTokenId = cursor.getLong(0);
1788                 String accountName = cursor.getString(1);
1789                 String authTokenType = cursor.getString(2);
1790                 db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
1791                 writeAuthTokenIntoCacheLocked(
1792                         accounts,
1793                         db,
1794                         new Account(accountName, accountType),
1795                         authTokenType,
1796                         null);
1797             }
1798         } finally {
1799             cursor.close();
1800         }
1801     }
1802 
saveCachedToken( UserAccounts accounts, Account account, String callerPkg, byte[] callerSigDigest, String tokenType, String token, long expiryMillis)1803     private void saveCachedToken(
1804             UserAccounts accounts,
1805             Account account,
1806             String callerPkg,
1807             byte[] callerSigDigest,
1808             String tokenType,
1809             String token,
1810             long expiryMillis) {
1811 
1812         if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1813             return;
1814         }
1815         cancelNotification(getSigninRequiredNotificationId(accounts, account),
1816                 UserHandle.of(accounts.userId));
1817         synchronized (accounts.cacheLock) {
1818             accounts.accountTokenCaches.put(
1819                     account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
1820         }
1821     }
1822 
saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, String authToken)1823     private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1824             String authToken) {
1825         if (account == null || type == null) {
1826             return false;
1827         }
1828         cancelNotification(getSigninRequiredNotificationId(accounts, account),
1829                 UserHandle.of(accounts.userId));
1830         synchronized (accounts.cacheLock) {
1831             final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1832             db.beginTransaction();
1833             try {
1834                 long accountId = getAccountIdLocked(db, account);
1835                 if (accountId < 0) {
1836                     return false;
1837                 }
1838                 db.delete(CE_TABLE_AUTHTOKENS,
1839                         AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1840                         new String[]{type});
1841                 ContentValues values = new ContentValues();
1842                 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1843                 values.put(AUTHTOKENS_TYPE, type);
1844                 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1845                 if (db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1846                     db.setTransactionSuccessful();
1847                     writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
1848                     return true;
1849                 }
1850                 return false;
1851             } finally {
1852                 db.endTransaction();
1853             }
1854         }
1855     }
1856 
1857     @Override
peekAuthToken(Account account, String authTokenType)1858     public String peekAuthToken(Account account, String authTokenType) {
1859         final int callingUid = Binder.getCallingUid();
1860         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1861             Log.v(TAG, "peekAuthToken: " + account
1862                     + ", authTokenType " + authTokenType
1863                     + ", caller's uid " + callingUid
1864                     + ", pid " + Binder.getCallingPid());
1865         }
1866         if (account == null) throw new IllegalArgumentException("account is null");
1867         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1868         int userId = UserHandle.getCallingUserId();
1869         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1870             String msg = String.format(
1871                     "uid %s cannot peek the authtokens associated with accounts of type: %s",
1872                     callingUid,
1873                     account.type);
1874             throw new SecurityException(msg);
1875         }
1876         if (!isLocalUnlockedUser(userId)) {
1877             Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
1878                     + callingUid);
1879             return null;
1880         }
1881         long identityToken = clearCallingIdentity();
1882         try {
1883             UserAccounts accounts = getUserAccounts(userId);
1884             return readAuthTokenInternal(accounts, account, authTokenType);
1885         } finally {
1886             restoreCallingIdentity(identityToken);
1887         }
1888     }
1889 
1890     @Override
setAuthToken(Account account, String authTokenType, String authToken)1891     public void setAuthToken(Account account, String authTokenType, String authToken) {
1892         final int callingUid = Binder.getCallingUid();
1893         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1894             Log.v(TAG, "setAuthToken: " + account
1895                     + ", authTokenType " + authTokenType
1896                     + ", caller's uid " + callingUid
1897                     + ", pid " + Binder.getCallingPid());
1898         }
1899         if (account == null) throw new IllegalArgumentException("account is null");
1900         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1901         int userId = UserHandle.getCallingUserId();
1902         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1903             String msg = String.format(
1904                     "uid %s cannot set auth tokens associated with accounts of type: %s",
1905                     callingUid,
1906                     account.type);
1907             throw new SecurityException(msg);
1908         }
1909         long identityToken = clearCallingIdentity();
1910         try {
1911             UserAccounts accounts = getUserAccounts(userId);
1912             saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
1913         } finally {
1914             restoreCallingIdentity(identityToken);
1915         }
1916     }
1917 
1918     @Override
setPassword(Account account, String password)1919     public void setPassword(Account account, String password) {
1920         final int callingUid = Binder.getCallingUid();
1921         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1922             Log.v(TAG, "setAuthToken: " + account
1923                     + ", caller's uid " + callingUid
1924                     + ", pid " + Binder.getCallingPid());
1925         }
1926         if (account == null) throw new IllegalArgumentException("account is null");
1927         int userId = UserHandle.getCallingUserId();
1928         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1929             String msg = String.format(
1930                     "uid %s cannot set secrets for accounts of type: %s",
1931                     callingUid,
1932                     account.type);
1933             throw new SecurityException(msg);
1934         }
1935         long identityToken = clearCallingIdentity();
1936         try {
1937             UserAccounts accounts = getUserAccounts(userId);
1938             setPasswordInternal(accounts, account, password, callingUid);
1939         } finally {
1940             restoreCallingIdentity(identityToken);
1941         }
1942     }
1943 
setPasswordInternal(UserAccounts accounts, Account account, String password, int callingUid)1944     private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1945             int callingUid) {
1946         if (account == null) {
1947             return;
1948         }
1949         boolean isChanged = false;
1950         synchronized (accounts.cacheLock) {
1951             final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1952             db.beginTransaction();
1953             try {
1954                 final ContentValues values = new ContentValues();
1955                 values.put(ACCOUNTS_PASSWORD, password);
1956                 final long accountId = getAccountIdLocked(db, account);
1957                 if (accountId >= 0) {
1958                     final String[] argsAccountId = {String.valueOf(accountId)};
1959                     db.update(
1960                             CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1961                     db.delete(
1962                             CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
1963                     accounts.authTokenCache.remove(account);
1964                     accounts.accountTokenCaches.remove(account);
1965                     db.setTransactionSuccessful();
1966                     // If there is an account whose password will be updated and the database
1967                     // transactions succeed, then we say that a change has occured. Even if the
1968                     // new password is the same as the old and there were no authtokens to delete.
1969                     isChanged = true;
1970                     String action = (password == null || password.length() == 0) ?
1971                             DebugDbHelper.ACTION_CLEAR_PASSWORD
1972                             : DebugDbHelper.ACTION_SET_PASSWORD;
1973                     logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
1974                 }
1975             } finally {
1976                 db.endTransaction();
1977                 if (isChanged) {
1978                     // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
1979                     sendAccountsChangedBroadcast(accounts.userId);
1980                 }
1981             }
1982         }
1983     }
1984 
sendAccountsChangedBroadcast(int userId)1985     private void sendAccountsChangedBroadcast(int userId) {
1986         Log.i(TAG, "the accounts changed, sending broadcast of "
1987                 + ACCOUNTS_CHANGED_INTENT.getAction());
1988         mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
1989     }
1990 
1991     @Override
clearPassword(Account account)1992     public void clearPassword(Account account) {
1993         final int callingUid = Binder.getCallingUid();
1994         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1995             Log.v(TAG, "clearPassword: " + account
1996                     + ", caller's uid " + callingUid
1997                     + ", pid " + Binder.getCallingPid());
1998         }
1999         if (account == null) throw new IllegalArgumentException("account is null");
2000         int userId = UserHandle.getCallingUserId();
2001         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2002             String msg = String.format(
2003                     "uid %s cannot clear passwords for accounts of type: %s",
2004                     callingUid,
2005                     account.type);
2006             throw new SecurityException(msg);
2007         }
2008         long identityToken = clearCallingIdentity();
2009         try {
2010             UserAccounts accounts = getUserAccounts(userId);
2011             setPasswordInternal(accounts, account, null, callingUid);
2012         } finally {
2013             restoreCallingIdentity(identityToken);
2014         }
2015     }
2016 
2017     @Override
setUserData(Account account, String key, String value)2018     public void setUserData(Account account, String key, String value) {
2019         final int callingUid = Binder.getCallingUid();
2020         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2021             Log.v(TAG, "setUserData: " + account
2022                     + ", key " + key
2023                     + ", caller's uid " + callingUid
2024                     + ", pid " + Binder.getCallingPid());
2025         }
2026         if (key == null) throw new IllegalArgumentException("key is null");
2027         if (account == null) throw new IllegalArgumentException("account is null");
2028         int userId = UserHandle.getCallingUserId();
2029         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2030             String msg = String.format(
2031                     "uid %s cannot set user data for accounts of type: %s",
2032                     callingUid,
2033                     account.type);
2034             throw new SecurityException(msg);
2035         }
2036         long identityToken = clearCallingIdentity();
2037         try {
2038             UserAccounts accounts = getUserAccounts(userId);
2039             synchronized (accounts.cacheLock) {
2040                 if (!accountExistsCacheLocked(accounts, account)) {
2041                     return;
2042                 }
2043                 setUserdataInternalLocked(accounts, account, key, value);
2044             }
2045         } finally {
2046             restoreCallingIdentity(identityToken);
2047         }
2048     }
2049 
accountExistsCacheLocked(UserAccounts accounts, Account account)2050     private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2051         if (accounts.accountCache.containsKey(account.type)) {
2052             for (Account acc : accounts.accountCache.get(account.type)) {
2053                 if (acc.name.equals(account.name)) {
2054                     return true;
2055                 }
2056             }
2057         }
2058         return false;
2059     }
2060 
setUserdataInternalLocked(UserAccounts accounts, Account account, String key, String value)2061     private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
2062             String value) {
2063         if (account == null || key == null) {
2064             return;
2065         }
2066         final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2067         db.beginTransaction();
2068         try {
2069             long accountId = getAccountIdLocked(db, account);
2070             if (accountId < 0) {
2071                 return;
2072             }
2073             long extrasId = getExtrasIdLocked(db, accountId, key);
2074             if (extrasId < 0) {
2075                 extrasId = insertExtraLocked(db, accountId, key, value);
2076                 if (extrasId < 0) {
2077                     return;
2078                 }
2079             } else {
2080                 ContentValues values = new ContentValues();
2081                 values.put(EXTRAS_VALUE, value);
2082                 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
2083                     return;
2084                 }
2085             }
2086             writeUserDataIntoCacheLocked(accounts, db, account, key, value);
2087             db.setTransactionSuccessful();
2088         } finally {
2089             db.endTransaction();
2090         }
2091     }
2092 
onResult(IAccountManagerResponse response, Bundle result)2093     private void onResult(IAccountManagerResponse response, Bundle result) {
2094         if (result == null) {
2095             Log.e(TAG, "the result is unexpectedly null", new Exception());
2096         }
2097         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2098             Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2099                     + response);
2100         }
2101         try {
2102             response.onResult(result);
2103         } catch (RemoteException e) {
2104             // if the caller is dead then there is no one to care about remote
2105             // exceptions
2106             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2107                 Log.v(TAG, "failure while notifying response", e);
2108             }
2109         }
2110     }
2111 
2112     @Override
getAuthTokenLabel(IAccountManagerResponse response, final String accountType, final String authTokenType)2113     public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2114                                   final String authTokenType)
2115             throws RemoteException {
2116         if (accountType == null) throw new IllegalArgumentException("accountType is null");
2117         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2118 
2119         final int callingUid = getCallingUid();
2120         clearCallingIdentity();
2121         if (callingUid != Process.SYSTEM_UID) {
2122             throw new SecurityException("can only call from system");
2123         }
2124         int userId = UserHandle.getUserId(callingUid);
2125         long identityToken = clearCallingIdentity();
2126         try {
2127             UserAccounts accounts = getUserAccounts(userId);
2128             new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2129                     false /* stripAuthTokenFromResult */,  null /* accountName */,
2130                     false /* authDetailsRequired */) {
2131                 @Override
2132                 protected String toDebugString(long now) {
2133                     return super.toDebugString(now) + ", getAuthTokenLabel"
2134                             + ", " + accountType
2135                             + ", authTokenType " + authTokenType;
2136                 }
2137 
2138                 @Override
2139                 public void run() throws RemoteException {
2140                     mAuthenticator.getAuthTokenLabel(this, authTokenType);
2141                 }
2142 
2143                 @Override
2144                 public void onResult(Bundle result) {
2145                     Bundle.setDefusable(result, true);
2146                     if (result != null) {
2147                         String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2148                         Bundle bundle = new Bundle();
2149                         bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2150                         super.onResult(bundle);
2151                         return;
2152                     } else {
2153                         super.onResult(result);
2154                     }
2155                 }
2156             }.bind();
2157         } finally {
2158             restoreCallingIdentity(identityToken);
2159         }
2160     }
2161 
2162     @Override
getAuthToken( IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, final boolean expectActivityLaunch, final Bundle loginOptions)2163     public void getAuthToken(
2164             IAccountManagerResponse response,
2165             final Account account,
2166             final String authTokenType,
2167             final boolean notifyOnAuthFailure,
2168             final boolean expectActivityLaunch,
2169             final Bundle loginOptions) {
2170         Bundle.setDefusable(loginOptions, true);
2171         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2172             Log.v(TAG, "getAuthToken: " + account
2173                     + ", response " + response
2174                     + ", authTokenType " + authTokenType
2175                     + ", notifyOnAuthFailure " + notifyOnAuthFailure
2176                     + ", expectActivityLaunch " + expectActivityLaunch
2177                     + ", caller's uid " + Binder.getCallingUid()
2178                     + ", pid " + Binder.getCallingPid());
2179         }
2180         if (response == null) throw new IllegalArgumentException("response is null");
2181         try {
2182             if (account == null) {
2183                 Slog.w(TAG, "getAuthToken called with null account");
2184                 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2185                 return;
2186             }
2187             if (authTokenType == null) {
2188                 Slog.w(TAG, "getAuthToken called with null authTokenType");
2189                 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2190                 return;
2191             }
2192         } catch (RemoteException e) {
2193             Slog.w(TAG, "Failed to report error back to the client." + e);
2194             return;
2195         }
2196         int userId = UserHandle.getCallingUserId();
2197         long ident = Binder.clearCallingIdentity();
2198         final UserAccounts accounts;
2199         final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
2200         try {
2201             accounts = getUserAccounts(userId);
2202             authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2203                     AuthenticatorDescription.newKey(account.type), accounts.userId);
2204         } finally {
2205             Binder.restoreCallingIdentity(ident);
2206         }
2207 
2208         final boolean customTokens =
2209                 authenticatorInfo != null && authenticatorInfo.type.customTokens;
2210 
2211         // skip the check if customTokens
2212         final int callerUid = Binder.getCallingUid();
2213         final boolean permissionGranted =
2214                 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
2215 
2216         // Get the calling package. We will use it for the purpose of caching.
2217         final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
2218         List<String> callerOwnedPackageNames;
2219         ident = Binder.clearCallingIdentity();
2220         try {
2221             callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2222         } finally {
2223             Binder.restoreCallingIdentity(ident);
2224         }
2225         if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2226             String msg = String.format(
2227                     "Uid %s is attempting to illegally masquerade as package %s!",
2228                     callerUid,
2229                     callerPkg);
2230             throw new SecurityException(msg);
2231         }
2232 
2233         // let authenticator know the identity of the caller
2234         loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2235         loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
2236 
2237         if (notifyOnAuthFailure) {
2238             loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
2239         }
2240 
2241         long identityToken = clearCallingIdentity();
2242         try {
2243             // Distill the caller's package signatures into a single digest.
2244             final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2245 
2246             // if the caller has permission, do the peek. otherwise go the more expensive
2247             // route of starting a Session
2248             if (!customTokens && permissionGranted) {
2249                 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
2250                 if (authToken != null) {
2251                     Bundle result = new Bundle();
2252                     result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2253                     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2254                     result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2255                     onResult(response, result);
2256                     return;
2257                 }
2258             }
2259 
2260             if (customTokens) {
2261                 /*
2262                  * Look up tokens in the new cache only if the loginOptions don't have parameters
2263                  * outside of those expected to be injected by the AccountManager, e.g.
2264                  * ANDORID_PACKAGE_NAME.
2265                  */
2266                 String token = readCachedTokenInternal(
2267                         accounts,
2268                         account,
2269                         authTokenType,
2270                         callerPkg,
2271                         callerPkgSigDigest);
2272                 if (token != null) {
2273                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2274                         Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2275                     }
2276                     Bundle result = new Bundle();
2277                     result.putString(AccountManager.KEY_AUTHTOKEN, token);
2278                     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2279                     result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2280                     onResult(response, result);
2281                     return;
2282                 }
2283             }
2284 
2285             new Session(
2286                     accounts,
2287                     response,
2288                     account.type,
2289                     expectActivityLaunch,
2290                     false /* stripAuthTokenFromResult */,
2291                     account.name,
2292                     false /* authDetailsRequired */) {
2293                 @Override
2294                 protected String toDebugString(long now) {
2295                     if (loginOptions != null) loginOptions.keySet();
2296                     return super.toDebugString(now) + ", getAuthToken"
2297                             + ", " + account
2298                             + ", authTokenType " + authTokenType
2299                             + ", loginOptions " + loginOptions
2300                             + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2301                 }
2302 
2303                 @Override
2304                 public void run() throws RemoteException {
2305                     // If the caller doesn't have permission then create and return the
2306                     // "grant permission" intent instead of the "getAuthToken" intent.
2307                     if (!permissionGranted) {
2308                         mAuthenticator.getAuthTokenLabel(this, authTokenType);
2309                     } else {
2310                         mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2311                     }
2312                 }
2313 
2314                 @Override
2315                 public void onResult(Bundle result) {
2316                     Bundle.setDefusable(result, true);
2317                     if (result != null) {
2318                         if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
2319                             Intent intent = newGrantCredentialsPermissionIntent(
2320                                     account,
2321                                     callerUid,
2322                                     new AccountAuthenticatorResponse(this),
2323                                     authTokenType);
2324                             Bundle bundle = new Bundle();
2325                             bundle.putParcelable(AccountManager.KEY_INTENT, intent);
2326                             onResult(bundle);
2327                             return;
2328                         }
2329                         String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
2330                         if (authToken != null) {
2331                             String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2332                             String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
2333                             if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
2334                                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
2335                                         "the type and name should not be empty");
2336                                 return;
2337                             }
2338                             Account resultAccount = new Account(name, type);
2339                             if (!customTokens) {
2340                                 saveAuthTokenToDatabase(
2341                                         mAccounts,
2342                                         resultAccount,
2343                                         authTokenType,
2344                                         authToken);
2345                             }
2346                             long expiryMillis = result.getLong(
2347                                     AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2348                             if (customTokens
2349                                     && expiryMillis > System.currentTimeMillis()) {
2350                                 saveCachedToken(
2351                                         mAccounts,
2352                                         account,
2353                                         callerPkg,
2354                                         callerPkgSigDigest,
2355                                         authTokenType,
2356                                         authToken,
2357                                         expiryMillis);
2358                             }
2359                         }
2360 
2361                         Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
2362                         if (intent != null && notifyOnAuthFailure && !customTokens) {
2363                             /*
2364                              * Make sure that the supplied intent is owned by the authenticator
2365                              * giving it to the system. Otherwise a malicious authenticator could
2366                              * have users launching arbitrary activities by tricking users to
2367                              * interact with malicious notifications.
2368                              */
2369                             checkKeyIntent(
2370                                     Binder.getCallingUid(),
2371                                     intent);
2372                             doNotification(mAccounts,
2373                                     account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
2374                                     intent, accounts.userId);
2375                         }
2376                     }
2377                     super.onResult(result);
2378                 }
2379             }.bind();
2380         } finally {
2381             restoreCallingIdentity(identityToken);
2382         }
2383     }
2384 
calculatePackageSignatureDigest(String callerPkg)2385     private byte[] calculatePackageSignatureDigest(String callerPkg) {
2386         MessageDigest digester;
2387         try {
2388             digester = MessageDigest.getInstance("SHA-256");
2389             PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2390                     callerPkg, PackageManager.GET_SIGNATURES);
2391             for (Signature sig : pkgInfo.signatures) {
2392                 digester.update(sig.toByteArray());
2393             }
2394         } catch (NoSuchAlgorithmException x) {
2395             Log.wtf(TAG, "SHA-256 should be available", x);
2396             digester = null;
2397         } catch (NameNotFoundException e) {
2398             Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2399             digester = null;
2400         }
2401         return (digester == null) ? null : digester.digest();
2402     }
2403 
createNoCredentialsPermissionNotification(Account account, Intent intent, int userId)2404     private void createNoCredentialsPermissionNotification(Account account, Intent intent,
2405             int userId) {
2406         int uid = intent.getIntExtra(
2407                 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2408         String authTokenType = intent.getStringExtra(
2409                 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
2410         final String titleAndSubtitle =
2411                 mContext.getString(R.string.permission_request_notification_with_subtitle,
2412                 account.name);
2413         final int index = titleAndSubtitle.indexOf('\n');
2414         String title = titleAndSubtitle;
2415         String subtitle = "";
2416         if (index > 0) {
2417             title = titleAndSubtitle.substring(0, index);
2418             subtitle = titleAndSubtitle.substring(index + 1);
2419         }
2420         UserHandle user = new UserHandle(userId);
2421         Context contextForUser = getContextForUser(user);
2422         Notification n = new Notification.Builder(contextForUser)
2423                 .setSmallIcon(android.R.drawable.stat_sys_warning)
2424                 .setWhen(0)
2425                 .setColor(contextForUser.getColor(
2426                         com.android.internal.R.color.system_notification_accent_color))
2427                 .setContentTitle(title)
2428                 .setContentText(subtitle)
2429                 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2430                         PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2431                 .build();
2432         installNotification(getCredentialPermissionNotificationId(
2433                 account, authTokenType, uid), n, user);
2434     }
2435 
newGrantCredentialsPermissionIntent(Account account, int uid, AccountAuthenticatorResponse response, String authTokenType)2436     private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
2437             AccountAuthenticatorResponse response, String authTokenType) {
2438 
2439         Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
2440         // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
2441         // Since it was set in Eclair+ we can't change it without breaking apps using
2442         // the intent from a non-Activity context.
2443         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2444         intent.addCategory(
2445                 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
2446 
2447         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
2448         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2449         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
2450         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
2451 
2452         return intent;
2453     }
2454 
getCredentialPermissionNotificationId(Account account, String authTokenType, int uid)2455     private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2456             int uid) {
2457         Integer id;
2458         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
2459         synchronized (accounts.credentialsPermissionNotificationIds) {
2460             final Pair<Pair<Account, String>, Integer> key =
2461                     new Pair<Pair<Account, String>, Integer>(
2462                             new Pair<Account, String>(account, authTokenType), uid);
2463             id = accounts.credentialsPermissionNotificationIds.get(key);
2464             if (id == null) {
2465                 id = mNotificationIds.incrementAndGet();
2466                 accounts.credentialsPermissionNotificationIds.put(key, id);
2467             }
2468         }
2469         return id;
2470     }
2471 
getSigninRequiredNotificationId(UserAccounts accounts, Account account)2472     private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
2473         Integer id;
2474         synchronized (accounts.signinRequiredNotificationIds) {
2475             id = accounts.signinRequiredNotificationIds.get(account);
2476             if (id == null) {
2477                 id = mNotificationIds.incrementAndGet();
2478                 accounts.signinRequiredNotificationIds.put(account, id);
2479             }
2480         }
2481         return id;
2482     }
2483 
2484     @Override
addAccount(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn)2485     public void addAccount(final IAccountManagerResponse response, final String accountType,
2486             final String authTokenType, final String[] requiredFeatures,
2487             final boolean expectActivityLaunch, final Bundle optionsIn) {
2488         Bundle.setDefusable(optionsIn, true);
2489         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2490             Log.v(TAG, "addAccount: accountType " + accountType
2491                     + ", response " + response
2492                     + ", authTokenType " + authTokenType
2493                     + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2494                     + ", expectActivityLaunch " + expectActivityLaunch
2495                     + ", caller's uid " + Binder.getCallingUid()
2496                     + ", pid " + Binder.getCallingPid());
2497         }
2498         if (response == null) throw new IllegalArgumentException("response is null");
2499         if (accountType == null) throw new IllegalArgumentException("accountType is null");
2500 
2501         // Is user disallowed from modifying accounts?
2502         final int uid = Binder.getCallingUid();
2503         final int userId = UserHandle.getUserId(uid);
2504         if (!canUserModifyAccounts(userId, uid)) {
2505             try {
2506                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2507                         "User is not allowed to add an account!");
2508             } catch (RemoteException re) {
2509             }
2510             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2511             return;
2512         }
2513         if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2514             try {
2515                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2516                         "User cannot modify accounts of this type (policy).");
2517             } catch (RemoteException re) {
2518             }
2519             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2520                     userId);
2521             return;
2522         }
2523 
2524         final int pid = Binder.getCallingPid();
2525         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2526         options.putInt(AccountManager.KEY_CALLER_UID, uid);
2527         options.putInt(AccountManager.KEY_CALLER_PID, pid);
2528 
2529         int usrId = UserHandle.getCallingUserId();
2530         long identityToken = clearCallingIdentity();
2531         try {
2532             UserAccounts accounts = getUserAccounts(usrId);
2533             logRecordWithUid(
2534                     accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
2535             new Session(accounts, response, accountType, expectActivityLaunch,
2536                     true /* stripAuthTokenFromResult */, null /* accountName */,
2537                     false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2538                 @Override
2539                 public void run() throws RemoteException {
2540                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2541                             options);
2542                 }
2543 
2544                 @Override
2545                 protected String toDebugString(long now) {
2546                     return super.toDebugString(now) + ", addAccount"
2547                             + ", accountType " + accountType
2548                             + ", requiredFeatures "
2549                             + (requiredFeatures != null
2550                               ? TextUtils.join(",", requiredFeatures)
2551                               : null);
2552                 }
2553             }.bind();
2554         } finally {
2555             restoreCallingIdentity(identityToken);
2556         }
2557     }
2558 
2559     @Override
addAccountAsUser(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn, int userId)2560     public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2561             final String authTokenType, final String[] requiredFeatures,
2562             final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
2563         Bundle.setDefusable(optionsIn, true);
2564         int callingUid = Binder.getCallingUid();
2565         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2566             Log.v(TAG, "addAccount: accountType " + accountType
2567                     + ", response " + response
2568                     + ", authTokenType " + authTokenType
2569                     + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2570                     + ", expectActivityLaunch " + expectActivityLaunch
2571                     + ", caller's uid " + Binder.getCallingUid()
2572                     + ", pid " + Binder.getCallingPid()
2573                     + ", for user id " + userId);
2574         }
2575         if (response == null) throw new IllegalArgumentException("response is null");
2576         if (accountType == null) throw new IllegalArgumentException("accountType is null");
2577         // Only allow the system process to add accounts of other users
2578         if (isCrossUser(callingUid, userId)) {
2579             throw new SecurityException(
2580                     String.format(
2581                             "User %s trying to add account for %s" ,
2582                             UserHandle.getCallingUserId(),
2583                             userId));
2584         }
2585 
2586         // Is user disallowed from modifying accounts?
2587         if (!canUserModifyAccounts(userId, callingUid)) {
2588             try {
2589                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2590                         "User is not allowed to add an account!");
2591             } catch (RemoteException re) {
2592             }
2593             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2594             return;
2595         }
2596         if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
2597             try {
2598                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2599                         "User cannot modify accounts of this type (policy).");
2600             } catch (RemoteException re) {
2601             }
2602             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2603                     userId);
2604             return;
2605         }
2606 
2607         final int pid = Binder.getCallingPid();
2608         final int uid = Binder.getCallingUid();
2609         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2610         options.putInt(AccountManager.KEY_CALLER_UID, uid);
2611         options.putInt(AccountManager.KEY_CALLER_PID, pid);
2612 
2613         long identityToken = clearCallingIdentity();
2614         try {
2615             UserAccounts accounts = getUserAccounts(userId);
2616             logRecordWithUid(
2617                     accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
2618             new Session(accounts, response, accountType, expectActivityLaunch,
2619                     true /* stripAuthTokenFromResult */, null /* accountName */,
2620                     false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2621                 @Override
2622                 public void run() throws RemoteException {
2623                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2624                             options);
2625                 }
2626 
2627                 @Override
2628                 protected String toDebugString(long now) {
2629                     return super.toDebugString(now) + ", addAccount"
2630                             + ", accountType " + accountType
2631                             + ", requiredFeatures "
2632                             + (requiredFeatures != null
2633                               ? TextUtils.join(",", requiredFeatures)
2634                               : null);
2635                 }
2636             }.bind();
2637         } finally {
2638             restoreCallingIdentity(identityToken);
2639         }
2640     }
2641 
2642     @Override
startAddAccountSession( final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn)2643     public void startAddAccountSession(
2644             final IAccountManagerResponse response,
2645             final String accountType,
2646             final String authTokenType,
2647             final String[] requiredFeatures,
2648             final boolean expectActivityLaunch,
2649             final Bundle optionsIn) {
2650         Bundle.setDefusable(optionsIn, true);
2651         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2652             Log.v(TAG,
2653                     "startAddAccountSession: accountType " + accountType
2654                     + ", response " + response
2655                     + ", authTokenType " + authTokenType
2656                     + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2657                     + ", expectActivityLaunch " + expectActivityLaunch
2658                     + ", caller's uid " + Binder.getCallingUid()
2659                     + ", pid " + Binder.getCallingPid());
2660         }
2661         if (response == null) {
2662             throw new IllegalArgumentException("response is null");
2663         }
2664         if (accountType == null) {
2665             throw new IllegalArgumentException("accountType is null");
2666         }
2667 
2668         final int uid = Binder.getCallingUid();
2669         // Only allow system to start session
2670         if (!isSystemUid(uid)) {
2671             String msg = String.format(
2672                     "uid %s cannot stat add account session.",
2673                     uid);
2674             throw new SecurityException(msg);
2675         }
2676 
2677         final int userId = UserHandle.getUserId(uid);
2678         if (!canUserModifyAccounts(userId, uid)) {
2679             try {
2680                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2681                         "User is not allowed to add an account!");
2682             } catch (RemoteException re) {
2683             }
2684             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2685             return;
2686         }
2687         if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2688             try {
2689                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2690                         "User cannot modify accounts of this type (policy).");
2691             } catch (RemoteException re) {
2692             }
2693             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2694                     userId);
2695             return;
2696         }
2697         final int pid = Binder.getCallingPid();
2698         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2699         options.putInt(AccountManager.KEY_CALLER_UID, uid);
2700         options.putInt(AccountManager.KEY_CALLER_PID, pid);
2701 
2702         // Check to see if the Password should be included to the caller.
2703         String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
2704         boolean isPasswordForwardingAllowed = isPermitted(
2705                 callerPkg, uid, Manifest.permission.GET_PASSWORD);
2706 
2707         long identityToken = clearCallingIdentity();
2708         try {
2709             UserAccounts accounts = getUserAccounts(userId);
2710             logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2711                     TABLE_ACCOUNTS, uid);
2712             new StartAccountSession(
2713                     accounts,
2714                     response,
2715                     accountType,
2716                     expectActivityLaunch,
2717                     null /* accountName */,
2718                     false /* authDetailsRequired */,
2719                     true /* updateLastAuthenticationTime */,
2720                     isPasswordForwardingAllowed) {
2721                 @Override
2722                 public void run() throws RemoteException {
2723                     mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2724                             requiredFeatures, options);
2725                 }
2726 
2727                 @Override
2728                 protected String toDebugString(long now) {
2729                     String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2730                     return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2731                             + accountType + ", requiredFeatures "
2732                             + (requiredFeatures != null ? requiredFeaturesStr : null);
2733                 }
2734             }.bind();
2735         } finally {
2736             restoreCallingIdentity(identityToken);
2737         }
2738     }
2739 
2740     /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2741     private abstract class StartAccountSession extends Session {
2742 
2743         private final boolean mIsPasswordForwardingAllowed;
2744 
StartAccountSession( UserAccounts accounts, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch, String accountName, boolean authDetailsRequired, boolean updateLastAuthenticationTime, boolean isPasswordForwardingAllowed)2745         public StartAccountSession(
2746                 UserAccounts accounts,
2747                 IAccountManagerResponse response,
2748                 String accountType,
2749                 boolean expectActivityLaunch,
2750                 String accountName,
2751                 boolean authDetailsRequired,
2752                 boolean updateLastAuthenticationTime,
2753                 boolean isPasswordForwardingAllowed) {
2754             super(accounts, response, accountType, expectActivityLaunch,
2755                     true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2756                     updateLastAuthenticationTime);
2757             mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
2758         }
2759 
2760         @Override
onResult(Bundle result)2761         public void onResult(Bundle result) {
2762             Bundle.setDefusable(result, true);
2763             mNumResults++;
2764             Intent intent = null;
2765             if (result != null
2766                     && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2767                 checkKeyIntent(
2768                         Binder.getCallingUid(),
2769                         intent);
2770             }
2771             IAccountManagerResponse response;
2772             if (mExpectActivityLaunch && result != null
2773                     && result.containsKey(AccountManager.KEY_INTENT)) {
2774                 response = mResponse;
2775             } else {
2776                 response = getResponseAndClose();
2777             }
2778             if (response == null) {
2779                 return;
2780             }
2781             if (result == null) {
2782                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2783                     Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2784                             + response);
2785                 }
2786                 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2787                         "null bundle returned");
2788                 return;
2789             }
2790 
2791             if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2792                 // All AccountManager error codes are greater
2793                 // than 0
2794                 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2795                         result.getString(AccountManager.KEY_ERROR_MESSAGE));
2796                 return;
2797             }
2798 
2799             // Omit passwords if the caller isn't permitted to see them.
2800             if (!mIsPasswordForwardingAllowed) {
2801                 result.remove(AccountManager.KEY_PASSWORD);
2802             }
2803 
2804             // Strip auth token from result.
2805             result.remove(AccountManager.KEY_AUTHTOKEN);
2806 
2807             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2808                 Log.v(TAG,
2809                         getClass().getSimpleName() + " calling onResult() on response " + response);
2810             }
2811 
2812             // Get the session bundle created by authenticator. The
2813             // bundle contains data necessary for finishing the session
2814             // later. The session bundle will be encrypted here and
2815             // decrypted later when trying to finish the session.
2816             Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
2817             if (sessionBundle != null) {
2818                 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2819                 if (TextUtils.isEmpty(accountType)
2820                         || !mAccountType.equalsIgnoreCase(accountType)) {
2821                     Log.w(TAG, "Account type in session bundle doesn't match request.");
2822                 }
2823                 // Add accountType info to session bundle. This will
2824                 // override any value set by authenticator.
2825                 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
2826 
2827                 // Encrypt session bundle before returning to caller.
2828                 try {
2829                     CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2830                     Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
2831                     result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
2832                 } catch (GeneralSecurityException e) {
2833                     if (Log.isLoggable(TAG, Log.DEBUG)) {
2834                         Log.v(TAG, "Failed to encrypt session bundle!", e);
2835                     }
2836                     sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2837                             "failed to encrypt session bundle");
2838                     return;
2839                 }
2840             }
2841 
2842             sendResponse(response, result);
2843         }
2844     }
2845 
2846     @Override
finishSessionAsUser(IAccountManagerResponse response, @NonNull Bundle sessionBundle, boolean expectActivityLaunch, Bundle appInfo, int userId)2847     public void finishSessionAsUser(IAccountManagerResponse response,
2848             @NonNull Bundle sessionBundle,
2849             boolean expectActivityLaunch,
2850             Bundle appInfo,
2851             int userId) {
2852         Bundle.setDefusable(sessionBundle, true);
2853         int callingUid = Binder.getCallingUid();
2854         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2855             Log.v(TAG,
2856                     "finishSession: response "+ response
2857                             + ", expectActivityLaunch " + expectActivityLaunch
2858                             + ", caller's uid " + callingUid
2859                             + ", caller's user id " + UserHandle.getCallingUserId()
2860                             + ", pid " + Binder.getCallingPid()
2861                             + ", for user id " + userId);
2862         }
2863         if (response == null) {
2864             throw new IllegalArgumentException("response is null");
2865         }
2866 
2867         // Session bundle is the encrypted bundle of the original bundle created by authenticator.
2868         // Account type is added to it before encryption.
2869         if (sessionBundle == null || sessionBundle.size() == 0) {
2870             throw new IllegalArgumentException("sessionBundle is empty");
2871         }
2872 
2873         // Only allow the system process to finish session for other users
2874         if (isCrossUser(callingUid, userId)) {
2875             throw new SecurityException(
2876                     String.format(
2877                             "User %s trying to finish session for %s without cross user permission",
2878                             UserHandle.getCallingUserId(),
2879                             userId));
2880         }
2881 
2882         // Only allow system to finish session
2883         if (!isSystemUid(callingUid)) {
2884             String msg = String.format(
2885                     "uid %s cannot finish session because it's not system uid.",
2886                     callingUid);
2887             throw new SecurityException(msg);
2888         }
2889 
2890         if (!canUserModifyAccounts(userId, callingUid)) {
2891             sendErrorResponse(response,
2892                     AccountManager.ERROR_CODE_USER_RESTRICTED,
2893                     "User is not allowed to add an account!");
2894             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2895             return;
2896         }
2897 
2898         final int pid = Binder.getCallingPid();
2899         final Bundle decryptedBundle;
2900         final String accountType;
2901         // First decrypt session bundle to get account type for checking permission.
2902         try {
2903             CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2904             decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
2905             if (decryptedBundle == null) {
2906                 sendErrorResponse(
2907                         response,
2908                         AccountManager.ERROR_CODE_BAD_REQUEST,
2909                         "failed to decrypt session bundle");
2910                 return;
2911             }
2912             accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2913             // Account type cannot be null. This should not happen if session bundle was created
2914             // properly by #StartAccountSession.
2915             if (TextUtils.isEmpty(accountType)) {
2916                 sendErrorResponse(
2917                         response,
2918                         AccountManager.ERROR_CODE_BAD_ARGUMENTS,
2919                         "accountType is empty");
2920                 return;
2921             }
2922 
2923             // If by any chances, decryptedBundle contains colliding keys with
2924             // system info
2925             // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
2926             // update credentials flow, we should replace with the new values of the current call.
2927             if (appInfo != null) {
2928                 decryptedBundle.putAll(appInfo);
2929             }
2930 
2931             // Add info that may be used by add account or update credentials flow.
2932             decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
2933             decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
2934         } catch (GeneralSecurityException e) {
2935             if (Log.isLoggable(TAG, Log.DEBUG)) {
2936                 Log.v(TAG, "Failed to decrypt session bundle!", e);
2937             }
2938             sendErrorResponse(
2939                     response,
2940                     AccountManager.ERROR_CODE_BAD_REQUEST,
2941                     "failed to decrypt session bundle");
2942             return;
2943         }
2944 
2945         if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
2946             sendErrorResponse(
2947                     response,
2948                     AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2949                     "User cannot modify accounts of this type (policy).");
2950             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2951                     userId);
2952             return;
2953         }
2954 
2955         long identityToken = clearCallingIdentity();
2956         try {
2957             UserAccounts accounts = getUserAccounts(userId);
2958             logRecordWithUid(
2959                     accounts,
2960                     DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
2961                     TABLE_ACCOUNTS,
2962                     callingUid);
2963             new Session(
2964                     accounts,
2965                     response,
2966                     accountType,
2967                     expectActivityLaunch,
2968                     true /* stripAuthTokenFromResult */,
2969                     null /* accountName */,
2970                     false /* authDetailsRequired */,
2971                     true /* updateLastAuthenticationTime */) {
2972                 @Override
2973                 public void run() throws RemoteException {
2974                     mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
2975                 }
2976 
2977                 @Override
2978                 protected String toDebugString(long now) {
2979                     return super.toDebugString(now)
2980                             + ", finishSession"
2981                             + ", accountType " + accountType;
2982                 }
2983             }.bind();
2984         } finally {
2985             restoreCallingIdentity(identityToken);
2986         }
2987     }
2988 
showCantAddAccount(int errorCode, int userId)2989     private void showCantAddAccount(int errorCode, int userId) {
2990         Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2991         cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2992         cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2993         long identityToken = clearCallingIdentity();
2994         try {
2995             mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
2996         } finally {
2997             restoreCallingIdentity(identityToken);
2998         }
2999     }
3000 
3001     @Override
confirmCredentialsAsUser( IAccountManagerResponse response, final Account account, final Bundle options, final boolean expectActivityLaunch, int userId)3002     public void confirmCredentialsAsUser(
3003             IAccountManagerResponse response,
3004             final Account account,
3005             final Bundle options,
3006             final boolean expectActivityLaunch,
3007             int userId) {
3008         Bundle.setDefusable(options, true);
3009         int callingUid = Binder.getCallingUid();
3010         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3011             Log.v(TAG, "confirmCredentials: " + account
3012                     + ", response " + response
3013                     + ", expectActivityLaunch " + expectActivityLaunch
3014                     + ", caller's uid " + callingUid
3015                     + ", pid " + Binder.getCallingPid());
3016         }
3017         // Only allow the system process to read accounts of other users
3018         if (isCrossUser(callingUid, userId)) {
3019             throw new SecurityException(
3020                     String.format(
3021                             "User %s trying to confirm account credentials for %s" ,
3022                             UserHandle.getCallingUserId(),
3023                             userId));
3024         }
3025         if (response == null) throw new IllegalArgumentException("response is null");
3026         if (account == null) throw new IllegalArgumentException("account is null");
3027         long identityToken = clearCallingIdentity();
3028         try {
3029             UserAccounts accounts = getUserAccounts(userId);
3030             new Session(accounts, response, account.type, expectActivityLaunch,
3031                     true /* stripAuthTokenFromResult */, account.name,
3032                     true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
3033                 @Override
3034                 public void run() throws RemoteException {
3035                     mAuthenticator.confirmCredentials(this, account, options);
3036                 }
3037                 @Override
3038                 protected String toDebugString(long now) {
3039                     return super.toDebugString(now) + ", confirmCredentials"
3040                             + ", " + account;
3041                 }
3042             }.bind();
3043         } finally {
3044             restoreCallingIdentity(identityToken);
3045         }
3046     }
3047 
3048     @Override
updateCredentials(IAccountManagerResponse response, final Account account, final String authTokenType, final boolean expectActivityLaunch, final Bundle loginOptions)3049     public void updateCredentials(IAccountManagerResponse response, final Account account,
3050             final String authTokenType, final boolean expectActivityLaunch,
3051             final Bundle loginOptions) {
3052         Bundle.setDefusable(loginOptions, true);
3053         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3054             Log.v(TAG, "updateCredentials: " + account
3055                     + ", response " + response
3056                     + ", authTokenType " + authTokenType
3057                     + ", expectActivityLaunch " + expectActivityLaunch
3058                     + ", caller's uid " + Binder.getCallingUid()
3059                     + ", pid " + Binder.getCallingPid());
3060         }
3061         if (response == null) throw new IllegalArgumentException("response is null");
3062         if (account == null) throw new IllegalArgumentException("account is null");
3063         int userId = UserHandle.getCallingUserId();
3064         long identityToken = clearCallingIdentity();
3065         try {
3066             UserAccounts accounts = getUserAccounts(userId);
3067             new Session(accounts, response, account.type, expectActivityLaunch,
3068                     true /* stripAuthTokenFromResult */, account.name,
3069                     false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
3070                 @Override
3071                 public void run() throws RemoteException {
3072                     mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3073                 }
3074                 @Override
3075                 protected String toDebugString(long now) {
3076                     if (loginOptions != null) loginOptions.keySet();
3077                     return super.toDebugString(now) + ", updateCredentials"
3078                             + ", " + account
3079                             + ", authTokenType " + authTokenType
3080                             + ", loginOptions " + loginOptions;
3081                 }
3082             }.bind();
3083         } finally {
3084             restoreCallingIdentity(identityToken);
3085         }
3086     }
3087 
3088     @Override
startUpdateCredentialsSession( IAccountManagerResponse response, final Account account, final String authTokenType, final boolean expectActivityLaunch, final Bundle loginOptions)3089     public void startUpdateCredentialsSession(
3090             IAccountManagerResponse response,
3091             final Account account,
3092             final String authTokenType,
3093             final boolean expectActivityLaunch,
3094             final Bundle loginOptions) {
3095         Bundle.setDefusable(loginOptions, true);
3096         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3097             Log.v(TAG,
3098                     "startUpdateCredentialsSession: " + account + ", response " + response
3099                             + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3100                             + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3101                             + ", pid " + Binder.getCallingPid());
3102         }
3103         if (response == null) {
3104             throw new IllegalArgumentException("response is null");
3105         }
3106         if (account == null) {
3107             throw new IllegalArgumentException("account is null");
3108         }
3109 
3110         final int uid = Binder.getCallingUid();
3111         // Only allow system to start session
3112         if (!isSystemUid(uid)) {
3113             String msg = String.format(
3114                     "uid %s cannot start update credentials session.",
3115                     uid);
3116             throw new SecurityException(msg);
3117         }
3118 
3119         int userId = UserHandle.getCallingUserId();
3120 
3121         // Check to see if the Password should be included to the caller.
3122         String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3123         boolean isPasswordForwardingAllowed = isPermitted(
3124                 callerPkg, uid, Manifest.permission.GET_PASSWORD);
3125 
3126         long identityToken = clearCallingIdentity();
3127         try {
3128             UserAccounts accounts = getUserAccounts(userId);
3129             new StartAccountSession(
3130                     accounts,
3131                     response,
3132                     account.type,
3133                     expectActivityLaunch,
3134                     account.name,
3135                     false /* authDetailsRequired */,
3136                     true /* updateLastCredentialTime */,
3137                     isPasswordForwardingAllowed) {
3138                 @Override
3139                 public void run() throws RemoteException {
3140                     mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3141                             loginOptions);
3142                 }
3143 
3144                 @Override
3145                 protected String toDebugString(long now) {
3146                     if (loginOptions != null)
3147                         loginOptions.keySet();
3148                     return super.toDebugString(now)
3149                             + ", startUpdateCredentialsSession"
3150                             + ", " + account
3151                             + ", authTokenType " + authTokenType
3152                             + ", loginOptions " + loginOptions;
3153                 }
3154             }.bind();
3155         } finally {
3156             restoreCallingIdentity(identityToken);
3157         }
3158     }
3159 
3160     @Override
isCredentialsUpdateSuggested( IAccountManagerResponse response, final Account account, final String statusToken)3161     public void isCredentialsUpdateSuggested(
3162             IAccountManagerResponse response,
3163             final Account account,
3164             final String statusToken) {
3165         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3166             Log.v(TAG,
3167                     "isCredentialsUpdateSuggested: " + account + ", response " + response
3168                             + ", caller's uid " + Binder.getCallingUid()
3169                             + ", pid " + Binder.getCallingPid());
3170         }
3171         if (response == null) {
3172             throw new IllegalArgumentException("response is null");
3173         }
3174         if (account == null) {
3175             throw new IllegalArgumentException("account is null");
3176         }
3177         if (TextUtils.isEmpty(statusToken)) {
3178             throw new IllegalArgumentException("status token is empty");
3179         }
3180 
3181         int uid = Binder.getCallingUid();
3182         // Only allow system to start session
3183         if (!isSystemUid(uid)) {
3184             String msg = String.format(
3185                     "uid %s cannot stat add account session.",
3186                     uid);
3187             throw new SecurityException(msg);
3188         }
3189 
3190         int usrId = UserHandle.getCallingUserId();
3191         long identityToken = clearCallingIdentity();
3192         try {
3193             UserAccounts accounts = getUserAccounts(usrId);
3194             new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3195                     false /* stripAuthTokenFromResult */, account.name,
3196                     false /* authDetailsRequired */) {
3197                 @Override
3198                 protected String toDebugString(long now) {
3199                     return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3200                             + ", " + account;
3201                 }
3202 
3203                 @Override
3204                 public void run() throws RemoteException {
3205                     mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3206                 }
3207 
3208                 @Override
3209                 public void onResult(Bundle result) {
3210                     Bundle.setDefusable(result, true);
3211                     IAccountManagerResponse response = getResponseAndClose();
3212                     if (response == null) {
3213                         return;
3214                     }
3215 
3216                     if (result == null) {
3217                         sendErrorResponse(
3218                                 response,
3219                                 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3220                                 "null bundle");
3221                         return;
3222                     }
3223 
3224                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
3225                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3226                                 + response);
3227                     }
3228                     // Check to see if an error occurred. We know if an error occurred because all
3229                     // error codes are greater than 0.
3230                     if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3231                         sendErrorResponse(response,
3232                                 result.getInt(AccountManager.KEY_ERROR_CODE),
3233                                 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3234                         return;
3235                     }
3236                     if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3237                         sendErrorResponse(
3238                                 response,
3239                                 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3240                                 "no result in response");
3241                         return;
3242                     }
3243                     final Bundle newResult = new Bundle();
3244                     newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3245                             result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3246                     sendResponse(response, newResult);
3247                 }
3248             }.bind();
3249         } finally {
3250             restoreCallingIdentity(identityToken);
3251         }
3252     }
3253 
3254     @Override
editProperties(IAccountManagerResponse response, final String accountType, final boolean expectActivityLaunch)3255     public void editProperties(IAccountManagerResponse response, final String accountType,
3256             final boolean expectActivityLaunch) {
3257         final int callingUid = Binder.getCallingUid();
3258         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3259             Log.v(TAG, "editProperties: accountType " + accountType
3260                     + ", response " + response
3261                     + ", expectActivityLaunch " + expectActivityLaunch
3262                     + ", caller's uid " + callingUid
3263                     + ", pid " + Binder.getCallingPid());
3264         }
3265         if (response == null) throw new IllegalArgumentException("response is null");
3266         if (accountType == null) throw new IllegalArgumentException("accountType is null");
3267         int userId = UserHandle.getCallingUserId();
3268         if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
3269             String msg = String.format(
3270                     "uid %s cannot edit authenticator properites for account type: %s",
3271                     callingUid,
3272                     accountType);
3273             throw new SecurityException(msg);
3274         }
3275         long identityToken = clearCallingIdentity();
3276         try {
3277             UserAccounts accounts = getUserAccounts(userId);
3278             new Session(accounts, response, accountType, expectActivityLaunch,
3279                     true /* stripAuthTokenFromResult */, null /* accountName */,
3280                     false /* authDetailsRequired */) {
3281                 @Override
3282                 public void run() throws RemoteException {
3283                     mAuthenticator.editProperties(this, mAccountType);
3284                 }
3285                 @Override
3286                 protected String toDebugString(long now) {
3287                     return super.toDebugString(now) + ", editProperties"
3288                             + ", accountType " + accountType;
3289                 }
3290             }.bind();
3291         } finally {
3292             restoreCallingIdentity(identityToken);
3293         }
3294     }
3295 
3296     @Override
someUserHasAccount(@onNull final Account account)3297     public boolean someUserHasAccount(@NonNull final Account account) {
3298         if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3299             throw new SecurityException("Only system can check for accounts across users");
3300         }
3301         final long token = Binder.clearCallingIdentity();
3302         try {
3303             AccountAndUser[] allAccounts = getAllAccounts();
3304             for (int i = allAccounts.length - 1; i >= 0; i--) {
3305                 if (allAccounts[i].account.equals(account)) {
3306                     return true;
3307                 }
3308             }
3309             return false;
3310         } finally {
3311             Binder.restoreCallingIdentity(token);
3312         }
3313     }
3314 
3315     private class GetAccountsByTypeAndFeatureSession extends Session {
3316         private final String[] mFeatures;
3317         private volatile Account[] mAccountsOfType = null;
3318         private volatile ArrayList<Account> mAccountsWithFeatures = null;
3319         private volatile int mCurrentAccount = 0;
3320         private final int mCallingUid;
3321 
GetAccountsByTypeAndFeatureSession(UserAccounts accounts, IAccountManagerResponse response, String type, String[] features, int callingUid)3322         public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
3323                 IAccountManagerResponse response, String type, String[] features, int callingUid) {
3324             super(accounts, response, type, false /* expectActivityLaunch */,
3325                     true /* stripAuthTokenFromResult */, null /* accountName */,
3326                     false /* authDetailsRequired */);
3327             mCallingUid = callingUid;
3328             mFeatures = features;
3329         }
3330 
3331         @Override
run()3332         public void run() throws RemoteException {
3333             synchronized (mAccounts.cacheLock) {
3334                 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3335                         null);
3336             }
3337             // check whether each account matches the requested features
3338             mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
3339             mCurrentAccount = 0;
3340 
3341             checkAccount();
3342         }
3343 
checkAccount()3344         public void checkAccount() {
3345             if (mCurrentAccount >= mAccountsOfType.length) {
3346                 sendResult();
3347                 return;
3348             }
3349 
3350             final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3351             if (accountAuthenticator == null) {
3352                 // It is possible that the authenticator has died, which is indicated by
3353                 // mAuthenticator being set to null. If this happens then just abort.
3354                 // There is no need to send back a result or error in this case since
3355                 // that already happened when mAuthenticator was cleared.
3356                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3357                     Log.v(TAG, "checkAccount: aborting session since we are no longer"
3358                             + " connected to the authenticator, " + toDebugString());
3359                 }
3360                 return;
3361             }
3362             try {
3363                 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
3364             } catch (RemoteException e) {
3365                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
3366             }
3367         }
3368 
3369         @Override
onResult(Bundle result)3370         public void onResult(Bundle result) {
3371             Bundle.setDefusable(result, true);
3372             mNumResults++;
3373             if (result == null) {
3374                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
3375                 return;
3376             }
3377             if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
3378                 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3379             }
3380             mCurrentAccount++;
3381             checkAccount();
3382         }
3383 
sendResult()3384         public void sendResult() {
3385             IAccountManagerResponse response = getResponseAndClose();
3386             if (response != null) {
3387                 try {
3388                     Account[] accounts = new Account[mAccountsWithFeatures.size()];
3389                     for (int i = 0; i < accounts.length; i++) {
3390                         accounts[i] = mAccountsWithFeatures.get(i);
3391                     }
3392                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
3393                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3394                                 + response);
3395                     }
3396                     Bundle result = new Bundle();
3397                     result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3398                     response.onResult(result);
3399                 } catch (RemoteException e) {
3400                     // if the caller is dead then there is no one to care about remote exceptions
3401                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
3402                         Log.v(TAG, "failure while notifying response", e);
3403                     }
3404                 }
3405             }
3406         }
3407 
3408 
3409         @Override
toDebugString(long now)3410         protected String toDebugString(long now) {
3411             return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3412                     + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3413         }
3414     }
3415 
3416     /**
3417      * Returns the accounts visible to the client within the context of a specific user
3418      * @hide
3419      */
3420     @NonNull
getAccounts(int userId, String opPackageName)3421     public Account[] getAccounts(int userId, String opPackageName) {
3422         int callingUid = Binder.getCallingUid();
3423         List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3424                 opPackageName);
3425         if (visibleAccountTypes.isEmpty()) {
3426             return new Account[0];
3427         }
3428         long identityToken = clearCallingIdentity();
3429         try {
3430             UserAccounts accounts = getUserAccounts(userId);
3431             return getAccountsInternal(
3432                     accounts,
3433                     callingUid,
3434                     null,  // packageName
3435                     visibleAccountTypes);
3436         } finally {
3437             restoreCallingIdentity(identityToken);
3438         }
3439     }
3440 
3441     /**
3442      * Returns accounts for all running users.
3443      *
3444      * @hide
3445      */
3446     @NonNull
getRunningAccounts()3447     public AccountAndUser[] getRunningAccounts() {
3448         final int[] runningUserIds;
3449         try {
3450             runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3451         } catch (RemoteException e) {
3452             // Running in system_server; should never happen
3453             throw new RuntimeException(e);
3454         }
3455         return getAccounts(runningUserIds);
3456     }
3457 
3458     /** {@hide} */
3459     @NonNull
getAllAccounts()3460     public AccountAndUser[] getAllAccounts() {
3461         final List<UserInfo> users = getUserManager().getUsers(true);
3462         final int[] userIds = new int[users.size()];
3463         for (int i = 0; i < userIds.length; i++) {
3464             userIds[i] = users.get(i).id;
3465         }
3466         return getAccounts(userIds);
3467     }
3468 
3469     @NonNull
getAccounts(int[] userIds)3470     private AccountAndUser[] getAccounts(int[] userIds) {
3471         final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
3472         for (int userId : userIds) {
3473             UserAccounts userAccounts = getUserAccounts(userId);
3474             if (userAccounts == null) continue;
3475             synchronized (userAccounts.cacheLock) {
3476                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3477                         Binder.getCallingUid(), null);
3478                 for (int a = 0; a < accounts.length; a++) {
3479                     runningAccounts.add(new AccountAndUser(accounts[a], userId));
3480                 }
3481             }
3482         }
3483 
3484         AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3485         return runningAccounts.toArray(accountsArray);
3486     }
3487 
3488     @Override
3489     @NonNull
getAccountsAsUser(String type, int userId, String opPackageName)3490     public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3491         return getAccountsAsUser(type, userId, null, -1, opPackageName);
3492     }
3493 
3494     @NonNull
getAccountsAsUser( String type, int userId, String callingPackage, int packageUid, String opPackageName)3495     private Account[] getAccountsAsUser(
3496             String type,
3497             int userId,
3498             String callingPackage,
3499             int packageUid,
3500             String opPackageName) {
3501         int callingUid = Binder.getCallingUid();
3502         // Only allow the system process to read accounts of other users
3503         if (userId != UserHandle.getCallingUserId()
3504                 && callingUid != Process.myUid()
3505                 && mContext.checkCallingOrSelfPermission(
3506                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3507                     != PackageManager.PERMISSION_GRANTED) {
3508             throw new SecurityException("User " + UserHandle.getCallingUserId()
3509                     + " trying to get account for " + userId);
3510         }
3511 
3512         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3513             Log.v(TAG, "getAccounts: accountType " + type
3514                     + ", caller's uid " + Binder.getCallingUid()
3515                     + ", pid " + Binder.getCallingPid());
3516         }
3517         // If the original calling app was using the framework account chooser activity, we'll
3518         // be passed in the original caller's uid here, which is what should be used for filtering.
3519         if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3520             callingUid = packageUid;
3521             opPackageName = callingPackage;
3522         }
3523 
3524         List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3525                 opPackageName);
3526         if (visibleAccountTypes.isEmpty()
3527                 || (type != null && !visibleAccountTypes.contains(type))) {
3528             return new Account[0];
3529         } else if (visibleAccountTypes.contains(type)) {
3530             // Prune the list down to just the requested type.
3531             visibleAccountTypes = new ArrayList<>();
3532             visibleAccountTypes.add(type);
3533         } // else aggregate all the visible accounts (it won't matter if the
3534           // list is empty).
3535 
3536         long identityToken = clearCallingIdentity();
3537         try {
3538             UserAccounts accounts = getUserAccounts(userId);
3539             return getAccountsInternal(
3540                     accounts,
3541                     callingUid,
3542                     callingPackage,
3543                     visibleAccountTypes);
3544         } finally {
3545             restoreCallingIdentity(identityToken);
3546         }
3547     }
3548 
3549     @NonNull
getAccountsInternal( UserAccounts userAccounts, int callingUid, String callingPackage, List<String> visibleAccountTypes)3550     private Account[] getAccountsInternal(
3551             UserAccounts userAccounts,
3552             int callingUid,
3553             String callingPackage,
3554             List<String> visibleAccountTypes) {
3555         synchronized (userAccounts.cacheLock) {
3556             ArrayList<Account> visibleAccounts = new ArrayList<>();
3557             for (String visibleType : visibleAccountTypes) {
3558                 Account[] accountsForType = getAccountsFromCacheLocked(
3559                         userAccounts, visibleType, callingUid, callingPackage);
3560                 if (accountsForType != null) {
3561                     visibleAccounts.addAll(Arrays.asList(accountsForType));
3562                 }
3563             }
3564             Account[] result = new Account[visibleAccounts.size()];
3565             for (int i = 0; i < visibleAccounts.size(); i++) {
3566                 result[i] = visibleAccounts.get(i);
3567             }
3568             return result;
3569         }
3570     }
3571 
3572     @Override
addSharedAccountsFromParentUser(int parentUserId, int userId)3573     public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3574         checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
3575         Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3576         for (Account account : accounts) {
3577             addSharedAccountAsUser(account, userId);
3578         }
3579     }
3580 
addSharedAccountAsUser(Account account, int userId)3581     private boolean addSharedAccountAsUser(Account account, int userId) {
3582         userId = handleIncomingUser(userId);
3583         UserAccounts accounts = getUserAccounts(userId);
3584         SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3585         ContentValues values = new ContentValues();
3586         values.put(ACCOUNTS_NAME, account.name);
3587         values.put(ACCOUNTS_TYPE, account.type);
3588         db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3589                 new String[] {account.name, account.type});
3590         long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3591         if (accountId < 0) {
3592             Log.w(TAG, "insertAccountIntoDatabase: " + account
3593                     + ", skipping the DB insert failed");
3594             return false;
3595         }
3596         logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
3597         return true;
3598     }
3599 
3600     @Override
renameSharedAccountAsUser(Account account, String newName, int userId)3601     public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3602         userId = handleIncomingUser(userId);
3603         UserAccounts accounts = getUserAccounts(userId);
3604         SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3605         long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3606         final ContentValues values = new ContentValues();
3607         values.put(ACCOUNTS_NAME, newName);
3608         int r = db.update(
3609                 TABLE_SHARED_ACCOUNTS,
3610                 values,
3611                 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3612                 new String[] { account.name, account.type });
3613         if (r > 0) {
3614             int callingUid = getCallingUid();
3615             logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3616                     sharedTableAccountId, accounts, callingUid);
3617             // Recursively rename the account.
3618             renameAccountInternal(accounts, account, newName);
3619         }
3620         return r > 0;
3621     }
3622 
3623     @Override
removeSharedAccountAsUser(Account account, int userId)3624     public boolean removeSharedAccountAsUser(Account account, int userId) {
3625         return removeSharedAccountAsUser(account, userId, getCallingUid());
3626     }
3627 
removeSharedAccountAsUser(Account account, int userId, int callingUid)3628     private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
3629         userId = handleIncomingUser(userId);
3630         UserAccounts accounts = getUserAccounts(userId);
3631         SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3632         long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3633         int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3634                 new String[] {account.name, account.type});
3635         if (r > 0) {
3636             logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3637                     sharedTableAccountId, accounts, callingUid);
3638             removeAccountInternal(accounts, account, callingUid);
3639         }
3640         return r > 0;
3641     }
3642 
3643     @Override
getSharedAccountsAsUser(int userId)3644     public Account[] getSharedAccountsAsUser(int userId) {
3645         userId = handleIncomingUser(userId);
3646         UserAccounts accounts = getUserAccounts(userId);
3647         ArrayList<Account> accountList = new ArrayList<>();
3648         Cursor cursor = null;
3649         try {
3650             cursor = accounts.openHelper.getReadableDatabase()
3651                     .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3652                     null, null, null, null, null);
3653             if (cursor != null && cursor.moveToFirst()) {
3654                 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3655                 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3656                 do {
3657                     accountList.add(new Account(cursor.getString(nameIndex),
3658                             cursor.getString(typeIndex)));
3659                 } while (cursor.moveToNext());
3660             }
3661         } finally {
3662             if (cursor != null) {
3663                 cursor.close();
3664             }
3665         }
3666         Account[] accountArray = new Account[accountList.size()];
3667         accountList.toArray(accountArray);
3668         return accountArray;
3669     }
3670 
3671     @Override
3672     @NonNull
getAccounts(String type, String opPackageName)3673     public Account[] getAccounts(String type, String opPackageName) {
3674         return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
3675     }
3676 
3677     @Override
3678     @NonNull
getAccountsForPackage(String packageName, int uid, String opPackageName)3679     public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
3680         int callingUid = Binder.getCallingUid();
3681         if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3682             throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3683                     + callingUid + " with uid=" + uid);
3684         }
3685         return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3686                 opPackageName);
3687     }
3688 
3689     @Override
3690     @NonNull
getAccountsByTypeForPackage(String type, String packageName, String opPackageName)3691     public Account[] getAccountsByTypeForPackage(String type, String packageName,
3692             String opPackageName) {
3693         int packageUid = -1;
3694         try {
3695             packageUid = AppGlobals.getPackageManager().getPackageUid(
3696                     packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3697                     UserHandle.getCallingUserId());
3698         } catch (RemoteException re) {
3699             Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3700             return new Account[0];
3701         }
3702         return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3703                 packageUid, opPackageName);
3704     }
3705 
3706     @Override
getAccountsByFeatures( IAccountManagerResponse response, String type, String[] features, String opPackageName)3707     public void getAccountsByFeatures(
3708             IAccountManagerResponse response,
3709             String type,
3710             String[] features,
3711             String opPackageName) {
3712         int callingUid = Binder.getCallingUid();
3713         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3714             Log.v(TAG, "getAccounts: accountType " + type
3715                     + ", response " + response
3716                     + ", features " + stringArrayToString(features)
3717                     + ", caller's uid " + callingUid
3718                     + ", pid " + Binder.getCallingPid());
3719         }
3720         if (response == null) throw new IllegalArgumentException("response is null");
3721         if (type == null) throw new IllegalArgumentException("accountType is null");
3722         int userId = UserHandle.getCallingUserId();
3723 
3724         List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3725                 opPackageName);
3726         if (!visibleAccountTypes.contains(type)) {
3727             Bundle result = new Bundle();
3728             // Need to return just the accounts that are from matching signatures.
3729             result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
3730             try {
3731                 response.onResult(result);
3732             } catch (RemoteException e) {
3733                 Log.e(TAG, "Cannot respond to caller do to exception." , e);
3734             }
3735             return;
3736         }
3737         long identityToken = clearCallingIdentity();
3738         try {
3739             UserAccounts userAccounts = getUserAccounts(userId);
3740             if (features == null || features.length == 0) {
3741                 Account[] accounts;
3742                 synchronized (userAccounts.cacheLock) {
3743                     accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
3744                 }
3745                 Bundle result = new Bundle();
3746                 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3747                 onResult(response, result);
3748                 return;
3749             }
3750             new GetAccountsByTypeAndFeatureSession(
3751                     userAccounts,
3752                     response,
3753                     type,
3754                     features,
3755                     callingUid).bind();
3756         } finally {
3757             restoreCallingIdentity(identityToken);
3758         }
3759     }
3760 
getAccountIdFromSharedTable(SQLiteDatabase db, Account account)3761     private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
3762         Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
3763                 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3764         try {
3765             if (cursor.moveToNext()) {
3766                 return cursor.getLong(0);
3767             }
3768             return -1;
3769         } finally {
3770             cursor.close();
3771         }
3772     }
3773 
getAccountIdLocked(SQLiteDatabase db, Account account)3774     private long getAccountIdLocked(SQLiteDatabase db, Account account) {
3775         Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
3776                 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3777         try {
3778             if (cursor.moveToNext()) {
3779                 return cursor.getLong(0);
3780             }
3781             return -1;
3782         } finally {
3783             cursor.close();
3784         }
3785     }
3786 
getExtrasIdLocked(SQLiteDatabase db, long accountId, String key)3787     private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
3788         Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
3789                 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
3790                 new String[]{key}, null, null, null);
3791         try {
3792             if (cursor.moveToNext()) {
3793                 return cursor.getLong(0);
3794             }
3795             return -1;
3796         } finally {
3797             cursor.close();
3798         }
3799     }
3800 
3801     private abstract class Session extends IAccountAuthenticatorResponse.Stub
3802             implements IBinder.DeathRecipient, ServiceConnection {
3803         IAccountManagerResponse mResponse;
3804         final String mAccountType;
3805         final boolean mExpectActivityLaunch;
3806         final long mCreationTime;
3807         final String mAccountName;
3808         // Indicates if we need to add auth details(like last credential time)
3809         final boolean mAuthDetailsRequired;
3810         // If set, we need to update the last authenticated time. This is
3811         // currently
3812         // used on
3813         // successful confirming credentials.
3814         final boolean mUpdateLastAuthenticatedTime;
3815 
3816         public int mNumResults = 0;
3817         private int mNumRequestContinued = 0;
3818         private int mNumErrors = 0;
3819 
3820         IAccountAuthenticator mAuthenticator = null;
3821 
3822         private final boolean mStripAuthTokenFromResult;
3823         protected final UserAccounts mAccounts;
3824 
Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, boolean authDetailsRequired)3825         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3826                 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3827                 boolean authDetailsRequired) {
3828             this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
3829                     accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
3830         }
3831 
Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, boolean authDetailsRequired, boolean updateLastAuthenticatedTime)3832         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3833                 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3834                 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
3835             super();
3836             //if (response == null) throw new IllegalArgumentException("response is null");
3837             if (accountType == null) throw new IllegalArgumentException("accountType is null");
3838             mAccounts = accounts;
3839             mStripAuthTokenFromResult = stripAuthTokenFromResult;
3840             mResponse = response;
3841             mAccountType = accountType;
3842             mExpectActivityLaunch = expectActivityLaunch;
3843             mCreationTime = SystemClock.elapsedRealtime();
3844             mAccountName = accountName;
3845             mAuthDetailsRequired = authDetailsRequired;
3846             mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
3847 
3848             synchronized (mSessions) {
3849                 mSessions.put(toString(), this);
3850             }
3851             if (response != null) {
3852                 try {
3853                     response.asBinder().linkToDeath(this, 0 /* flags */);
3854                 } catch (RemoteException e) {
3855                     mResponse = null;
3856                     binderDied();
3857                 }
3858             }
3859         }
3860 
getResponseAndClose()3861         IAccountManagerResponse getResponseAndClose() {
3862             if (mResponse == null) {
3863                 // this session has already been closed
3864                 return null;
3865             }
3866             IAccountManagerResponse response = mResponse;
3867             close(); // this clears mResponse so we need to save the response before this call
3868             return response;
3869         }
3870 
3871         /**
3872          * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
3873          * security policy.
3874          *
3875          * In particular we want to make sure that the Authenticator doesn't try to trick users
3876          * into launching aribtrary intents on the device via by tricking to click authenticator
3877          * supplied entries in the system Settings app.
3878          */
checkKeyIntent( int authUid, Intent intent)3879         protected void checkKeyIntent(
3880                 int authUid,
3881                 Intent intent) throws SecurityException {
3882             long bid = Binder.clearCallingIdentity();
3883             try {
3884                 PackageManager pm = mContext.getPackageManager();
3885                 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
3886                 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
3887                 int targetUid = targetActivityInfo.applicationInfo.uid;
3888                 if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) {
3889                     String pkgName = targetActivityInfo.packageName;
3890                     String activityName = targetActivityInfo.name;
3891                     String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
3892                             + "does not share a signature with the supplying authenticator (%s).";
3893                     throw new SecurityException(
3894                             String.format(tmpl, activityName, pkgName, mAccountType));
3895                 }
3896             } finally {
3897                 Binder.restoreCallingIdentity(bid);
3898             }
3899         }
3900 
close()3901         private void close() {
3902             synchronized (mSessions) {
3903                 if (mSessions.remove(toString()) == null) {
3904                     // the session was already closed, so bail out now
3905                     return;
3906                 }
3907             }
3908             if (mResponse != null) {
3909                 // stop listening for response deaths
3910                 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
3911 
3912                 // clear this so that we don't accidentally send any further results
3913                 mResponse = null;
3914             }
3915             cancelTimeout();
3916             unbind();
3917         }
3918 
3919         @Override
binderDied()3920         public void binderDied() {
3921             mResponse = null;
3922             close();
3923         }
3924 
toDebugString()3925         protected String toDebugString() {
3926             return toDebugString(SystemClock.elapsedRealtime());
3927         }
3928 
toDebugString(long now)3929         protected String toDebugString(long now) {
3930             return "Session: expectLaunch " + mExpectActivityLaunch
3931                     + ", connected " + (mAuthenticator != null)
3932                     + ", stats (" + mNumResults + "/" + mNumRequestContinued
3933                     + "/" + mNumErrors + ")"
3934                     + ", lifetime " + ((now - mCreationTime) / 1000.0);
3935         }
3936 
bind()3937         void bind() {
3938             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3939                 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
3940             }
3941             if (!bindToAuthenticator(mAccountType)) {
3942                 Log.d(TAG, "bind attempt failed for " + toDebugString());
3943                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
3944             }
3945         }
3946 
unbind()3947         private void unbind() {
3948             if (mAuthenticator != null) {
3949                 mAuthenticator = null;
3950                 mContext.unbindService(this);
3951             }
3952         }
3953 
cancelTimeout()3954         public void cancelTimeout() {
3955             mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
3956         }
3957 
3958         @Override
onServiceConnected(ComponentName name, IBinder service)3959         public void onServiceConnected(ComponentName name, IBinder service) {
3960             mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
3961             try {
3962                 run();
3963             } catch (RemoteException e) {
3964                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3965                         "remote exception");
3966             }
3967         }
3968 
3969         @Override
onServiceDisconnected(ComponentName name)3970         public void onServiceDisconnected(ComponentName name) {
3971             mAuthenticator = null;
3972             IAccountManagerResponse response = getResponseAndClose();
3973             if (response != null) {
3974                 try {
3975                     response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3976                             "disconnected");
3977                 } catch (RemoteException e) {
3978                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
3979                         Log.v(TAG, "Session.onServiceDisconnected: "
3980                                 + "caught RemoteException while responding", e);
3981                     }
3982                 }
3983             }
3984         }
3985 
run()3986         public abstract void run() throws RemoteException;
3987 
onTimedOut()3988         public void onTimedOut() {
3989             IAccountManagerResponse response = getResponseAndClose();
3990             if (response != null) {
3991                 try {
3992                     response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3993                             "timeout");
3994                 } catch (RemoteException e) {
3995                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
3996                         Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
3997                                 e);
3998                     }
3999                 }
4000             }
4001         }
4002 
4003         @Override
onResult(Bundle result)4004         public void onResult(Bundle result) {
4005             Bundle.setDefusable(result, true);
4006             mNumResults++;
4007             Intent intent = null;
4008             if (result != null) {
4009                 boolean isSuccessfulConfirmCreds = result.getBoolean(
4010                         AccountManager.KEY_BOOLEAN_RESULT, false);
4011                 boolean isSuccessfulUpdateCredsOrAddAccount =
4012                         result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4013                         && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
4014                 // We should only update lastAuthenticated time, if
4015                 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4016                 // or updateRequest was successful
4017                 boolean needUpdate = mUpdateLastAuthenticatedTime
4018                         && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
4019                 if (needUpdate || mAuthDetailsRequired) {
4020                     boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4021                     if (needUpdate && accountPresent) {
4022                         updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4023                     }
4024                     if (mAuthDetailsRequired) {
4025                         long lastAuthenticatedTime = -1;
4026                         if (accountPresent) {
4027                             lastAuthenticatedTime = DatabaseUtils.longForQuery(
4028                                     mAccounts.openHelper.getReadableDatabase(),
4029                                     "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4030                                             + " FROM " +
4031                                             TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4032                                             + ACCOUNTS_TYPE + "=?",
4033                                     new String[] {
4034                                             mAccountName, mAccountType
4035                                     });
4036                         }
4037                         result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
4038                                 lastAuthenticatedTime);
4039                     }
4040                 }
4041             }
4042             if (result != null
4043                     && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
4044                 checkKeyIntent(
4045                         Binder.getCallingUid(),
4046                         intent);
4047             }
4048             if (result != null
4049                     && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
4050                 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4051                 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
4052                 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4053                     Account account = new Account(accountName, accountType);
4054                     cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4055                             new UserHandle(mAccounts.userId));
4056                 }
4057             }
4058             IAccountManagerResponse response;
4059             if (mExpectActivityLaunch && result != null
4060                     && result.containsKey(AccountManager.KEY_INTENT)) {
4061                 response = mResponse;
4062             } else {
4063                 response = getResponseAndClose();
4064             }
4065             if (response != null) {
4066                 try {
4067                     if (result == null) {
4068                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
4069                             Log.v(TAG, getClass().getSimpleName()
4070                                     + " calling onError() on response " + response);
4071                         }
4072                         response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
4073                                 "null bundle returned");
4074                     } else {
4075                         if (mStripAuthTokenFromResult) {
4076                             result.remove(AccountManager.KEY_AUTHTOKEN);
4077                         }
4078                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
4079                             Log.v(TAG, getClass().getSimpleName()
4080                                     + " calling onResult() on response " + response);
4081                         }
4082                         if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4083                                 (intent == null)) {
4084                             // All AccountManager error codes are greater than 0
4085                             response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4086                                     result.getString(AccountManager.KEY_ERROR_MESSAGE));
4087                         } else {
4088                             response.onResult(result);
4089                         }
4090                     }
4091                 } catch (RemoteException e) {
4092                     // if the caller is dead then there is no one to care about remote exceptions
4093                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
4094                         Log.v(TAG, "failure while notifying response", e);
4095                     }
4096                 }
4097             }
4098         }
4099 
4100         @Override
onRequestContinued()4101         public void onRequestContinued() {
4102             mNumRequestContinued++;
4103         }
4104 
4105         @Override
onError(int errorCode, String errorMessage)4106         public void onError(int errorCode, String errorMessage) {
4107             mNumErrors++;
4108             IAccountManagerResponse response = getResponseAndClose();
4109             if (response != null) {
4110                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4111                     Log.v(TAG, getClass().getSimpleName()
4112                             + " calling onError() on response " + response);
4113                 }
4114                 try {
4115                     response.onError(errorCode, errorMessage);
4116                 } catch (RemoteException e) {
4117                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
4118                         Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4119                     }
4120                 }
4121             } else {
4122                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4123                     Log.v(TAG, "Session.onError: already closed");
4124                 }
4125             }
4126         }
4127 
4128         /**
4129          * find the component name for the authenticator and initiate a bind
4130          * if no authenticator or the bind fails then return false, otherwise return true
4131          */
bindToAuthenticator(String authenticatorType)4132         private boolean bindToAuthenticator(String authenticatorType) {
4133             final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4134             authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4135                     AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
4136             if (authenticatorInfo == null) {
4137                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4138                     Log.v(TAG, "there is no authenticator for " + authenticatorType
4139                             + ", bailing out");
4140                 }
4141                 return false;
4142             }
4143 
4144             if (!isLocalUnlockedUser(mAccounts.userId)
4145                     && !authenticatorInfo.componentInfo.directBootAware) {
4146                 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4147                         + " which isn't encryption aware");
4148                 return false;
4149             }
4150 
4151             Intent intent = new Intent();
4152             intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4153             intent.setComponent(authenticatorInfo.componentName);
4154             if (Log.isLoggable(TAG, Log.VERBOSE)) {
4155                 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4156             }
4157             if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
4158                     UserHandle.of(mAccounts.userId))) {
4159                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4160                     Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4161                 }
4162                 return false;
4163             }
4164 
4165             return true;
4166         }
4167     }
4168 
4169     private class MessageHandler extends Handler {
MessageHandler(Looper looper)4170         MessageHandler(Looper looper) {
4171             super(looper);
4172         }
4173 
4174         @Override
handleMessage(Message msg)4175         public void handleMessage(Message msg) {
4176             switch (msg.what) {
4177                 case MESSAGE_TIMED_OUT:
4178                     Session session = (Session)msg.obj;
4179                     session.onTimedOut();
4180                     break;
4181 
4182                 case MESSAGE_COPY_SHARED_ACCOUNT:
4183                     copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
4184                     break;
4185 
4186                 default:
4187                     throw new IllegalStateException("unhandled message: " + msg.what);
4188             }
4189         }
4190     }
4191 
4192     @VisibleForTesting
getPreNDatabaseName(int userId)4193     String getPreNDatabaseName(int userId) {
4194         File systemDir = Environment.getDataSystemDirectory();
4195         File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4196                 PRE_N_DATABASE_NAME);
4197         if (userId == 0) {
4198             // Migrate old file, if it exists, to the new location.
4199             // Make sure the new file doesn't already exist. A dummy file could have been
4200             // accidentally created in the old location, causing the new one to become corrupted
4201             // as well.
4202             File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
4203             if (oldFile.exists() && !databaseFile.exists()) {
4204                 // Check for use directory; create if it doesn't exist, else renameTo will fail
4205                 File userDir = Environment.getUserSystemDirectory(userId);
4206                 if (!userDir.exists()) {
4207                     if (!userDir.mkdirs()) {
4208                         throw new IllegalStateException("User dir cannot be created: " + userDir);
4209                     }
4210                 }
4211                 if (!oldFile.renameTo(databaseFile)) {
4212                     throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4213                 }
4214             }
4215         }
4216         return databaseFile.getPath();
4217     }
4218 
4219     @VisibleForTesting
getDeDatabaseName(int userId)4220     String getDeDatabaseName(int userId) {
4221         File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4222                 DE_DATABASE_NAME);
4223         return databaseFile.getPath();
4224     }
4225 
4226     @VisibleForTesting
getCeDatabaseName(int userId)4227     String getCeDatabaseName(int userId) {
4228         File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4229                 CE_DATABASE_NAME);
4230         return databaseFile.getPath();
4231     }
4232 
4233     private static class DebugDbHelper{
DebugDbHelper()4234         private DebugDbHelper() {
4235         }
4236 
4237         private static String TABLE_DEBUG = "debug_table";
4238 
4239         // Columns for the table
4240         private static String ACTION_TYPE = "action_type";
4241         private static String TIMESTAMP = "time";
4242         private static String CALLER_UID = "caller_uid";
4243         private static String TABLE_NAME = "table_name";
4244         private static String KEY = "primary_key";
4245 
4246         // These actions correspond to the occurrence of real actions. Since
4247         // these are called by the authenticators, the uid associated will be
4248         // of the authenticator.
4249         private static String ACTION_SET_PASSWORD = "action_set_password";
4250         private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4251         private static String ACTION_ACCOUNT_ADD = "action_account_add";
4252         private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
4253         private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
4254         private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4255         private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4256 
4257         // These actions don't necessarily correspond to any action on
4258         // accountDb taking place. As an example, there might be a request for
4259         // addingAccount, which might not lead to addition of account on grounds
4260         // of bad authentication. We will still be logging it to keep track of
4261         // who called.
4262         private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4263         private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
4264         private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
4265 
4266         //This action doesn't add account to accountdb. Account is only
4267         // added in finishSession which may be in a different user profile.
4268         private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
4269         private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4270                 "action_called_account_session_finish";
4271 
4272         private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4273 
createDebugTable(SQLiteDatabase db)4274         private static void createDebugTable(SQLiteDatabase db) {
4275             db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4276                     + ACCOUNTS_ID + " INTEGER,"
4277                     + ACTION_TYPE + " TEXT NOT NULL, "
4278                     + TIMESTAMP + " DATETIME,"
4279                     + CALLER_UID + " INTEGER NOT NULL,"
4280                     + TABLE_NAME + " TEXT NOT NULL,"
4281                     + KEY + " INTEGER PRIMARY KEY)");
4282             db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4283         }
4284     }
4285 
logRecord(UserAccounts accounts, String action, String tableName)4286     private void logRecord(UserAccounts accounts, String action, String tableName) {
4287         SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4288         logRecord(db, action, tableName, -1, accounts);
4289     }
4290 
logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid)4291     private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4292         SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4293         logRecord(db, action, tableName, -1, accounts, uid);
4294     }
4295 
4296     /*
4297      * This function receives an opened writable database.
4298      */
logRecord(SQLiteDatabase db, String action, String tableName, long accountId, UserAccounts userAccount)4299     private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4300             UserAccounts userAccount) {
4301         logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4302     }
4303 
4304     /*
4305      * This function receives an opened writable database.
4306      */
logRecord(SQLiteDatabase db, String action, String tableName, long accountId, UserAccounts userAccount, int callingUid)4307     private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4308             UserAccounts userAccount, int callingUid) {
4309         SQLiteStatement logStatement = userAccount.statementForLogging;
4310         logStatement.bindLong(1, accountId);
4311         logStatement.bindString(2, action);
4312         logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
4313         logStatement.bindLong(4, callingUid);
4314         logStatement.bindString(5, tableName);
4315         logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
4316         logStatement.execute();
4317         logStatement.clearBindings();
4318         userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
4319                 % MAX_DEBUG_DB_SIZE;
4320     }
4321 
4322     /*
4323      * This should only be called once to compile the sql statement for logging
4324      * and to find the insertion point.
4325      */
initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount)4326     private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
4327             UserAccounts userAccount) {
4328         // Initialize the count if not done earlier.
4329         int size = (int) getDebugTableRowCount(db);
4330         if (size >= MAX_DEBUG_DB_SIZE) {
4331             // Table is full, and we need to find the point where to insert.
4332             userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
4333         } else {
4334             userAccount.debugDbInsertionPoint = size;
4335         }
4336         compileSqlStatementForLogging(db, userAccount);
4337     }
4338 
compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount)4339     private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
4340         String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
4341                 + " VALUES (?,?,?,?,?,?)";
4342         userAccount.statementForLogging = db.compileStatement(sql);
4343     }
4344 
getDebugTableRowCount(SQLiteDatabase db)4345     private long getDebugTableRowCount(SQLiteDatabase db) {
4346         String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
4347         return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4348     }
4349 
4350     /*
4351      * Finds the row key where the next insertion should take place. This should
4352      * be invoked only if the table has reached its full capacity.
4353      */
getDebugTableInsertionPoint(SQLiteDatabase db)4354     private long getDebugTableInsertionPoint(SQLiteDatabase db) {
4355         // This query finds the smallest timestamp value (and if 2 records have
4356         // same timestamp, the choose the lower id).
4357         String queryCountDebugDbRows = new StringBuilder()
4358                 .append("SELECT ").append(DebugDbHelper.KEY)
4359                 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
4360                 .append(" ORDER BY ")
4361                 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
4362                 .append(" LIMIT 1")
4363                 .toString();
4364         return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4365     }
4366 
4367     static class PreNDatabaseHelper extends SQLiteOpenHelper {
4368         private final Context mContext;
4369         private final int mUserId;
4370 
PreNDatabaseHelper(Context context, int userId, String preNDatabaseName)4371         public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
4372             super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
4373             mContext = context;
4374             mUserId = userId;
4375         }
4376 
4377         @Override
onCreate(SQLiteDatabase db)4378         public void onCreate(SQLiteDatabase db) {
4379             // We use PreNDatabaseHelper only if pre-N db exists
4380             throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
4381         }
4382 
createSharedAccountsTable(SQLiteDatabase db)4383         private void createSharedAccountsTable(SQLiteDatabase db) {
4384             db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4385                     + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4386                     + ACCOUNTS_NAME + " TEXT NOT NULL, "
4387                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4388                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4389         }
4390 
addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db)4391         private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4392             db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4393                     + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4394         }
4395 
addOldAccountNameColumn(SQLiteDatabase db)4396         private void addOldAccountNameColumn(SQLiteDatabase db) {
4397             db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4398         }
4399 
addDebugTable(SQLiteDatabase db)4400         private void addDebugTable(SQLiteDatabase db) {
4401             DebugDbHelper.createDebugTable(db);
4402         }
4403 
createAccountsDeletionTrigger(SQLiteDatabase db)4404         private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4405             db.execSQL(""
4406                     + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4407                     + " BEGIN"
4408                     + "   DELETE FROM " + TABLE_AUTHTOKENS
4409                     + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4410                     + "   DELETE FROM " + TABLE_EXTRAS
4411                     + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4412                     + "   DELETE FROM " + TABLE_GRANTS
4413                     + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4414                     + " END");
4415         }
4416 
createGrantsTable(SQLiteDatabase db)4417         private void createGrantsTable(SQLiteDatabase db) {
4418             db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4419                     + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4420                     + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4421                     + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4422                     + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4423                     +   "," + GRANTS_GRANTEE_UID + "))");
4424         }
4425 
populateMetaTableWithAuthTypeAndUID( SQLiteDatabase db, Map<String, Integer> authTypeAndUIDMap)4426         private void populateMetaTableWithAuthTypeAndUID(
4427                 SQLiteDatabase db,
4428                 Map<String, Integer> authTypeAndUIDMap) {
4429             Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator();
4430             while (iterator.hasNext()) {
4431                 Entry<String, Integer> entry = iterator.next();
4432                 ContentValues values = new ContentValues();
4433                 values.put(META_KEY,
4434                         META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
4435                 values.put(META_VALUE, entry.getValue());
4436                 db.insert(TABLE_META, null, values);
4437             }
4438         }
4439 
4440         /**
4441          * Pre-N database may need an upgrade before splitting
4442          */
4443         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)4444         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4445             Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4446 
4447             if (oldVersion == 1) {
4448                 // no longer need to do anything since the work is done
4449                 // when upgrading from version 2
4450                 oldVersion++;
4451             }
4452 
4453             if (oldVersion == 2) {
4454                 createGrantsTable(db);
4455                 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4456                 createAccountsDeletionTrigger(db);
4457                 oldVersion++;
4458             }
4459 
4460             if (oldVersion == 3) {
4461                 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4462                         " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4463                 oldVersion++;
4464             }
4465 
4466             if (oldVersion == 4) {
4467                 createSharedAccountsTable(db);
4468                 oldVersion++;
4469             }
4470 
4471             if (oldVersion == 5) {
4472                 addOldAccountNameColumn(db);
4473                 oldVersion++;
4474             }
4475 
4476             if (oldVersion == 6) {
4477                 addLastSuccessfullAuthenticatedTimeColumn(db);
4478                 oldVersion++;
4479             }
4480 
4481             if (oldVersion == 7) {
4482                 addDebugTable(db);
4483                 oldVersion++;
4484             }
4485 
4486             if (oldVersion == 8) {
4487                 populateMetaTableWithAuthTypeAndUID(
4488                         db,
4489                         AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
4490                 oldVersion++;
4491             }
4492 
4493             if (oldVersion != newVersion) {
4494                 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4495             }
4496         }
4497 
4498         @Override
onOpen(SQLiteDatabase db)4499         public void onOpen(SQLiteDatabase db) {
4500             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4501         }
4502     }
4503 
4504     static class DeDatabaseHelper extends SQLiteOpenHelper {
4505 
4506         private final int mUserId;
4507         private volatile boolean mCeAttached;
4508 
DeDatabaseHelper(Context context, int userId, String deDatabaseName)4509         private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
4510             super(context, deDatabaseName, null, DE_DATABASE_VERSION);
4511             mUserId = userId;
4512         }
4513 
4514         /**
4515          * This call needs to be made while the mCacheLock is held. The way to
4516          * ensure this is to get the lock any time a method is called ont the DatabaseHelper
4517          * @param db The database.
4518          */
4519         @Override
onCreate(SQLiteDatabase db)4520         public void onCreate(SQLiteDatabase db) {
4521             Log.i(TAG, "Creating DE database for user " + mUserId);
4522             db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4523                     + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
4524                     + ACCOUNTS_NAME + " TEXT NOT NULL, "
4525                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4526                     + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
4527                     + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
4528                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4529 
4530             db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4531                     + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4532                     + META_VALUE + " TEXT)");
4533 
4534             createGrantsTable(db);
4535             createSharedAccountsTable(db);
4536             createAccountsDeletionTrigger(db);
4537             DebugDbHelper.createDebugTable(db);
4538         }
4539 
createSharedAccountsTable(SQLiteDatabase db)4540         private void createSharedAccountsTable(SQLiteDatabase db) {
4541             db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4542                     + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4543                     + ACCOUNTS_NAME + " TEXT NOT NULL, "
4544                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4545                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4546         }
4547 
createAccountsDeletionTrigger(SQLiteDatabase db)4548         private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4549             db.execSQL(""
4550                     + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4551                     + " BEGIN"
4552                     + "   DELETE FROM " + TABLE_GRANTS
4553                     + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4554                     + " END");
4555         }
4556 
createGrantsTable(SQLiteDatabase db)4557         private void createGrantsTable(SQLiteDatabase db) {
4558             db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4559                     + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4560                     + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4561                     + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4562                     + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4563                     +   "," + GRANTS_GRANTEE_UID + "))");
4564         }
4565 
4566         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)4567         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4568             Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4569 
4570             if (oldVersion != newVersion) {
4571                 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4572             }
4573         }
4574 
attachCeDatabase(File ceDbFile)4575         public void attachCeDatabase(File ceDbFile) {
4576             SQLiteDatabase db = getWritableDatabase();
4577             db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
4578             mCeAttached = true;
4579         }
4580 
isCeDatabaseAttached()4581         public boolean isCeDatabaseAttached() {
4582             return mCeAttached;
4583         }
4584 
4585 
getReadableDatabaseUserIsUnlocked()4586         public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
4587             if(!mCeAttached) {
4588                 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
4589                         + " is still locked. CE database is not yet available.", new Throwable());
4590             }
4591             return super.getReadableDatabase();
4592         }
4593 
getWritableDatabaseUserIsUnlocked()4594         public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
4595             if(!mCeAttached) {
4596                 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
4597                         + " is still locked. CE database is not yet available.", new Throwable());
4598             }
4599             return super.getWritableDatabase();
4600         }
4601 
4602         @Override
onOpen(SQLiteDatabase db)4603         public void onOpen(SQLiteDatabase db) {
4604             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
4605         }
4606 
migratePreNDbToDe(File preNDbFile)4607         private void migratePreNDbToDe(File preNDbFile) {
4608             Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
4609             SQLiteDatabase db = getWritableDatabase();
4610             db.execSQL("ATTACH DATABASE '" +  preNDbFile.getPath() + "' AS preNDb");
4611             db.beginTransaction();
4612             // Copy accounts fields
4613             db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
4614                     + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
4615                     + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4616                     + ") "
4617                     + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
4618                     + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4619                     + " FROM preNDb." + TABLE_ACCOUNTS);
4620             // Copy SHARED_ACCOUNTS
4621             db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
4622                     + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
4623                     "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
4624                     + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
4625             // Copy DEBUG_TABLE
4626             db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
4627                     + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
4628                     + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
4629                     + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
4630                     "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
4631                     + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
4632                     + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
4633                     + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
4634             // Copy GRANTS
4635             db.execSQL("INSERT INTO " + TABLE_GRANTS
4636                     + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
4637                     + GRANTS_GRANTEE_UID + ") " +
4638                     "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
4639                     + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
4640             // Copy META
4641             db.execSQL("INSERT INTO " + TABLE_META
4642                     + "(" + META_KEY + "," + META_VALUE + ") "
4643                     + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
4644             db.setTransactionSuccessful();
4645             db.endTransaction();
4646 
4647             db.execSQL("DETACH DATABASE preNDb");
4648         }
4649 
create( Context context, int userId, File preNDatabaseFile, File deDatabaseFile)4650         static DeDatabaseHelper create(
4651                 Context context,
4652                 int userId,
4653                 File preNDatabaseFile,
4654                 File deDatabaseFile) {
4655             boolean newDbExists = deDatabaseFile.exists();
4656             DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
4657                     deDatabaseFile.getPath());
4658             // If the db just created, and there is a legacy db, migrate it
4659             if (!newDbExists && preNDatabaseFile.exists()) {
4660                 // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
4661                 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
4662                         preNDatabaseFile.getPath());
4663                 // Open the database to force upgrade if required
4664                 preNDatabaseHelper.getWritableDatabase();
4665                 preNDatabaseHelper.close();
4666                 // Move data without SPII to DE
4667                 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
4668             }
4669             return deDatabaseHelper;
4670         }
4671     }
4672 
4673     static class CeDatabaseHelper extends SQLiteOpenHelper {
4674 
CeDatabaseHelper(Context context, String ceDatabaseName)4675         public CeDatabaseHelper(Context context, String ceDatabaseName) {
4676             super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
4677         }
4678 
4679         /**
4680          * This call needs to be made while the mCacheLock is held.
4681          * @param db The database.
4682          */
4683         @Override
onCreate(SQLiteDatabase db)4684         public void onCreate(SQLiteDatabase db) {
4685             Log.i(TAG, "Creating CE database " + getDatabaseName());
4686             db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4687                     + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4688                     + ACCOUNTS_NAME + " TEXT NOT NULL, "
4689                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4690                     + ACCOUNTS_PASSWORD + " TEXT, "
4691                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4692 
4693             db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
4694                     + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
4695                     + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4696                     + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
4697                     + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
4698                     + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
4699 
4700             db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
4701                     + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4702                     + EXTRAS_ACCOUNTS_ID + " INTEGER, "
4703                     + EXTRAS_KEY + " TEXT NOT NULL, "
4704                     + EXTRAS_VALUE + " TEXT, "
4705                     + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
4706 
4707             createAccountsDeletionTrigger(db);
4708         }
4709 
createAccountsDeletionTrigger(SQLiteDatabase db)4710         private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4711             db.execSQL(""
4712                     + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4713                     + " BEGIN"
4714                     + "   DELETE FROM " + TABLE_AUTHTOKENS
4715                     + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4716                     + "   DELETE FROM " + TABLE_EXTRAS
4717                     + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4718                     + " END");
4719         }
4720 
4721         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)4722         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4723             Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
4724 
4725             if (oldVersion == 9) {
4726                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4727                     Log.v(TAG, "onUpgrade upgrading to v10");
4728                 }
4729                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
4730                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
4731                 // Recreate the trigger, since the old one references the table to be removed
4732                 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
4733                 createAccountsDeletionTrigger(db);
4734                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
4735                 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
4736                 oldVersion ++;
4737             }
4738 
4739             if (oldVersion != newVersion) {
4740                 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4741             }
4742         }
4743 
4744         @Override
onOpen(SQLiteDatabase db)4745         public void onOpen(SQLiteDatabase db) {
4746             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
4747         }
4748 
findAccountPasswordByNameAndType(SQLiteDatabase db, String name, String type)4749         static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
4750                 String type) {
4751             Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
4752                     ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
4753                     new String[]{name, type}, null, null, null);
4754             try {
4755                 if (cursor.moveToNext()) {
4756                     return cursor.getString(0);
4757                 }
4758                 return null;
4759             } finally {
4760                 cursor.close();
4761             }
4762         }
4763 
findCeAccountsNotInDe(SQLiteDatabase db)4764         static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
4765             // Select accounts from CE that do not exist in DE
4766             Cursor cursor = db.rawQuery(
4767                     "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
4768                             + " FROM " + CE_TABLE_ACCOUNTS
4769                             + " WHERE NOT EXISTS "
4770                             + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
4771                             + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
4772                             + " )", null);
4773             try {
4774                 List<Account> accounts = new ArrayList<>(cursor.getCount());
4775                 while (cursor.moveToNext()) {
4776                     String accountName = cursor.getString(0);
4777                     String accountType = cursor.getString(1);
4778                     accounts.add(new Account(accountName, accountType));
4779                 }
4780                 return accounts;
4781             } finally {
4782                 cursor.close();
4783             }
4784         }
4785 
4786         /**
4787          * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
4788          * it also performs migration to the new CE database.
4789          * @param context
4790          * @param userId id of the user where the database is located
4791          */
create( Context context, int userId, File preNDatabaseFile, File ceDatabaseFile)4792         static CeDatabaseHelper create(
4793                 Context context,
4794                 int userId,
4795                 File preNDatabaseFile,
4796                 File ceDatabaseFile) {
4797             boolean newDbExists = ceDatabaseFile.exists();
4798             if (Log.isLoggable(TAG, Log.VERBOSE)) {
4799                 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
4800                         + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
4801             }
4802             boolean removeOldDb = false;
4803             if (!newDbExists && preNDatabaseFile.exists()) {
4804                 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
4805             }
4806             // Try to open and upgrade if necessary
4807             CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
4808             ceHelper.getWritableDatabase();
4809             ceHelper.close();
4810             if (removeOldDb) {
4811                 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
4812                 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
4813                     Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
4814                 }
4815             }
4816             return ceHelper;
4817         }
4818 
migratePreNDbToCe(File oldDbFile, File ceDbFile)4819         private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
4820             Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
4821             try {
4822                 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
4823             } catch (IOException e) {
4824                 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
4825                 // Try to remove potentially damaged file if I/O error occurred
4826                 deleteDbFileWarnIfFailed(ceDbFile);
4827                 return false;
4828             }
4829             return true;
4830         }
4831     }
4832 
onBind(@uppressWarnings"unused") Intent intent)4833     public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
4834         return asBinder();
4835     }
4836 
4837     /**
4838      * Searches array of arguments for the specified string
4839      * @param args array of argument strings
4840      * @param value value to search for
4841      * @return true if the value is contained in the array
4842      */
scanArgs(String[] args, String value)4843     private static boolean scanArgs(String[] args, String value) {
4844         if (args != null) {
4845             for (String arg : args) {
4846                 if (value.equals(arg)) {
4847                     return true;
4848                 }
4849             }
4850         }
4851         return false;
4852     }
4853 
4854     @Override
dump(FileDescriptor fd, PrintWriter fout, String[] args)4855     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
4856         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4857                 != PackageManager.PERMISSION_GRANTED) {
4858             fout.println("Permission Denial: can't dump AccountsManager from from pid="
4859                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4860                     + " without permission " + android.Manifest.permission.DUMP);
4861             return;
4862         }
4863         final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
4864         final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
4865 
4866         final List<UserInfo> users = getUserManager().getUsers();
4867         for (UserInfo user : users) {
4868             ipw.println("User " + user + ":");
4869             ipw.increaseIndent();
4870             dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4871             ipw.println();
4872             ipw.decreaseIndent();
4873         }
4874     }
4875 
dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, String[] args, boolean isCheckinRequest)4876     private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4877             String[] args, boolean isCheckinRequest) {
4878         synchronized (userAccounts.cacheLock) {
4879             final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
4880 
4881             if (isCheckinRequest) {
4882                 // This is a checkin request. *Only* upload the account types and the count of each.
4883                 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
4884                         null, null, ACCOUNTS_TYPE, null, null);
4885                 try {
4886                     while (cursor.moveToNext()) {
4887                         // print type,count
4888                         fout.println(cursor.getString(0) + "," + cursor.getString(1));
4889                     }
4890                 } finally {
4891                     if (cursor != null) {
4892                         cursor.close();
4893                     }
4894                 }
4895             } else {
4896                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
4897                         Process.myUid(), null);
4898                 fout.println("Accounts: " + accounts.length);
4899                 for (Account account : accounts) {
4900                     fout.println("  " + account);
4901                 }
4902 
4903                 // Add debug information.
4904                 fout.println();
4905                 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
4906                         null, null, null, null, DebugDbHelper.TIMESTAMP);
4907                 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
4908                 fout.println("Accounts History");
4909                 try {
4910                     while (cursor.moveToNext()) {
4911                         // print type,count
4912                         fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
4913                                 cursor.getString(2) + "," + cursor.getString(3) + ","
4914                                 + cursor.getString(4) + "," + cursor.getString(5));
4915                     }
4916                 } finally {
4917                     cursor.close();
4918                 }
4919 
4920                 fout.println();
4921                 synchronized (mSessions) {
4922                     final long now = SystemClock.elapsedRealtime();
4923                     fout.println("Active Sessions: " + mSessions.size());
4924                     for (Session session : mSessions.values()) {
4925                         fout.println("  " + session.toDebugString(now));
4926                     }
4927                 }
4928 
4929                 fout.println();
4930                 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
4931             }
4932         }
4933     }
4934 
doNotification(UserAccounts accounts, Account account, CharSequence message, Intent intent, int userId)4935     private void doNotification(UserAccounts accounts, Account account, CharSequence message,
4936             Intent intent, int userId) {
4937         long identityToken = clearCallingIdentity();
4938         try {
4939             if (Log.isLoggable(TAG, Log.VERBOSE)) {
4940                 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4941             }
4942 
4943             if (intent.getComponent() != null &&
4944                     GrantCredentialsPermissionActivity.class.getName().equals(
4945                             intent.getComponent().getClassName())) {
4946                 createNoCredentialsPermissionNotification(account, intent, userId);
4947             } else {
4948                 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
4949                 intent.addCategory(String.valueOf(notificationId));
4950                 UserHandle user = new UserHandle(userId);
4951                 Context contextForUser = getContextForUser(user);
4952                 final String notificationTitleFormat =
4953                         contextForUser.getText(R.string.notification_title).toString();
4954                 Notification n = new Notification.Builder(contextForUser)
4955                         .setWhen(0)
4956                         .setSmallIcon(android.R.drawable.stat_sys_warning)
4957                         .setColor(contextForUser.getColor(
4958                                 com.android.internal.R.color.system_notification_accent_color))
4959                         .setContentTitle(String.format(notificationTitleFormat, account.name))
4960                         .setContentText(message)
4961                         .setContentIntent(PendingIntent.getActivityAsUser(
4962                                 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
4963                                 null, user))
4964                         .build();
4965                 installNotification(notificationId, n, user);
4966             }
4967         } finally {
4968             restoreCallingIdentity(identityToken);
4969         }
4970     }
4971 
4972     @VisibleForTesting
installNotification(final int notificationId, final Notification n, UserHandle user)4973     protected void installNotification(final int notificationId, final Notification n,
4974             UserHandle user) {
4975         ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
4976                 .notifyAsUser(null, notificationId, n, user);
4977     }
4978 
4979     @VisibleForTesting
cancelNotification(int id, UserHandle user)4980     protected void cancelNotification(int id, UserHandle user) {
4981         long identityToken = clearCallingIdentity();
4982         try {
4983             ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
4984                 .cancelAsUser(null, id, user);
4985         } finally {
4986             restoreCallingIdentity(identityToken);
4987         }
4988     }
4989 
isPermitted(String opPackageName, int callingUid, String... permissions)4990     private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4991         for (String perm : permissions) {
4992             if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4993                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4994                     Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
4995                 }
4996                 final int opCode = AppOpsManager.permissionToOpCode(perm);
4997                 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4998                         opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4999                     return true;
5000                 }
5001             }
5002         }
5003         return false;
5004     }
5005 
handleIncomingUser(int userId)5006     private int handleIncomingUser(int userId) {
5007         try {
5008             return ActivityManagerNative.getDefault().handleIncomingUser(
5009                     Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5010         } catch (RemoteException re) {
5011             // Shouldn't happen, local.
5012         }
5013         return userId;
5014     }
5015 
isPrivileged(int callingUid)5016     private boolean isPrivileged(int callingUid) {
5017         final int callingUserId = UserHandle.getUserId(callingUid);
5018 
5019         final PackageManager userPackageManager;
5020         try {
5021             userPackageManager = mContext.createPackageContextAsUser(
5022                     "android", 0, new UserHandle(callingUserId)).getPackageManager();
5023         } catch (NameNotFoundException e) {
5024             return false;
5025         }
5026 
5027         String[] packages = userPackageManager.getPackagesForUid(callingUid);
5028         for (String name : packages) {
5029             try {
5030                 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
5031                 if (packageInfo != null
5032                         && (packageInfo.applicationInfo.privateFlags
5033                                 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
5034                     return true;
5035                 }
5036             } catch (PackageManager.NameNotFoundException e) {
5037                 return false;
5038             }
5039         }
5040         return false;
5041     }
5042 
permissionIsGranted( Account account, String authTokenType, int callerUid, int userId)5043     private boolean permissionIsGranted(
5044             Account account, String authTokenType, int callerUid, int userId) {
5045         final boolean isPrivileged = isPrivileged(callerUid);
5046         final boolean fromAuthenticator = account != null
5047                 && isAccountManagedByCaller(account.type, callerUid, userId);
5048         final boolean hasExplicitGrants = account != null
5049                 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
5050         if (Log.isLoggable(TAG, Log.VERBOSE)) {
5051             Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
5052                     + callerUid + ", " + account
5053                     + ": is authenticator? " + fromAuthenticator
5054                     + ", has explicit permission? " + hasExplicitGrants);
5055         }
5056         return fromAuthenticator || hasExplicitGrants || isPrivileged;
5057     }
5058 
isAccountVisibleToCaller(String accountType, int callingUid, int userId, String opPackageName)5059     private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5060             String opPackageName) {
5061         if (accountType == null) {
5062             return false;
5063         } else {
5064             return getTypesVisibleToCaller(callingUid, userId,
5065                     opPackageName).contains(accountType);
5066         }
5067     }
5068 
isAccountManagedByCaller(String accountType, int callingUid, int userId)5069     private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5070         if (accountType == null) {
5071             return false;
5072         } else {
5073             return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5074         }
5075     }
5076 
getTypesVisibleToCaller(int callingUid, int userId, String opPackageName)5077     private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5078             String opPackageName) {
5079         boolean isPermitted =
5080                 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5081                         Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
5082         return getTypesForCaller(callingUid, userId, isPermitted);
5083     }
5084 
getTypesManagedByCaller(int callingUid, int userId)5085     private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5086         return getTypesForCaller(callingUid, userId, false);
5087     }
5088 
getTypesForCaller( int callingUid, int userId, boolean isOtherwisePermitted)5089     private List<String> getTypesForCaller(
5090             int callingUid, int userId, boolean isOtherwisePermitted) {
5091         List<String> managedAccountTypes = new ArrayList<>();
5092         long identityToken = Binder.clearCallingIdentity();
5093         Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5094         try {
5095             serviceInfos = mAuthenticatorCache.getAllServices(userId);
5096         } finally {
5097             Binder.restoreCallingIdentity(identityToken);
5098         }
5099         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
5100                 serviceInfos) {
5101             final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5102             if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5103                 managedAccountTypes.add(serviceInfo.type.type);
5104             }
5105         }
5106         return managedAccountTypes;
5107     }
5108 
isAccountPresentForCaller(String accountName, String accountType)5109     private boolean isAccountPresentForCaller(String accountName, String accountType) {
5110         if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5111             for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5112                 if (account.name.equals(accountName)) {
5113                     return true;
5114                 }
5115             }
5116         }
5117         return false;
5118     }
5119 
checkManageUsersPermission(String message)5120     private static void checkManageUsersPermission(String message) {
5121         if (ActivityManager.checkComponentPermission(
5122                 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5123                 != PackageManager.PERMISSION_GRANTED) {
5124             throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5125         }
5126     }
5127 
checkManageOrCreateUsersPermission(String message)5128     private static void checkManageOrCreateUsersPermission(String message) {
5129         if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5130                 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5131                 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5132                         Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5133             throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5134                     + message);
5135         }
5136     }
5137 
hasExplicitlyGrantedPermission(Account account, String authTokenType, int callerUid)5138     private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5139             int callerUid) {
5140         if (callerUid == Process.SYSTEM_UID) {
5141             return true;
5142         }
5143         UserAccounts accounts = getUserAccountsForCaller();
5144         synchronized (accounts.cacheLock) {
5145             final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
5146             String[] args = { String.valueOf(callerUid), authTokenType,
5147                     account.name, account.type};
5148             final boolean permissionGranted =
5149                     DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
5150             if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5151                 // TODO: Skip this check when running automated tests. Replace this
5152                 // with a more general solution.
5153                 Log.d(TAG, "no credentials permission for usage of " + account + ", "
5154                         + authTokenType + " by uid " + callerUid
5155                         + " but ignoring since device is in test harness.");
5156                 return true;
5157             }
5158             return permissionGranted;
5159         }
5160     }
5161 
isSystemUid(int callingUid)5162     private boolean isSystemUid(int callingUid) {
5163         String[] packages = null;
5164         long ident = Binder.clearCallingIdentity();
5165         try {
5166             packages = mPackageManager.getPackagesForUid(callingUid);
5167         } finally {
5168             Binder.restoreCallingIdentity(ident);
5169         }
5170         if (packages != null) {
5171             for (String name : packages) {
5172                 try {
5173                     PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5174                     if (packageInfo != null
5175                             && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5176                                     != 0) {
5177                         return true;
5178                     }
5179                 } catch (PackageManager.NameNotFoundException e) {
5180                     Log.w(TAG, String.format("Could not find package [%s]", name), e);
5181                 }
5182             }
5183         } else {
5184             Log.w(TAG, "No known packages with uid " + callingUid);
5185         }
5186         return false;
5187     }
5188 
5189     /** Succeeds if any of the specified permissions are granted. */
checkReadAccountsPermitted( int callingUid, String accountType, int userId, String opPackageName)5190     private void checkReadAccountsPermitted(
5191             int callingUid,
5192             String accountType,
5193             int userId,
5194             String opPackageName) {
5195         if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
5196             String msg = String.format(
5197                     "caller uid %s cannot access %s accounts",
5198                     callingUid,
5199                     accountType);
5200             Log.w(TAG, "  " + msg);
5201             throw new SecurityException(msg);
5202         }
5203     }
5204 
canUserModifyAccounts(int userId, int callingUid)5205     private boolean canUserModifyAccounts(int userId, int callingUid) {
5206         // the managing app can always modify accounts
5207         if (isProfileOwner(callingUid)) {
5208             return true;
5209         }
5210         if (getUserManager().getUserRestrictions(new UserHandle(userId))
5211                 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5212             return false;
5213         }
5214         return true;
5215     }
5216 
canUserModifyAccountsForType(int userId, String accountType, int callingUid)5217     private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5218         // the managing app can always modify accounts
5219         if (isProfileOwner(callingUid)) {
5220             return true;
5221         }
5222         DevicePolicyManager dpm = (DevicePolicyManager) mContext
5223                 .getSystemService(Context.DEVICE_POLICY_SERVICE);
5224         String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
5225         if (typesArray == null) {
5226             return true;
5227         }
5228         for (String forbiddenType : typesArray) {
5229             if (forbiddenType.equals(accountType)) {
5230                 return false;
5231             }
5232         }
5233         return true;
5234     }
5235 
isProfileOwner(int uid)5236     private boolean isProfileOwner(int uid) {
5237         final DevicePolicyManagerInternal dpmi =
5238                 LocalServices.getService(DevicePolicyManagerInternal.class);
5239         return (dpmi != null)
5240                 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5241     }
5242 
5243     @Override
updateAppPermission(Account account, String authTokenType, int uid, boolean value)5244     public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5245             throws RemoteException {
5246         final int callingUid = getCallingUid();
5247 
5248         if (callingUid != Process.SYSTEM_UID) {
5249             throw new SecurityException();
5250         }
5251 
5252         if (value) {
5253             grantAppPermission(account, authTokenType, uid);
5254         } else {
5255             revokeAppPermission(account, authTokenType, uid);
5256         }
5257     }
5258 
5259     /**
5260      * Allow callers with the given uid permission to get credentials for account/authTokenType.
5261      * <p>
5262      * Although this is public it can only be accessed via the AccountManagerService object
5263      * which is in the system. This means we don't need to protect it with permissions.
5264      * @hide
5265      */
grantAppPermission(Account account, String authTokenType, int uid)5266     private void grantAppPermission(Account account, String authTokenType, int uid) {
5267         if (account == null || authTokenType == null) {
5268             Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
5269             return;
5270         }
5271         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5272         synchronized (accounts.cacheLock) {
5273             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
5274             db.beginTransaction();
5275             try {
5276                 long accountId = getAccountIdLocked(db, account);
5277                 if (accountId >= 0) {
5278                     ContentValues values = new ContentValues();
5279                     values.put(GRANTS_ACCOUNTS_ID, accountId);
5280                     values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
5281                     values.put(GRANTS_GRANTEE_UID, uid);
5282                     db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
5283                     db.setTransactionSuccessful();
5284                 }
5285             } finally {
5286                 db.endTransaction();
5287             }
5288             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5289                     UserHandle.of(accounts.userId));
5290         }
5291     }
5292 
5293     /**
5294      * Don't allow callers with the given uid permission to get credentials for
5295      * account/authTokenType.
5296      * <p>
5297      * Although this is public it can only be accessed via the AccountManagerService object
5298      * which is in the system. This means we don't need to protect it with permissions.
5299      * @hide
5300      */
revokeAppPermission(Account account, String authTokenType, int uid)5301     private void revokeAppPermission(Account account, String authTokenType, int uid) {
5302         if (account == null || authTokenType == null) {
5303             Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
5304             return;
5305         }
5306         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5307         synchronized (accounts.cacheLock) {
5308             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
5309             db.beginTransaction();
5310             try {
5311                 long accountId = getAccountIdLocked(db, account);
5312                 if (accountId >= 0) {
5313                     db.delete(TABLE_GRANTS,
5314                             GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
5315                                     + GRANTS_GRANTEE_UID + "=?",
5316                             new String[]{String.valueOf(accountId), authTokenType,
5317                                     String.valueOf(uid)});
5318                     db.setTransactionSuccessful();
5319                 }
5320             } finally {
5321                 db.endTransaction();
5322             }
5323             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5324                     new UserHandle(accounts.userId));
5325         }
5326     }
5327 
stringArrayToString(String[] value)5328     static final private String stringArrayToString(String[] value) {
5329         return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
5330     }
5331 
removeAccountFromCacheLocked(UserAccounts accounts, Account account)5332     private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5333         final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
5334         if (oldAccountsForType != null) {
5335             ArrayList<Account> newAccountsList = new ArrayList<Account>();
5336             for (Account curAccount : oldAccountsForType) {
5337                 if (!curAccount.equals(account)) {
5338                     newAccountsList.add(curAccount);
5339                 }
5340             }
5341             if (newAccountsList.isEmpty()) {
5342                 accounts.accountCache.remove(account.type);
5343             } else {
5344                 Account[] newAccountsForType = new Account[newAccountsList.size()];
5345                 newAccountsForType = newAccountsList.toArray(newAccountsForType);
5346                 accounts.accountCache.put(account.type, newAccountsForType);
5347             }
5348         }
5349         accounts.userDataCache.remove(account);
5350         accounts.authTokenCache.remove(account);
5351         accounts.previousNameCache.remove(account);
5352     }
5353 
5354     /**
5355      * This assumes that the caller has already checked that the account is not already present.
5356      */
insertAccountIntoCacheLocked(UserAccounts accounts, Account account)5357     private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
5358         Account[] accountsForType = accounts.accountCache.get(account.type);
5359         int oldLength = (accountsForType != null) ? accountsForType.length : 0;
5360         Account[] newAccountsForType = new Account[oldLength + 1];
5361         if (accountsForType != null) {
5362             System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
5363         }
5364         newAccountsForType[oldLength] = account;
5365         accounts.accountCache.put(account.type, newAccountsForType);
5366     }
5367 
filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, int callingUid, String callingPackage)5368     private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
5369             int callingUid, String callingPackage) {
5370         if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
5371                 || callingUid == Process.myUid()) {
5372             return unfiltered;
5373         }
5374         UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
5375         if (user != null && user.isRestricted()) {
5376             String[] packages = mPackageManager.getPackagesForUid(callingUid);
5377             // If any of the packages is a white listed package, return the full set,
5378             // otherwise return non-shared accounts only.
5379             // This might be a temporary way to specify a whitelist
5380             String whiteList = mContext.getResources().getString(
5381                     com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
5382             for (String packageName : packages) {
5383                 if (whiteList.contains(";" + packageName + ";")) {
5384                     return unfiltered;
5385                 }
5386             }
5387             ArrayList<Account> allowed = new ArrayList<Account>();
5388             Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
5389             if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
5390             String requiredAccountType = "";
5391             try {
5392                 // If there's an explicit callingPackage specified, check if that package
5393                 // opted in to see restricted accounts.
5394                 if (callingPackage != null) {
5395                     PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
5396                     if (pi != null && pi.restrictedAccountType != null) {
5397                         requiredAccountType = pi.restrictedAccountType;
5398                     }
5399                 } else {
5400                     // Otherwise check if the callingUid has a package that has opted in
5401                     for (String packageName : packages) {
5402                         PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
5403                         if (pi != null && pi.restrictedAccountType != null) {
5404                             requiredAccountType = pi.restrictedAccountType;
5405                             break;
5406                         }
5407                     }
5408                 }
5409             } catch (NameNotFoundException nnfe) {
5410             }
5411             for (Account account : unfiltered) {
5412                 if (account.type.equals(requiredAccountType)) {
5413                     allowed.add(account);
5414                 } else {
5415                     boolean found = false;
5416                     for (Account shared : sharedAccounts) {
5417                         if (shared.equals(account)) {
5418                             found = true;
5419                             break;
5420                         }
5421                     }
5422                     if (!found) {
5423                         allowed.add(account);
5424                     }
5425                 }
5426             }
5427             Account[] filtered = new Account[allowed.size()];
5428             allowed.toArray(filtered);
5429             return filtered;
5430         } else {
5431             return unfiltered;
5432         }
5433     }
5434 
5435     /*
5436      * packageName can be null. If not null, it should be used to filter out restricted accounts
5437      * that the package is not allowed to access.
5438      */
getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, int callingUid, String callingPackage)5439     protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
5440             int callingUid, String callingPackage) {
5441         if (accountType != null) {
5442             final Account[] accounts = userAccounts.accountCache.get(accountType);
5443             if (accounts == null) {
5444                 return EMPTY_ACCOUNT_ARRAY;
5445             } else {
5446                 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
5447                         callingUid, callingPackage);
5448             }
5449         } else {
5450             int totalLength = 0;
5451             for (Account[] accounts : userAccounts.accountCache.values()) {
5452                 totalLength += accounts.length;
5453             }
5454             if (totalLength == 0) {
5455                 return EMPTY_ACCOUNT_ARRAY;
5456             }
5457             Account[] accounts = new Account[totalLength];
5458             totalLength = 0;
5459             for (Account[] accountsOfType : userAccounts.accountCache.values()) {
5460                 System.arraycopy(accountsOfType, 0, accounts, totalLength,
5461                         accountsOfType.length);
5462                 totalLength += accountsOfType.length;
5463             }
5464             return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
5465         }
5466     }
5467 
writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, Account account, String key, String value)5468     protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5469             Account account, String key, String value) {
5470         HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
5471         if (userDataForAccount == null) {
5472             userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
5473             accounts.userDataCache.put(account, userDataForAccount);
5474         }
5475         if (value == null) {
5476             userDataForAccount.remove(key);
5477         } else {
5478             userDataForAccount.put(key, value);
5479         }
5480     }
5481 
readCachedTokenInternal( UserAccounts accounts, Account account, String tokenType, String callingPackage, byte[] pkgSigDigest)5482     protected String readCachedTokenInternal(
5483             UserAccounts accounts,
5484             Account account,
5485             String tokenType,
5486             String callingPackage,
5487             byte[] pkgSigDigest) {
5488         synchronized (accounts.cacheLock) {
5489             return accounts.accountTokenCaches.get(
5490                     account, tokenType, callingPackage, pkgSigDigest);
5491         }
5492     }
5493 
writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, Account account, String key, String value)5494     protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5495             Account account, String key, String value) {
5496         HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
5497         if (authTokensForAccount == null) {
5498             authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
5499             accounts.authTokenCache.put(account, authTokensForAccount);
5500         }
5501         if (value == null) {
5502             authTokensForAccount.remove(key);
5503         } else {
5504             authTokensForAccount.put(key, value);
5505         }
5506     }
5507 
readAuthTokenInternal(UserAccounts accounts, Account account, String authTokenType)5508     protected String readAuthTokenInternal(UserAccounts accounts, Account account,
5509             String authTokenType) {
5510         synchronized (accounts.cacheLock) {
5511             HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
5512             if (authTokensForAccount == null) {
5513                 // need to populate the cache for this account
5514                 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
5515                 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
5516                 accounts.authTokenCache.put(account, authTokensForAccount);
5517             }
5518             return authTokensForAccount.get(authTokenType);
5519         }
5520     }
5521 
readUserDataInternalLocked( UserAccounts accounts, Account account, String key)5522     protected String readUserDataInternalLocked(
5523             UserAccounts accounts, Account account, String key) {
5524         HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
5525         if (userDataForAccount == null) {
5526             // need to populate the cache for this account
5527             final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
5528             userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
5529             accounts.userDataCache.put(account, userDataForAccount);
5530         }
5531         return userDataForAccount.get(key);
5532     }
5533 
readUserDataForAccountFromDatabaseLocked( final SQLiteDatabase db, Account account)5534     protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
5535             final SQLiteDatabase db, Account account) {
5536         HashMap<String, String> userDataForAccount = new HashMap<>();
5537         Cursor cursor = db.query(CE_TABLE_EXTRAS,
5538                 COLUMNS_EXTRAS_KEY_AND_VALUE,
5539                 SELECTION_USERDATA_BY_ACCOUNT,
5540                 new String[]{account.name, account.type},
5541                 null, null, null);
5542         try {
5543             while (cursor.moveToNext()) {
5544                 final String tmpkey = cursor.getString(0);
5545                 final String value = cursor.getString(1);
5546                 userDataForAccount.put(tmpkey, value);
5547             }
5548         } finally {
5549             cursor.close();
5550         }
5551         return userDataForAccount;
5552     }
5553 
readAuthTokensForAccountFromDatabaseLocked( final SQLiteDatabase db, Account account)5554     protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
5555             final SQLiteDatabase db, Account account) {
5556         HashMap<String, String> authTokensForAccount = new HashMap<>();
5557         Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
5558                 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
5559                 SELECTION_AUTHTOKENS_BY_ACCOUNT,
5560                 new String[]{account.name, account.type},
5561                 null, null, null);
5562         try {
5563             while (cursor.moveToNext()) {
5564                 final String type = cursor.getString(0);
5565                 final String authToken = cursor.getString(1);
5566                 authTokensForAccount.put(type, authToken);
5567             }
5568         } finally {
5569             cursor.close();
5570         }
5571         return authTokensForAccount;
5572     }
5573 
getContextForUser(UserHandle user)5574     private Context getContextForUser(UserHandle user) {
5575         try {
5576             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
5577         } catch (NameNotFoundException e) {
5578             // Default to mContext, not finding the package system is running as is unlikely.
5579             return mContext;
5580         }
5581     }
5582 
sendResponse(IAccountManagerResponse response, Bundle result)5583     private void sendResponse(IAccountManagerResponse response, Bundle result) {
5584         try {
5585             response.onResult(result);
5586         } catch (RemoteException e) {
5587             // if the caller is dead then there is no one to care about remote
5588             // exceptions
5589             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5590                 Log.v(TAG, "failure while notifying response", e);
5591             }
5592         }
5593     }
5594 
sendErrorResponse(IAccountManagerResponse response, int errorCode, String errorMessage)5595     private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
5596             String errorMessage) {
5597         try {
5598             response.onError(errorCode, errorMessage);
5599         } catch (RemoteException e) {
5600             // if the caller is dead then there is no one to care about remote
5601             // exceptions
5602             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5603                 Log.v(TAG, "failure while notifying response", e);
5604             }
5605         }
5606     }
5607 }
5608