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