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