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