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