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