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