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