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