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