AccountManagerService.java revision a666d74d4bc7e1298314c516d1309571fb87c212
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.AbstractAccountAuthenticator;
21import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
25import android.accounts.AuthenticatorDescription;
26import android.accounts.CantAddAccountActivity;
27import android.accounts.GrantCredentialsPermissionActivity;
28import android.accounts.IAccountAuthenticator;
29import android.accounts.IAccountAuthenticatorResponse;
30import android.accounts.IAccountManager;
31import android.accounts.IAccountManagerResponse;
32import android.annotation.NonNull;
33import android.app.ActivityManager;
34import android.app.ActivityManagerNative;
35import android.app.AppGlobals;
36import android.app.AppOpsManager;
37import android.app.Notification;
38import android.app.NotificationManager;
39import android.app.PendingIntent;
40import android.app.admin.DeviceAdminInfo;
41import android.app.admin.DevicePolicyManager;
42import android.app.admin.DevicePolicyManagerInternal;
43import android.content.BroadcastReceiver;
44import android.content.ComponentName;
45import android.content.ContentValues;
46import android.content.Context;
47import android.content.Intent;
48import android.content.IntentFilter;
49import android.content.ServiceConnection;
50import android.content.pm.ApplicationInfo;
51import android.content.pm.PackageInfo;
52import android.content.pm.PackageManager;
53import android.content.pm.PackageManager.NameNotFoundException;
54import android.content.pm.RegisteredServicesCache;
55import android.content.pm.RegisteredServicesCacheListener;
56import android.content.pm.ResolveInfo;
57import android.content.pm.Signature;
58import android.content.pm.UserInfo;
59import android.database.Cursor;
60import android.database.DatabaseUtils;
61import android.database.sqlite.SQLiteDatabase;
62import android.database.sqlite.SQLiteOpenHelper;
63import android.database.sqlite.SQLiteStatement;
64import android.os.Binder;
65import android.os.Bundle;
66import android.os.Environment;
67import android.os.Handler;
68import android.os.IBinder;
69import android.os.Looper;
70import android.os.Message;
71import android.os.Parcel;
72import android.os.Process;
73import android.os.RemoteException;
74import android.os.SystemClock;
75import android.os.UserHandle;
76import android.os.UserManager;
77import android.text.TextUtils;
78import android.util.Log;
79import android.util.Pair;
80import android.util.Slog;
81import android.util.SparseArray;
82
83import com.android.internal.R;
84import com.android.internal.util.ArrayUtils;
85import com.android.internal.util.IndentingPrintWriter;
86import com.android.server.FgThread;
87import com.android.server.LocalServices;
88import com.google.android.collect.Lists;
89import com.google.android.collect.Sets;
90
91import java.io.File;
92import java.io.FileDescriptor;
93import java.io.PrintWriter;
94import java.security.GeneralSecurityException;
95import java.security.MessageDigest;
96import java.security.NoSuchAlgorithmException;
97import java.text.SimpleDateFormat;
98import java.util.ArrayList;
99import java.util.Arrays;
100import java.util.Collection;
101import java.util.Date;
102import java.util.HashMap;
103import java.util.HashSet;
104import java.util.LinkedHashMap;
105import java.util.List;
106import java.util.Map;
107import java.util.concurrent.atomic.AtomicInteger;
108import java.util.concurrent.atomic.AtomicReference;
109
110/**
111 * A system service that provides  account, password, and authtoken management for all
112 * accounts on the device. Some of these calls are implemented with the help of the corresponding
113 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
114 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
115 *    AccountManager accountManager = AccountManager.get(context);
116 * @hide
117 */
118public class AccountManagerService
119        extends IAccountManager.Stub
120        implements RegisteredServicesCacheListener<AuthenticatorDescription> {
121
122    private static final String TAG = "AccountManagerService";
123
124    private static final String DATABASE_NAME = "accounts.db";
125    private static final int DATABASE_VERSION = 8;
126
127    private static final int MAX_DEBUG_DB_SIZE = 64;
128
129    private final Context mContext;
130
131    private final PackageManager mPackageManager;
132    private final AppOpsManager mAppOpsManager;
133    private UserManager mUserManager;
134
135    private final MessageHandler mMessageHandler;
136
137    // Messages that can be sent on mHandler
138    private static final int MESSAGE_TIMED_OUT = 3;
139    private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
140
141    private final IAccountAuthenticatorCache mAuthenticatorCache;
142
143    private static final String TABLE_ACCOUNTS = "accounts";
144    private static final String ACCOUNTS_ID = "_id";
145    private static final String ACCOUNTS_NAME = "name";
146    private static final String ACCOUNTS_TYPE = "type";
147    private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
148    private static final String ACCOUNTS_PASSWORD = "password";
149    private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
150    private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
151            "last_password_entry_time_millis_epoch";
152
153    private static final String TABLE_AUTHTOKENS = "authtokens";
154    private static final String AUTHTOKENS_ID = "_id";
155    private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
156    private static final String AUTHTOKENS_TYPE = "type";
157    private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
158
159    private static final String TABLE_GRANTS = "grants";
160    private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
161    private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
162    private static final String GRANTS_GRANTEE_UID = "uid";
163
164    private static final String TABLE_EXTRAS = "extras";
165    private static final String EXTRAS_ID = "_id";
166    private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
167    private static final String EXTRAS_KEY = "key";
168    private static final String EXTRAS_VALUE = "value";
169
170    private static final String TABLE_META = "meta";
171    private static final String META_KEY = "key";
172    private static final String META_VALUE = "value";
173
174    private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
175
176    private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
177            new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
178    private static final Intent ACCOUNTS_CHANGED_INTENT;
179
180    static {
181        ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
182        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
183    }
184
185    private static final String COUNT_OF_MATCHING_GRANTS = ""
186            + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
187            + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
188            + " AND " + GRANTS_GRANTEE_UID + "=?"
189            + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
190            + " AND " + ACCOUNTS_NAME + "=?"
191            + " AND " + ACCOUNTS_TYPE + "=?";
192
193    private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
194            AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
195
196    private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
197            AUTHTOKENS_AUTHTOKEN};
198
199    private static final String SELECTION_USERDATA_BY_ACCOUNT =
200            EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
201    private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
202
203    private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
204    private final AtomicInteger mNotificationIds = new AtomicInteger(1);
205
206    static class UserAccounts {
207        private final int userId;
208        private final DatabaseHelper openHelper;
209        private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
210                credentialsPermissionNotificationIds =
211                new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
212        private final HashMap<Account, Integer> signinRequiredNotificationIds =
213                new HashMap<Account, Integer>();
214        private final Object cacheLock = new Object();
215        /** protected by the {@link #cacheLock} */
216        private final HashMap<String, Account[]> accountCache =
217                new LinkedHashMap<String, Account[]>();
218        /** protected by the {@link #cacheLock} */
219        private final HashMap<Account, HashMap<String, String>> userDataCache =
220                new HashMap<Account, HashMap<String, String>>();
221        /** protected by the {@link #cacheLock} */
222        private final HashMap<Account, HashMap<String, String>> authTokenCache =
223                new HashMap<Account, HashMap<String, String>>();
224
225        /** protected by the {@link #cacheLock} */
226        private final TokenCache accountTokenCaches = new TokenCache();
227
228        /**
229         * protected by the {@link #cacheLock}
230         *
231         * Caches the previous names associated with an account. Previous names
232         * should be cached because we expect that when an Account is renamed,
233         * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
234         * want to know if the accounts they care about have been renamed.
235         *
236         * The previous names are wrapped in an {@link AtomicReference} so that
237         * we can distinguish between those accounts with no previous names and
238         * those whose previous names haven't been cached (yet).
239         */
240        private final HashMap<Account, AtomicReference<String>> previousNameCache =
241                new HashMap<Account, AtomicReference<String>>();
242
243        private int debugDbInsertionPoint = -1;
244        private SQLiteStatement statementForLogging;
245
246        UserAccounts(Context context, int userId) {
247            this.userId = userId;
248            synchronized (cacheLock) {
249                openHelper = new DatabaseHelper(context, userId);
250            }
251        }
252    }
253
254    private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
255
256    private static AtomicReference<AccountManagerService> sThis =
257            new AtomicReference<AccountManagerService>();
258    private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
259
260    /**
261     * This should only be called by system code. One should only call this after the service
262     * has started.
263     * @return a reference to the AccountManagerService instance
264     * @hide
265     */
266    public static AccountManagerService getSingleton() {
267        return sThis.get();
268    }
269
270    public AccountManagerService(Context context) {
271        this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
272    }
273
274    public AccountManagerService(Context context, PackageManager packageManager,
275            IAccountAuthenticatorCache authenticatorCache) {
276        mContext = context;
277        mPackageManager = packageManager;
278        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
279
280        mMessageHandler = new MessageHandler(FgThread.get().getLooper());
281
282        mAuthenticatorCache = authenticatorCache;
283        mAuthenticatorCache.setListener(this, null /* Handler */);
284
285        sThis.set(this);
286
287        IntentFilter intentFilter = new IntentFilter();
288        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
289        intentFilter.addDataScheme("package");
290        mContext.registerReceiver(new BroadcastReceiver() {
291            @Override
292            public void onReceive(Context context1, Intent intent) {
293                // Don't delete accounts when updating a authenticator's
294                // package.
295                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
296                    /* Purging data requires file io, don't block the main thread. This is probably
297                     * less than ideal because we are introducing a race condition where old grants
298                     * could be exercised until they are purged. But that race condition existed
299                     * anyway with the broadcast receiver.
300                     *
301                     * Ideally, we would completely clear the cache, purge data from the database,
302                     * and then rebuild the cache. All under the cache lock. But that change is too
303                     * large at this point.
304                     */
305                    Runnable r = new Runnable() {
306                        @Override
307                        public void run() {
308                            purgeOldGrantsAll();
309                        }
310                    };
311                    new Thread(r).start();
312                }
313            }
314        }, intentFilter);
315
316        IntentFilter userFilter = new IntentFilter();
317        userFilter.addAction(Intent.ACTION_USER_REMOVED);
318        userFilter.addAction(Intent.ACTION_USER_STARTED);
319        mContext.registerReceiverAsUser(new BroadcastReceiver() {
320            @Override
321            public void onReceive(Context context, Intent intent) {
322                String action = intent.getAction();
323                if (Intent.ACTION_USER_REMOVED.equals(action)) {
324                    onUserRemoved(intent);
325                } else if (Intent.ACTION_USER_STARTED.equals(action)) {
326                    onUserStarted(intent);
327                }
328            }
329        }, UserHandle.ALL, userFilter, null, null);
330    }
331
332    @Override
333    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
334            throws RemoteException {
335        try {
336            return super.onTransact(code, data, reply, flags);
337        } catch (RuntimeException e) {
338            // The account manager only throws security exceptions, so let's
339            // log all others.
340            if (!(e instanceof SecurityException)) {
341                Slog.wtf(TAG, "Account Manager Crash", e);
342            }
343            throw e;
344        }
345    }
346
347    public void systemReady() {
348    }
349
350    private UserManager getUserManager() {
351        if (mUserManager == null) {
352            mUserManager = UserManager.get(mContext);
353        }
354        return mUserManager;
355    }
356
357    /**
358     * Validate internal set of accounts against installed authenticators for
359     * given user. Clears cached authenticators before validating.
360     */
361    public void validateAccounts(int userId) {
362        final UserAccounts accounts = getUserAccounts(userId);
363
364        // Invalidate user-specific cache to make sure we catch any
365        // removed authenticators.
366        validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
367    }
368
369    /**
370     * Validate internal set of accounts against installed authenticators for
371     * given user. Clear cached authenticators before validating when requested.
372     */
373    private void validateAccountsInternal(
374            UserAccounts accounts, boolean invalidateAuthenticatorCache) {
375        if (invalidateAuthenticatorCache) {
376            mAuthenticatorCache.invalidateCache(accounts.userId);
377        }
378
379        final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
380        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
381                mAuthenticatorCache.getAllServices(accounts.userId)) {
382            knownAuth.add(service.type);
383        }
384
385        synchronized (accounts.cacheLock) {
386            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
387            boolean accountDeleted = false;
388            Cursor cursor = db.query(TABLE_ACCOUNTS,
389                    new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
390                    null, null, null, null, ACCOUNTS_ID);
391            try {
392                accounts.accountCache.clear();
393                final HashMap<String, ArrayList<String>> accountNamesByType =
394                        new LinkedHashMap<String, ArrayList<String>>();
395                while (cursor.moveToNext()) {
396                    final long accountId = cursor.getLong(0);
397                    final String accountType = cursor.getString(1);
398                    final String accountName = cursor.getString(2);
399
400                    if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
401                        Slog.w(TAG, "deleting account " + accountName + " because type "
402                                + accountType + " no longer has a registered authenticator");
403                        db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
404                        accountDeleted = true;
405
406                        logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
407                                accountId, accounts);
408
409                        final Account account = new Account(accountName, accountType);
410                        accounts.userDataCache.remove(account);
411                        accounts.authTokenCache.remove(account);
412                        accounts.accountTokenCaches.remove(account);
413                    } else {
414                        ArrayList<String> accountNames = accountNamesByType.get(accountType);
415                        if (accountNames == null) {
416                            accountNames = new ArrayList<String>();
417                            accountNamesByType.put(accountType, accountNames);
418                        }
419                        accountNames.add(accountName);
420                    }
421                }
422                for (Map.Entry<String, ArrayList<String>> cur
423                        : accountNamesByType.entrySet()) {
424                    final String accountType = cur.getKey();
425                    final ArrayList<String> accountNames = cur.getValue();
426                    final Account[] accountsForType = new Account[accountNames.size()];
427                    int i = 0;
428                    for (String accountName : accountNames) {
429                        accountsForType[i] = new Account(accountName, accountType);
430                        ++i;
431                    }
432                    accounts.accountCache.put(accountType, accountsForType);
433                }
434            } finally {
435                cursor.close();
436                if (accountDeleted) {
437                    sendAccountsChangedBroadcast(accounts.userId);
438                }
439            }
440        }
441    }
442
443    private UserAccounts getUserAccountsForCaller() {
444        return getUserAccounts(UserHandle.getCallingUserId());
445    }
446
447    protected UserAccounts getUserAccounts(int userId) {
448        synchronized (mUsers) {
449            UserAccounts accounts = mUsers.get(userId);
450            if (accounts == null) {
451                accounts = new UserAccounts(mContext, userId);
452                initializeDebugDbSizeAndCompileSqlStatementForLogging(
453                        accounts.openHelper.getWritableDatabase(), accounts);
454                mUsers.append(userId, accounts);
455                purgeOldGrants(accounts);
456                validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
457            }
458            return accounts;
459        }
460    }
461
462    private void purgeOldGrantsAll() {
463        synchronized (mUsers) {
464            for (int i = 0; i < mUsers.size(); i++) {
465                purgeOldGrants(mUsers.valueAt(i));
466            }
467        }
468    }
469
470    private void purgeOldGrants(UserAccounts accounts) {
471        synchronized (accounts.cacheLock) {
472            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
473            final Cursor cursor = db.query(TABLE_GRANTS,
474                    new String[]{GRANTS_GRANTEE_UID},
475                    null, null, GRANTS_GRANTEE_UID, null, null);
476            try {
477                while (cursor.moveToNext()) {
478                    final int uid = cursor.getInt(0);
479                    final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
480                    if (packageExists) {
481                        continue;
482                    }
483                    Log.d(TAG, "deleting grants for UID " + uid
484                            + " because its package is no longer installed");
485                    db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
486                            new String[]{Integer.toString(uid)});
487                }
488            } finally {
489                cursor.close();
490            }
491        }
492    }
493
494    private void onUserRemoved(Intent intent) {
495        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
496        if (userId < 1) return;
497
498        UserAccounts accounts;
499        synchronized (mUsers) {
500            accounts = mUsers.get(userId);
501            mUsers.remove(userId);
502        }
503        if (accounts == null) {
504            File dbFile = new File(getDatabaseName(userId));
505            dbFile.delete();
506            return;
507        }
508
509        synchronized (accounts.cacheLock) {
510            accounts.openHelper.close();
511            File dbFile = new File(getDatabaseName(userId));
512            dbFile.delete();
513        }
514    }
515
516    private void onUserStarted(Intent intent) {
517        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
518        if (userId < 1) return;
519
520        // Check if there's a shared account that needs to be created as an account
521        Account[] sharedAccounts = getSharedAccountsAsUser(userId);
522        if (sharedAccounts == null || sharedAccounts.length == 0) return;
523        Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
524        int parentUserId = UserManager.isSplitSystemUser()
525                ? mUserManager.getUserInfo(userId).restrictedProfileParentId
526                : UserHandle.USER_SYSTEM;
527        if (parentUserId < 0) {
528            Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
529            return;
530        }
531        for (Account sa : sharedAccounts) {
532            if (ArrayUtils.contains(accounts, sa)) continue;
533            // Account doesn't exist. Copy it now.
534            copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
535        }
536    }
537
538    @Override
539    public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
540        validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
541    }
542
543    @Override
544    public String getPassword(Account account) {
545        int callingUid = Binder.getCallingUid();
546        if (Log.isLoggable(TAG, Log.VERBOSE)) {
547            Log.v(TAG, "getPassword: " + account
548                    + ", caller's uid " + Binder.getCallingUid()
549                    + ", pid " + Binder.getCallingPid());
550        }
551        if (account == null) throw new IllegalArgumentException("account is null");
552        int userId = UserHandle.getCallingUserId();
553        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
554            String msg = String.format(
555                    "uid %s cannot get secrets for accounts of type: %s",
556                    callingUid,
557                    account.type);
558            throw new SecurityException(msg);
559        }
560        long identityToken = clearCallingIdentity();
561        try {
562            UserAccounts accounts = getUserAccounts(userId);
563            return readPasswordInternal(accounts, account);
564        } finally {
565            restoreCallingIdentity(identityToken);
566        }
567    }
568
569    private String readPasswordInternal(UserAccounts accounts, Account account) {
570        if (account == null) {
571            return null;
572        }
573
574        synchronized (accounts.cacheLock) {
575            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
576            Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
577                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
578                    new String[]{account.name, account.type}, null, null, null);
579            try {
580                if (cursor.moveToNext()) {
581                    return cursor.getString(0);
582                }
583                return null;
584            } finally {
585                cursor.close();
586            }
587        }
588    }
589
590    @Override
591    public String getPreviousName(Account account) {
592        if (Log.isLoggable(TAG, Log.VERBOSE)) {
593            Log.v(TAG, "getPreviousName: " + account
594                    + ", caller's uid " + Binder.getCallingUid()
595                    + ", pid " + Binder.getCallingPid());
596        }
597        if (account == null) throw new IllegalArgumentException("account is null");
598        int userId = UserHandle.getCallingUserId();
599        long identityToken = clearCallingIdentity();
600        try {
601            UserAccounts accounts = getUserAccounts(userId);
602            return readPreviousNameInternal(accounts, account);
603        } finally {
604            restoreCallingIdentity(identityToken);
605        }
606    }
607
608    private String readPreviousNameInternal(UserAccounts accounts, Account account) {
609        if  (account == null) {
610            return null;
611        }
612        synchronized (accounts.cacheLock) {
613            AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
614            if (previousNameRef == null) {
615                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
616                Cursor cursor = db.query(
617                        TABLE_ACCOUNTS,
618                        new String[]{ ACCOUNTS_PREVIOUS_NAME },
619                        ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
620                        new String[] { account.name, account.type },
621                        null,
622                        null,
623                        null);
624                try {
625                    if (cursor.moveToNext()) {
626                        String previousName = cursor.getString(0);
627                        previousNameRef = new AtomicReference<String>(previousName);
628                        accounts.previousNameCache.put(account, previousNameRef);
629                        return previousName;
630                    } else {
631                        return null;
632                    }
633                } finally {
634                    cursor.close();
635                }
636            } else {
637                return previousNameRef.get();
638            }
639        }
640    }
641
642    @Override
643    public String getUserData(Account account, String key) {
644        final int callingUid = Binder.getCallingUid();
645        if (Log.isLoggable(TAG, Log.VERBOSE)) {
646            String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
647                    account, key, callingUid, Binder.getCallingPid());
648            Log.v(TAG, msg);
649        }
650        if (account == null) throw new IllegalArgumentException("account is null");
651        if (key == null) throw new IllegalArgumentException("key is null");
652        int userId = UserHandle.getCallingUserId();
653        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
654            String msg = String.format(
655                    "uid %s cannot get user data for accounts of type: %s",
656                    callingUid,
657                    account.type);
658            throw new SecurityException(msg);
659        }
660        long identityToken = clearCallingIdentity();
661        try {
662            UserAccounts accounts = getUserAccounts(userId);
663            synchronized (accounts.cacheLock) {
664                if (!accountExistsCacheLocked(accounts, account)) {
665                    return null;
666                }
667                return readUserDataInternalLocked(accounts, account, key);
668            }
669        } finally {
670            restoreCallingIdentity(identityToken);
671        }
672    }
673
674    @Override
675    public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
676        int callingUid = Binder.getCallingUid();
677        if (Log.isLoggable(TAG, Log.VERBOSE)) {
678            Log.v(TAG, "getAuthenticatorTypes: "
679                    + "for user id " + userId
680                    + "caller's uid " + callingUid
681                    + ", pid " + Binder.getCallingPid());
682        }
683        // Only allow the system process to read accounts of other users
684        if (isCrossUser(callingUid, userId)) {
685            throw new SecurityException(
686                    String.format(
687                            "User %s tying to get authenticator types for %s" ,
688                            UserHandle.getCallingUserId(),
689                            userId));
690        }
691
692        final long identityToken = clearCallingIdentity();
693        try {
694            return getAuthenticatorTypesInternal(userId);
695
696        } finally {
697            restoreCallingIdentity(identityToken);
698        }
699    }
700
701    /**
702     * Should only be called inside of a clearCallingIdentity block.
703     */
704    private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
705        Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
706                authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
707        AuthenticatorDescription[] types =
708                new AuthenticatorDescription[authenticatorCollection.size()];
709        int i = 0;
710        for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
711                : authenticatorCollection) {
712            types[i] = authenticator.type;
713            i++;
714        }
715        return types;
716    }
717
718
719
720    private boolean isCrossUser(int callingUid, int userId) {
721        return (userId != UserHandle.getCallingUserId()
722                && callingUid != Process.myUid()
723                && mContext.checkCallingOrSelfPermission(
724                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
725                                != PackageManager.PERMISSION_GRANTED);
726    }
727
728    @Override
729    public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
730        final int callingUid = Binder.getCallingUid();
731        if (Log.isLoggable(TAG, Log.VERBOSE)) {
732            Log.v(TAG, "addAccountExplicitly: " + account
733                    + ", caller's uid " + callingUid
734                    + ", pid " + Binder.getCallingPid());
735        }
736        if (account == null) throw new IllegalArgumentException("account is null");
737        int userId = UserHandle.getCallingUserId();
738        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
739            String msg = String.format(
740                    "uid %s cannot explicitly add accounts of type: %s",
741                    callingUid,
742                    account.type);
743            throw new SecurityException(msg);
744        }
745        /*
746         * Child users are not allowed to add accounts. Only the accounts that are
747         * shared by the parent profile can be added to child profile.
748         *
749         * TODO: Only allow accounts that were shared to be added by
750         *     a limited user.
751         */
752
753        // fails if the account already exists
754        long identityToken = clearCallingIdentity();
755        try {
756            UserAccounts accounts = getUserAccounts(userId);
757            return addAccountInternal(accounts, account, password, extras, false, callingUid);
758        } finally {
759            restoreCallingIdentity(identityToken);
760        }
761    }
762
763    @Override
764    public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
765            final int userFrom, int userTo) {
766        int callingUid = Binder.getCallingUid();
767        if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
768            throw new SecurityException("Calling copyAccountToUser requires "
769                    + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
770        }
771        final UserAccounts fromAccounts = getUserAccounts(userFrom);
772        final UserAccounts toAccounts = getUserAccounts(userTo);
773        if (fromAccounts == null || toAccounts == null) {
774            if (response != null) {
775                Bundle result = new Bundle();
776                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
777                try {
778                    response.onResult(result);
779                } catch (RemoteException e) {
780                    Slog.w(TAG, "Failed to report error back to the client." + e);
781                }
782            }
783            return;
784        }
785
786        Slog.d(TAG, "Copying account " + account.name
787                + " from user " + userFrom + " to user " + userTo);
788        long identityToken = clearCallingIdentity();
789        try {
790            new Session(fromAccounts, response, account.type, false,
791                    false /* stripAuthTokenFromResult */, account.name,
792                    false /* authDetailsRequired */) {
793                @Override
794                protected String toDebugString(long now) {
795                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
796                            + ", " + account.type;
797                }
798
799                @Override
800                public void run() throws RemoteException {
801                    mAuthenticator.getAccountCredentialsForCloning(this, account);
802                }
803
804                @Override
805                public void onResult(Bundle result) {
806                    if (result != null
807                            && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
808                        // Create a Session for the target user and pass in the bundle
809                        completeCloningAccount(response, result, account, toAccounts, userFrom);
810                    } else {
811                        super.onResult(result);
812                    }
813                }
814            }.bind();
815        } finally {
816            restoreCallingIdentity(identityToken);
817        }
818    }
819
820    @Override
821    public boolean accountAuthenticated(final Account account) {
822        final int callingUid = Binder.getCallingUid();
823        if (Log.isLoggable(TAG, Log.VERBOSE)) {
824            String msg = String.format(
825                    "accountAuthenticated( account: %s, callerUid: %s)",
826                    account,
827                    callingUid);
828            Log.v(TAG, msg);
829        }
830        if (account == null) {
831            throw new IllegalArgumentException("account is null");
832        }
833        int userId = UserHandle.getCallingUserId();
834        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
835            String msg = String.format(
836                    "uid %s cannot notify authentication for accounts of type: %s",
837                    callingUid,
838                    account.type);
839            throw new SecurityException(msg);
840        }
841
842        if (!canUserModifyAccounts(userId, callingUid) ||
843                !canUserModifyAccountsForType(userId, account.type, callingUid)) {
844            return false;
845        }
846
847        long identityToken = clearCallingIdentity();
848        try {
849            UserAccounts accounts = getUserAccounts(userId);
850            return updateLastAuthenticatedTime(account);
851        } finally {
852            restoreCallingIdentity(identityToken);
853        }
854    }
855
856    private boolean updateLastAuthenticatedTime(Account account) {
857        final UserAccounts accounts = getUserAccountsForCaller();
858        synchronized (accounts.cacheLock) {
859            final ContentValues values = new ContentValues();
860            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
861            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
862            int i = db.update(
863                    TABLE_ACCOUNTS,
864                    values,
865                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
866                    new String[] {
867                            account.name, account.type
868                    });
869            if (i > 0) {
870                return true;
871            }
872        }
873        return false;
874    }
875
876    private void completeCloningAccount(IAccountManagerResponse response,
877            final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
878            final int parentUserId){
879        long id = clearCallingIdentity();
880        try {
881            new Session(targetUser, response, account.type, false,
882                    false /* stripAuthTokenFromResult */, account.name,
883                    false /* authDetailsRequired */) {
884                @Override
885                protected String toDebugString(long now) {
886                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
887                            + ", " + account.type;
888                }
889
890                @Override
891                public void run() throws RemoteException {
892                    // Confirm that the owner's account still exists before this step.
893                    UserAccounts owner = getUserAccounts(parentUserId);
894                    synchronized (owner.cacheLock) {
895                        for (Account acc : getAccounts(parentUserId,
896                                mContext.getOpPackageName())) {
897                            if (acc.equals(account)) {
898                                mAuthenticator.addAccountFromCredentials(
899                                        this, account, accountCredentials);
900                                break;
901                            }
902                        }
903                    }
904                }
905
906                @Override
907                public void onResult(Bundle result) {
908                    // TODO: Anything to do if if succedded?
909                    // TODO: If it failed: Show error notification? Should we remove the shadow
910                    // account to avoid retries?
911                    super.onResult(result);
912                }
913
914                @Override
915                public void onError(int errorCode, String errorMessage) {
916                    super.onError(errorCode,  errorMessage);
917                    // TODO: Show error notification to user
918                    // TODO: Should we remove the shadow account so that it doesn't keep trying?
919                }
920
921            }.bind();
922        } finally {
923            restoreCallingIdentity(id);
924        }
925    }
926
927    private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
928            Bundle extras, boolean restricted, int callingUid) {
929        if (account == null) {
930            return false;
931        }
932        synchronized (accounts.cacheLock) {
933            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
934            db.beginTransaction();
935            try {
936                long numMatches = DatabaseUtils.longForQuery(db,
937                        "select count(*) from " + TABLE_ACCOUNTS
938                                + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
939                        new String[]{account.name, account.type});
940                if (numMatches > 0) {
941                    Log.w(TAG, "insertAccountIntoDatabase: " + account
942                            + ", skipping since the account already exists");
943                    return false;
944                }
945                ContentValues values = new ContentValues();
946                values.put(ACCOUNTS_NAME, account.name);
947                values.put(ACCOUNTS_TYPE, account.type);
948                values.put(ACCOUNTS_PASSWORD, password);
949                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
950                long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
951                if (accountId < 0) {
952                    Log.w(TAG, "insertAccountIntoDatabase: " + account
953                            + ", skipping the DB insert failed");
954                    return false;
955                }
956                if (extras != null) {
957                    for (String key : extras.keySet()) {
958                        final String value = extras.getString(key);
959                        if (insertExtraLocked(db, accountId, key, value) < 0) {
960                            Log.w(TAG, "insertAccountIntoDatabase: " + account
961                                    + ", skipping since insertExtra failed for key " + key);
962                            return false;
963                        }
964                    }
965                }
966                db.setTransactionSuccessful();
967
968                logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
969                        accounts, callingUid);
970
971                insertAccountIntoCacheLocked(accounts, account);
972            } finally {
973                db.endTransaction();
974            }
975            sendAccountsChangedBroadcast(accounts.userId);
976        }
977        if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
978            addAccountToLinkedRestrictedUsers(account, accounts.userId);
979        }
980        return true;
981    }
982
983    /**
984     * Adds the account to all linked restricted users as shared accounts. If the user is currently
985     * running, then clone the account too.
986     * @param account the account to share with limited users
987     *
988     */
989    private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
990        List<UserInfo> users = getUserManager().getUsers();
991        for (UserInfo user : users) {
992            if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
993                addSharedAccountAsUser(account, user.id);
994                try {
995                    if (ActivityManagerNative.getDefault().isUserRunning(user.id, 0)) {
996                        mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
997                                MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
998                    }
999                } catch (RemoteException re) {
1000                    // Shouldn't happen
1001                }
1002            }
1003        }
1004    }
1005
1006    private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
1007        ContentValues values = new ContentValues();
1008        values.put(EXTRAS_KEY, key);
1009        values.put(EXTRAS_ACCOUNTS_ID, accountId);
1010        values.put(EXTRAS_VALUE, value);
1011        return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
1012    }
1013
1014    @Override
1015    public void hasFeatures(IAccountManagerResponse response,
1016            Account account, String[] features, String opPackageName) {
1017        int callingUid = Binder.getCallingUid();
1018        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1019            Log.v(TAG, "hasFeatures: " + account
1020                    + ", response " + response
1021                    + ", features " + stringArrayToString(features)
1022                    + ", caller's uid " + callingUid
1023                    + ", pid " + Binder.getCallingPid());
1024        }
1025        if (response == null) throw new IllegalArgumentException("response is null");
1026        if (account == null) throw new IllegalArgumentException("account is null");
1027        if (features == null) throw new IllegalArgumentException("features is null");
1028        int userId = UserHandle.getCallingUserId();
1029        checkReadAccountsPermitted(callingUid, account.type, userId,
1030                opPackageName);
1031
1032        long identityToken = clearCallingIdentity();
1033        try {
1034            UserAccounts accounts = getUserAccounts(userId);
1035            new TestFeaturesSession(accounts, response, account, features).bind();
1036        } finally {
1037            restoreCallingIdentity(identityToken);
1038        }
1039    }
1040
1041    private class TestFeaturesSession extends Session {
1042        private final String[] mFeatures;
1043        private final Account mAccount;
1044
1045        public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
1046                Account account, String[] features) {
1047            super(accounts, response, account.type, false /* expectActivityLaunch */,
1048                    true /* stripAuthTokenFromResult */, account.name,
1049                    false /* authDetailsRequired */);
1050            mFeatures = features;
1051            mAccount = account;
1052        }
1053
1054        @Override
1055        public void run() throws RemoteException {
1056            try {
1057                mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1058            } catch (RemoteException e) {
1059                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1060            }
1061        }
1062
1063        @Override
1064        public void onResult(Bundle result) {
1065            IAccountManagerResponse response = getResponseAndClose();
1066            if (response != null) {
1067                try {
1068                    if (result == null) {
1069                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
1070                        return;
1071                    }
1072                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1073                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1074                                + response);
1075                    }
1076                    final Bundle newResult = new Bundle();
1077                    newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1078                            result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1079                    response.onResult(newResult);
1080                } catch (RemoteException e) {
1081                    // if the caller is dead then there is no one to care about remote exceptions
1082                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1083                        Log.v(TAG, "failure while notifying response", e);
1084                    }
1085                }
1086            }
1087        }
1088
1089        @Override
1090        protected String toDebugString(long now) {
1091            return super.toDebugString(now) + ", hasFeatures"
1092                    + ", " + mAccount
1093                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1094        }
1095    }
1096
1097    @Override
1098    public void renameAccount(
1099            IAccountManagerResponse response, Account accountToRename, String newName) {
1100        final int callingUid = Binder.getCallingUid();
1101        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1102            Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
1103                + ", caller's uid " + callingUid
1104                + ", pid " + Binder.getCallingPid());
1105        }
1106        if (accountToRename == null) throw new IllegalArgumentException("account is null");
1107        int userId = UserHandle.getCallingUserId();
1108        if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
1109            String msg = String.format(
1110                    "uid %s cannot rename accounts of type: %s",
1111                    callingUid,
1112                    accountToRename.type);
1113            throw new SecurityException(msg);
1114        }
1115        long identityToken = clearCallingIdentity();
1116        try {
1117            UserAccounts accounts = getUserAccounts(userId);
1118            Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
1119            Bundle result = new Bundle();
1120            result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1121            result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1122            try {
1123                response.onResult(result);
1124            } catch (RemoteException e) {
1125                Log.w(TAG, e.getMessage());
1126            }
1127        } finally {
1128            restoreCallingIdentity(identityToken);
1129        }
1130    }
1131
1132    private Account renameAccountInternal(
1133            UserAccounts accounts, Account accountToRename, String newName) {
1134        Account resultAccount = null;
1135        /*
1136         * Cancel existing notifications. Let authenticators
1137         * re-post notifications as required. But we don't know if
1138         * the authenticators have bound their notifications to
1139         * now stale account name data.
1140         *
1141         * With a rename api, we might not need to do this anymore but it
1142         * shouldn't hurt.
1143         */
1144        cancelNotification(
1145                getSigninRequiredNotificationId(accounts, accountToRename),
1146                 new UserHandle(accounts.userId));
1147        synchronized(accounts.credentialsPermissionNotificationIds) {
1148            for (Pair<Pair<Account, String>, Integer> pair:
1149                    accounts.credentialsPermissionNotificationIds.keySet()) {
1150                if (accountToRename.equals(pair.first.first)) {
1151                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
1152                    cancelNotification(id, new UserHandle(accounts.userId));
1153                }
1154            }
1155        }
1156        synchronized (accounts.cacheLock) {
1157            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1158            db.beginTransaction();
1159            boolean isSuccessful = false;
1160            Account renamedAccount = new Account(newName, accountToRename.type);
1161            try {
1162                final ContentValues values = new ContentValues();
1163                values.put(ACCOUNTS_NAME, newName);
1164                values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1165                final long accountId = getAccountIdLocked(db, accountToRename);
1166                if (accountId >= 0) {
1167                    final String[] argsAccountId = { String.valueOf(accountId) };
1168                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1169                    db.setTransactionSuccessful();
1170                    isSuccessful = true;
1171                    logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1172                            accounts);
1173                }
1174            } finally {
1175                db.endTransaction();
1176                if (isSuccessful) {
1177                    /*
1178                     * Database transaction was successful. Clean up cached
1179                     * data associated with the account in the user profile.
1180                     */
1181                    insertAccountIntoCacheLocked(accounts, renamedAccount);
1182                    /*
1183                     * Extract the data and token caches before removing the
1184                     * old account to preserve the user data associated with
1185                     * the account.
1186                     */
1187                    HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1188                    HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1189                    removeAccountFromCacheLocked(accounts, accountToRename);
1190                    /*
1191                     * Update the cached data associated with the renamed
1192                     * account.
1193                     */
1194                    accounts.userDataCache.put(renamedAccount, tmpData);
1195                    accounts.authTokenCache.put(renamedAccount, tmpTokens);
1196                    accounts.previousNameCache.put(
1197                          renamedAccount,
1198                          new AtomicReference<String>(accountToRename.name));
1199                    resultAccount = renamedAccount;
1200
1201                    int parentUserId = accounts.userId;
1202                    if (canHaveProfile(parentUserId)) {
1203                        /*
1204                         * Owner or system user account was renamed, rename the account for
1205                         * those users with which the account was shared.
1206                         */
1207                        List<UserInfo> users = mUserManager.getUsers(true);
1208                        for (UserInfo user : users) {
1209                            if (user.isRestricted()
1210                                    && (user.restrictedProfileParentId == parentUserId)) {
1211                                renameSharedAccountAsUser(accountToRename, newName, user.id);
1212                            }
1213                        }
1214                    }
1215                    sendAccountsChangedBroadcast(accounts.userId);
1216                }
1217            }
1218        }
1219        return resultAccount;
1220    }
1221
1222    private boolean canHaveProfile(final int parentUserId) {
1223        final UserInfo userInfo = mUserManager.getUserInfo(parentUserId);
1224        return userInfo != null && userInfo.canHaveProfile();
1225    }
1226
1227    @Override
1228    public void removeAccount(IAccountManagerResponse response, Account account,
1229            boolean expectActivityLaunch) {
1230        removeAccountAsUser(
1231                response,
1232                account,
1233                expectActivityLaunch,
1234                UserHandle.getCallingUserId());
1235    }
1236
1237    @Override
1238    public void removeAccountAsUser(IAccountManagerResponse response, Account account,
1239            boolean expectActivityLaunch, int userId) {
1240        final int callingUid = Binder.getCallingUid();
1241        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1242            Log.v(TAG, "removeAccount: " + account
1243                    + ", response " + response
1244                    + ", caller's uid " + callingUid
1245                    + ", pid " + Binder.getCallingPid()
1246                    + ", for user id " + userId);
1247        }
1248        if (response == null) throw new IllegalArgumentException("response is null");
1249        if (account == null) throw new IllegalArgumentException("account is null");
1250        // Only allow the system process to modify accounts of other users
1251        if (isCrossUser(callingUid, userId)) {
1252            throw new SecurityException(
1253                    String.format(
1254                            "User %s tying remove account for %s" ,
1255                            UserHandle.getCallingUserId(),
1256                            userId));
1257        }
1258        /*
1259         * Only the system or authenticator should be allowed to remove accounts for that
1260         * authenticator.  This will let users remove accounts (via Settings in the system) but not
1261         * arbitrary applications (like competing authenticators).
1262         */
1263        UserHandle user = new UserHandle(userId);
1264        if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1265                && !isSystemUid(callingUid)) {
1266            String msg = String.format(
1267                    "uid %s cannot remove accounts of type: %s",
1268                    callingUid,
1269                    account.type);
1270            throw new SecurityException(msg);
1271        }
1272        if (!canUserModifyAccounts(userId, callingUid)) {
1273            try {
1274                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1275                        "User cannot modify accounts");
1276            } catch (RemoteException re) {
1277            }
1278            return;
1279        }
1280        if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
1281            try {
1282                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1283                        "User cannot modify accounts of this type (policy).");
1284            } catch (RemoteException re) {
1285            }
1286            return;
1287        }
1288        long identityToken = clearCallingIdentity();
1289        UserAccounts accounts = getUserAccounts(userId);
1290        cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
1291        synchronized(accounts.credentialsPermissionNotificationIds) {
1292            for (Pair<Pair<Account, String>, Integer> pair:
1293                accounts.credentialsPermissionNotificationIds.keySet()) {
1294                if (account.equals(pair.first.first)) {
1295                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
1296                    cancelNotification(id, user);
1297                }
1298            }
1299        }
1300
1301        logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1302
1303        try {
1304            new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1305        } finally {
1306            restoreCallingIdentity(identityToken);
1307        }
1308    }
1309
1310    @Override
1311    public boolean removeAccountExplicitly(Account account) {
1312        final int callingUid = Binder.getCallingUid();
1313        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1314            Log.v(TAG, "removeAccountExplicitly: " + account
1315                    + ", caller's uid " + callingUid
1316                    + ", pid " + Binder.getCallingPid());
1317        }
1318        int userId = Binder.getCallingUserHandle().getIdentifier();
1319        if (account == null) {
1320            /*
1321             * Null accounts should result in returning false, as per
1322             * AccountManage.addAccountExplicitly(...) java doc.
1323             */
1324            Log.e(TAG, "account is null");
1325            return false;
1326        } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1327            String msg = String.format(
1328                    "uid %s cannot explicitly add accounts of type: %s",
1329                    callingUid,
1330                    account.type);
1331            throw new SecurityException(msg);
1332        }
1333        UserAccounts accounts = getUserAccountsForCaller();
1334        logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1335        long identityToken = clearCallingIdentity();
1336        try {
1337            return removeAccountInternal(accounts, account, callingUid);
1338        } finally {
1339            restoreCallingIdentity(identityToken);
1340        }
1341    }
1342
1343    private class RemoveAccountSession extends Session {
1344        final Account mAccount;
1345        public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
1346                Account account, boolean expectActivityLaunch) {
1347            super(accounts, response, account.type, expectActivityLaunch,
1348                    true /* stripAuthTokenFromResult */, account.name,
1349                    false /* authDetailsRequired */);
1350            mAccount = account;
1351        }
1352
1353        @Override
1354        protected String toDebugString(long now) {
1355            return super.toDebugString(now) + ", removeAccount"
1356                    + ", account " + mAccount;
1357        }
1358
1359        @Override
1360        public void run() throws RemoteException {
1361            mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1362        }
1363
1364        @Override
1365        public void onResult(Bundle result) {
1366            if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1367                    && !result.containsKey(AccountManager.KEY_INTENT)) {
1368                final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
1369                if (removalAllowed) {
1370                    removeAccountInternal(mAccounts, mAccount, getCallingUid());
1371                }
1372                IAccountManagerResponse response = getResponseAndClose();
1373                if (response != null) {
1374                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1375                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1376                                + response);
1377                    }
1378                    Bundle result2 = new Bundle();
1379                    result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
1380                    try {
1381                        response.onResult(result2);
1382                    } catch (RemoteException e) {
1383                        // ignore
1384                    }
1385                }
1386            }
1387            super.onResult(result);
1388        }
1389    }
1390
1391    /* For testing */
1392    protected void removeAccountInternal(Account account) {
1393        removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
1394    }
1395
1396    private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
1397        int deleted;
1398        synchronized (accounts.cacheLock) {
1399            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1400            final long accountId = getAccountIdLocked(db, account);
1401            deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
1402                    + "=?",
1403                    new String[]{account.name, account.type});
1404            removeAccountFromCacheLocked(accounts, account);
1405            sendAccountsChangedBroadcast(accounts.userId);
1406
1407            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
1408        }
1409        long id = Binder.clearCallingIdentity();
1410        try {
1411            int parentUserId = accounts.userId;
1412            if (canHaveProfile(parentUserId)) {
1413                // Remove from any restricted profiles that are sharing this account.
1414                List<UserInfo> users = mUserManager.getUsers(true);
1415                for (UserInfo user : users) {
1416                    if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
1417                        removeSharedAccountAsUser(account, user.id, callingUid);
1418                    }
1419                }
1420            }
1421        } finally {
1422            Binder.restoreCallingIdentity(id);
1423        }
1424        return (deleted > 0);
1425    }
1426
1427    @Override
1428    public void invalidateAuthToken(String accountType, String authToken) {
1429        int callerUid = Binder.getCallingUid();
1430        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1431            Log.v(TAG, "invalidateAuthToken: accountType " + accountType
1432                    + ", caller's uid " + callerUid
1433                    + ", pid " + Binder.getCallingPid());
1434        }
1435        if (accountType == null) throw new IllegalArgumentException("accountType is null");
1436        if (authToken == null) throw new IllegalArgumentException("authToken is null");
1437        int userId = UserHandle.getCallingUserId();
1438        long identityToken = clearCallingIdentity();
1439        try {
1440            UserAccounts accounts = getUserAccounts(userId);
1441            synchronized (accounts.cacheLock) {
1442                final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1443                db.beginTransaction();
1444                try {
1445                    invalidateAuthTokenLocked(accounts, db, accountType, authToken);
1446                    invalidateCustomTokenLocked(accounts, accountType, authToken);
1447                    db.setTransactionSuccessful();
1448                } finally {
1449                    db.endTransaction();
1450                }
1451            }
1452        } finally {
1453            restoreCallingIdentity(identityToken);
1454        }
1455    }
1456
1457    private void invalidateCustomTokenLocked(
1458            UserAccounts accounts,
1459            String accountType,
1460            String authToken) {
1461        if (authToken == null || accountType == null) {
1462            return;
1463        }
1464        // Also wipe out cached token in memory.
1465        accounts.accountTokenCaches.remove(accountType, authToken);
1466    }
1467
1468    private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1469            String accountType, String authToken) {
1470        if (authToken == null || accountType == null) {
1471            return;
1472        }
1473        Cursor cursor = db.rawQuery(
1474                "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1475                        + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1476                        + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1477                        + " FROM " + TABLE_ACCOUNTS
1478                        + " JOIN " + TABLE_AUTHTOKENS
1479                        + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1480                        + " = " + AUTHTOKENS_ACCOUNTS_ID
1481                        + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
1482                        + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1483                new String[]{authToken, accountType});
1484        try {
1485            while (cursor.moveToNext()) {
1486                long authTokenId = cursor.getLong(0);
1487                String accountName = cursor.getString(1);
1488                String authTokenType = cursor.getString(2);
1489                db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
1490                writeAuthTokenIntoCacheLocked(
1491                        accounts,
1492                        db,
1493                        new Account(accountName, accountType),
1494                        authTokenType,
1495                        null);
1496            }
1497        } finally {
1498            cursor.close();
1499        }
1500    }
1501
1502    private void saveCachedToken(
1503            UserAccounts accounts,
1504            Account account,
1505            String callerPkg,
1506            byte[] callerSigDigest,
1507            String tokenType,
1508            String token,
1509            long expiryMillis) {
1510
1511        if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1512            return;
1513        }
1514        cancelNotification(getSigninRequiredNotificationId(accounts, account),
1515                new UserHandle(accounts.userId));
1516        synchronized (accounts.cacheLock) {
1517            accounts.accountTokenCaches.put(
1518                    account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
1519        }
1520    }
1521
1522    private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1523            String authToken) {
1524        if (account == null || type == null) {
1525            return false;
1526        }
1527        cancelNotification(getSigninRequiredNotificationId(accounts, account),
1528                new UserHandle(accounts.userId));
1529        synchronized (accounts.cacheLock) {
1530            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1531            db.beginTransaction();
1532            try {
1533                long accountId = getAccountIdLocked(db, account);
1534                if (accountId < 0) {
1535                    return false;
1536                }
1537                db.delete(TABLE_AUTHTOKENS,
1538                        AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1539                        new String[]{type});
1540                ContentValues values = new ContentValues();
1541                values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1542                values.put(AUTHTOKENS_TYPE, type);
1543                values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1544                if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1545                    db.setTransactionSuccessful();
1546                    writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
1547                    return true;
1548                }
1549                return false;
1550            } finally {
1551                db.endTransaction();
1552            }
1553        }
1554    }
1555
1556    @Override
1557    public String peekAuthToken(Account account, String authTokenType) {
1558        final int callingUid = Binder.getCallingUid();
1559        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1560            Log.v(TAG, "peekAuthToken: " + account
1561                    + ", authTokenType " + authTokenType
1562                    + ", caller's uid " + callingUid
1563                    + ", pid " + Binder.getCallingPid());
1564        }
1565        if (account == null) throw new IllegalArgumentException("account is null");
1566        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1567        int userId = UserHandle.getCallingUserId();
1568        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1569            String msg = String.format(
1570                    "uid %s cannot peek the authtokens associated with accounts of type: %s",
1571                    callingUid,
1572                    account.type);
1573            throw new SecurityException(msg);
1574        }
1575        long identityToken = clearCallingIdentity();
1576        try {
1577            UserAccounts accounts = getUserAccounts(userId);
1578            return readAuthTokenInternal(accounts, account, authTokenType);
1579        } finally {
1580            restoreCallingIdentity(identityToken);
1581        }
1582    }
1583
1584    @Override
1585    public void setAuthToken(Account account, String authTokenType, String authToken) {
1586        final int callingUid = Binder.getCallingUid();
1587        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1588            Log.v(TAG, "setAuthToken: " + account
1589                    + ", authTokenType " + authTokenType
1590                    + ", caller's uid " + callingUid
1591                    + ", pid " + Binder.getCallingPid());
1592        }
1593        if (account == null) throw new IllegalArgumentException("account is null");
1594        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1595        int userId = UserHandle.getCallingUserId();
1596        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1597            String msg = String.format(
1598                    "uid %s cannot set auth tokens associated with accounts of type: %s",
1599                    callingUid,
1600                    account.type);
1601            throw new SecurityException(msg);
1602        }
1603        long identityToken = clearCallingIdentity();
1604        try {
1605            UserAccounts accounts = getUserAccounts(userId);
1606            saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
1607        } finally {
1608            restoreCallingIdentity(identityToken);
1609        }
1610    }
1611
1612    @Override
1613    public void setPassword(Account account, String password) {
1614        final int callingUid = Binder.getCallingUid();
1615        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1616            Log.v(TAG, "setAuthToken: " + account
1617                    + ", caller's uid " + callingUid
1618                    + ", pid " + Binder.getCallingPid());
1619        }
1620        if (account == null) throw new IllegalArgumentException("account is null");
1621        int userId = UserHandle.getCallingUserId();
1622        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1623            String msg = String.format(
1624                    "uid %s cannot set secrets for accounts of type: %s",
1625                    callingUid,
1626                    account.type);
1627            throw new SecurityException(msg);
1628        }
1629        long identityToken = clearCallingIdentity();
1630        try {
1631            UserAccounts accounts = getUserAccounts(userId);
1632            setPasswordInternal(accounts, account, password, callingUid);
1633        } finally {
1634            restoreCallingIdentity(identityToken);
1635        }
1636    }
1637
1638    private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1639            int callingUid) {
1640        if (account == null) {
1641            return;
1642        }
1643        synchronized (accounts.cacheLock) {
1644            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1645            db.beginTransaction();
1646            try {
1647                final ContentValues values = new ContentValues();
1648                values.put(ACCOUNTS_PASSWORD, password);
1649                final long accountId = getAccountIdLocked(db, account);
1650                if (accountId >= 0) {
1651                    final String[] argsAccountId = {String.valueOf(accountId)};
1652                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1653                    db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
1654                    accounts.authTokenCache.remove(account);
1655                    accounts.accountTokenCaches.remove(account);
1656                    db.setTransactionSuccessful();
1657
1658                    String action = (password == null || password.length() == 0) ?
1659                            DebugDbHelper.ACTION_CLEAR_PASSWORD
1660                            : DebugDbHelper.ACTION_SET_PASSWORD;
1661                    logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
1662                }
1663            } finally {
1664                db.endTransaction();
1665            }
1666            sendAccountsChangedBroadcast(accounts.userId);
1667        }
1668    }
1669
1670    private void sendAccountsChangedBroadcast(int userId) {
1671        Log.i(TAG, "the accounts changed, sending broadcast of "
1672                + ACCOUNTS_CHANGED_INTENT.getAction());
1673        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
1674    }
1675
1676    @Override
1677    public void clearPassword(Account account) {
1678        final int callingUid = Binder.getCallingUid();
1679        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1680            Log.v(TAG, "clearPassword: " + account
1681                    + ", caller's uid " + callingUid
1682                    + ", pid " + Binder.getCallingPid());
1683        }
1684        if (account == null) throw new IllegalArgumentException("account is null");
1685        int userId = UserHandle.getCallingUserId();
1686        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1687            String msg = String.format(
1688                    "uid %s cannot clear passwords for accounts of type: %s",
1689                    callingUid,
1690                    account.type);
1691            throw new SecurityException(msg);
1692        }
1693        long identityToken = clearCallingIdentity();
1694        try {
1695            UserAccounts accounts = getUserAccounts(userId);
1696            setPasswordInternal(accounts, account, null, callingUid);
1697        } finally {
1698            restoreCallingIdentity(identityToken);
1699        }
1700    }
1701
1702    @Override
1703    public void setUserData(Account account, String key, String value) {
1704        final int callingUid = Binder.getCallingUid();
1705        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1706            Log.v(TAG, "setUserData: " + account
1707                    + ", key " + key
1708                    + ", caller's uid " + callingUid
1709                    + ", pid " + Binder.getCallingPid());
1710        }
1711        if (key == null) throw new IllegalArgumentException("key is null");
1712        if (account == null) throw new IllegalArgumentException("account is null");
1713        int userId = UserHandle.getCallingUserId();
1714        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1715            String msg = String.format(
1716                    "uid %s cannot set user data for accounts of type: %s",
1717                    callingUid,
1718                    account.type);
1719            throw new SecurityException(msg);
1720        }
1721        long identityToken = clearCallingIdentity();
1722        try {
1723            UserAccounts accounts = getUserAccounts(userId);
1724            synchronized (accounts.cacheLock) {
1725                if (!accountExistsCacheLocked(accounts, account)) {
1726                    return;
1727                }
1728                setUserdataInternalLocked(accounts, account, key, value);
1729            }
1730        } finally {
1731            restoreCallingIdentity(identityToken);
1732        }
1733    }
1734
1735    private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
1736        if (accounts.accountCache.containsKey(account.type)) {
1737            for (Account acc : getUserAccountsForCaller().accountCache.get(account.type)) {
1738                if (acc.name.equals(account.name)) {
1739                    return true;
1740                }
1741            }
1742        }
1743        return false;
1744    }
1745
1746    private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
1747            String value) {
1748        if (account == null || key == null) {
1749            return;
1750        }
1751        final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1752        db.beginTransaction();
1753        try {
1754            long accountId = getAccountIdLocked(db, account);
1755            if (accountId < 0) {
1756                return;
1757            }
1758            long extrasId = getExtrasIdLocked(db, accountId, key);
1759            if (extrasId < 0) {
1760                extrasId = insertExtraLocked(db, accountId, key, value);
1761                if (extrasId < 0) {
1762                    return;
1763                }
1764            } else {
1765                ContentValues values = new ContentValues();
1766                values.put(EXTRAS_VALUE, value);
1767                if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1768                    return;
1769                }
1770
1771            }
1772            writeUserDataIntoCacheLocked(accounts, db, account, key, value);
1773            db.setTransactionSuccessful();
1774        } finally {
1775            db.endTransaction();
1776        }
1777    }
1778
1779    private void onResult(IAccountManagerResponse response, Bundle result) {
1780        if (result == null) {
1781            Log.e(TAG, "the result is unexpectedly null", new Exception());
1782        }
1783        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1784            Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1785                    + response);
1786        }
1787        try {
1788            response.onResult(result);
1789        } catch (RemoteException e) {
1790            // if the caller is dead then there is no one to care about remote
1791            // exceptions
1792            if (Log.isLoggable(TAG, Log.VERBOSE)) {
1793                Log.v(TAG, "failure while notifying response", e);
1794            }
1795        }
1796    }
1797
1798    @Override
1799    public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1800                                  final String authTokenType)
1801            throws RemoteException {
1802        if (accountType == null) throw new IllegalArgumentException("accountType is null");
1803        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1804
1805        final int callingUid = getCallingUid();
1806        clearCallingIdentity();
1807        if (callingUid != Process.SYSTEM_UID) {
1808            throw new SecurityException("can only call from system");
1809        }
1810        int userId = UserHandle.getUserId(callingUid);
1811        long identityToken = clearCallingIdentity();
1812        try {
1813            UserAccounts accounts = getUserAccounts(userId);
1814            new Session(accounts, response, accountType, false /* expectActivityLaunch */,
1815                    false /* stripAuthTokenFromResult */,  null /* accountName */,
1816                    false /* authDetailsRequired */) {
1817                @Override
1818                protected String toDebugString(long now) {
1819                    return super.toDebugString(now) + ", getAuthTokenLabel"
1820                            + ", " + accountType
1821                            + ", authTokenType " + authTokenType;
1822                }
1823
1824                @Override
1825                public void run() throws RemoteException {
1826                    mAuthenticator.getAuthTokenLabel(this, authTokenType);
1827                }
1828
1829                @Override
1830                public void onResult(Bundle result) {
1831                    if (result != null) {
1832                        String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1833                        Bundle bundle = new Bundle();
1834                        bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1835                        super.onResult(bundle);
1836                        return;
1837                    } else {
1838                        super.onResult(result);
1839                    }
1840                }
1841            }.bind();
1842        } finally {
1843            restoreCallingIdentity(identityToken);
1844        }
1845    }
1846
1847    @Override
1848    public void getAuthToken(
1849            IAccountManagerResponse response,
1850            final Account account,
1851            final String authTokenType,
1852            final boolean notifyOnAuthFailure,
1853            final boolean expectActivityLaunch,
1854            final Bundle loginOptions) {
1855        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1856            Log.v(TAG, "getAuthToken: " + account
1857                    + ", response " + response
1858                    + ", authTokenType " + authTokenType
1859                    + ", notifyOnAuthFailure " + notifyOnAuthFailure
1860                    + ", expectActivityLaunch " + expectActivityLaunch
1861                    + ", caller's uid " + Binder.getCallingUid()
1862                    + ", pid " + Binder.getCallingPid());
1863        }
1864        if (response == null) throw new IllegalArgumentException("response is null");
1865        try {
1866            if (account == null) {
1867                Slog.w(TAG, "getAuthToken called with null account");
1868                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
1869                return;
1870            }
1871            if (authTokenType == null) {
1872                Slog.w(TAG, "getAuthToken called with null authTokenType");
1873                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
1874                return;
1875            }
1876        } catch (RemoteException e) {
1877            Slog.w(TAG, "Failed to report error back to the client." + e);
1878            return;
1879        }
1880        int userId = UserHandle.getCallingUserId();
1881        long ident = Binder.clearCallingIdentity();
1882        final UserAccounts accounts;
1883        final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
1884        try {
1885            accounts = getUserAccounts(userId);
1886            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1887                    AuthenticatorDescription.newKey(account.type), accounts.userId);
1888        } finally {
1889            Binder.restoreCallingIdentity(ident);
1890        }
1891
1892        final boolean customTokens =
1893                authenticatorInfo != null && authenticatorInfo.type.customTokens;
1894
1895        // skip the check if customTokens
1896        final int callerUid = Binder.getCallingUid();
1897        final boolean permissionGranted =
1898                customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
1899
1900        // Get the calling package. We will use it for the purpose of caching.
1901        final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
1902        List<String> callerOwnedPackageNames;
1903        ident = Binder.clearCallingIdentity();
1904        try {
1905            callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
1906        } finally {
1907            Binder.restoreCallingIdentity(ident);
1908        }
1909        if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
1910            String msg = String.format(
1911                    "Uid %s is attempting to illegally masquerade as package %s!",
1912                    callerUid,
1913                    callerPkg);
1914            throw new SecurityException(msg);
1915        }
1916
1917        // let authenticator know the identity of the caller
1918        loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1919        loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
1920
1921        if (notifyOnAuthFailure) {
1922            loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
1923        }
1924
1925        long identityToken = clearCallingIdentity();
1926        try {
1927            // Distill the caller's package signatures into a single digest.
1928            final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
1929
1930            // if the caller has permission, do the peek. otherwise go the more expensive
1931            // route of starting a Session
1932            if (!customTokens && permissionGranted) {
1933                String authToken = readAuthTokenInternal(accounts, account, authTokenType);
1934                if (authToken != null) {
1935                    Bundle result = new Bundle();
1936                    result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1937                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1938                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
1939                    onResult(response, result);
1940                    return;
1941                }
1942            }
1943
1944            if (customTokens) {
1945                /*
1946                 * Look up tokens in the new cache only if the loginOptions don't have parameters
1947                 * outside of those expected to be injected by the AccountManager, e.g.
1948                 * ANDORID_PACKAGE_NAME.
1949                 */
1950                String token = readCachedTokenInternal(
1951                        accounts,
1952                        account,
1953                        authTokenType,
1954                        callerPkg,
1955                        callerPkgSigDigest);
1956                if (token != null) {
1957                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1958                        Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
1959                    }
1960                    Bundle result = new Bundle();
1961                    result.putString(AccountManager.KEY_AUTHTOKEN, token);
1962                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1963                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
1964                    onResult(response, result);
1965                    return;
1966                }
1967            }
1968
1969            new Session(accounts, response, account.type, expectActivityLaunch,
1970                    false /* stripAuthTokenFromResult */, account.name,
1971                    false /* authDetailsRequired */) {
1972                @Override
1973                protected String toDebugString(long now) {
1974                    if (loginOptions != null) loginOptions.keySet();
1975                    return super.toDebugString(now) + ", getAuthToken"
1976                            + ", " + account
1977                            + ", authTokenType " + authTokenType
1978                            + ", loginOptions " + loginOptions
1979                            + ", notifyOnAuthFailure " + notifyOnAuthFailure;
1980                }
1981
1982                @Override
1983                public void run() throws RemoteException {
1984                    // If the caller doesn't have permission then create and return the
1985                    // "grant permission" intent instead of the "getAuthToken" intent.
1986                    if (!permissionGranted) {
1987                        mAuthenticator.getAuthTokenLabel(this, authTokenType);
1988                    } else {
1989                        mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
1990                    }
1991                }
1992
1993                @Override
1994                public void onResult(Bundle result) {
1995                    if (result != null) {
1996                        if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
1997                            Intent intent = newGrantCredentialsPermissionIntent(
1998                                    account,
1999                                    callerUid,
2000                                    new AccountAuthenticatorResponse(this),
2001                                    authTokenType);
2002                            Bundle bundle = new Bundle();
2003                            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
2004                            onResult(bundle);
2005                            return;
2006                        }
2007                        String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
2008                        if (authToken != null) {
2009                            String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2010                            String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
2011                            if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
2012                                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
2013                                        "the type and name should not be empty");
2014                                return;
2015                            }
2016                            Account resultAccount = new Account(name, type);
2017                            if (!customTokens) {
2018                                saveAuthTokenToDatabase(
2019                                        mAccounts,
2020                                        resultAccount,
2021                                        authTokenType,
2022                                        authToken);
2023                            }
2024                            long expiryMillis = result.getLong(
2025                                    AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2026                            if (customTokens
2027                                    && expiryMillis > System.currentTimeMillis()) {
2028                                saveCachedToken(
2029                                        mAccounts,
2030                                        account,
2031                                        callerPkg,
2032                                        callerPkgSigDigest,
2033                                        authTokenType,
2034                                        authToken,
2035                                        expiryMillis);
2036                            }
2037                        }
2038
2039                        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
2040                        if (intent != null && notifyOnAuthFailure && !customTokens) {
2041                            doNotification(mAccounts,
2042                                    account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
2043                                    intent, accounts.userId);
2044                        }
2045                    }
2046                    super.onResult(result);
2047                }
2048            }.bind();
2049        } finally {
2050            restoreCallingIdentity(identityToken);
2051        }
2052    }
2053
2054    private byte[] calculatePackageSignatureDigest(String callerPkg) {
2055        MessageDigest digester;
2056        try {
2057            digester = MessageDigest.getInstance("SHA-256");
2058            PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2059                    callerPkg, PackageManager.GET_SIGNATURES);
2060            for (Signature sig : pkgInfo.signatures) {
2061                digester.update(sig.toByteArray());
2062            }
2063        } catch (NoSuchAlgorithmException x) {
2064            Log.wtf(TAG, "SHA-256 should be available", x);
2065            digester = null;
2066        } catch (NameNotFoundException e) {
2067            Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2068            digester = null;
2069        }
2070        return (digester == null) ? null : digester.digest();
2071    }
2072
2073    private void createNoCredentialsPermissionNotification(Account account, Intent intent,
2074            int userId) {
2075        int uid = intent.getIntExtra(
2076                GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2077        String authTokenType = intent.getStringExtra(
2078                GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
2079        final String titleAndSubtitle =
2080                mContext.getString(R.string.permission_request_notification_with_subtitle,
2081                account.name);
2082        final int index = titleAndSubtitle.indexOf('\n');
2083        String title = titleAndSubtitle;
2084        String subtitle = "";
2085        if (index > 0) {
2086            title = titleAndSubtitle.substring(0, index);
2087            subtitle = titleAndSubtitle.substring(index + 1);
2088        }
2089        UserHandle user = new UserHandle(userId);
2090        Context contextForUser = getContextForUser(user);
2091        Notification n = new Notification.Builder(contextForUser)
2092                .setSmallIcon(android.R.drawable.stat_sys_warning)
2093                .setWhen(0)
2094                .setColor(contextForUser.getColor(
2095                        com.android.internal.R.color.system_notification_accent_color))
2096                .setContentTitle(title)
2097                .setContentText(subtitle)
2098                .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2099                        PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2100                .build();
2101        installNotification(getCredentialPermissionNotificationId(
2102                account, authTokenType, uid), n, user);
2103    }
2104
2105    private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
2106            AccountAuthenticatorResponse response, String authTokenType) {
2107
2108        Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
2109        // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
2110        // Since it was set in Eclair+ we can't change it without breaking apps using
2111        // the intent from a non-Activity context.
2112        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2113        intent.addCategory(
2114                String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
2115
2116        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
2117        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2118        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
2119        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
2120
2121        return intent;
2122    }
2123
2124    private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2125            int uid) {
2126        Integer id;
2127        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
2128        synchronized (accounts.credentialsPermissionNotificationIds) {
2129            final Pair<Pair<Account, String>, Integer> key =
2130                    new Pair<Pair<Account, String>, Integer>(
2131                            new Pair<Account, String>(account, authTokenType), uid);
2132            id = accounts.credentialsPermissionNotificationIds.get(key);
2133            if (id == null) {
2134                id = mNotificationIds.incrementAndGet();
2135                accounts.credentialsPermissionNotificationIds.put(key, id);
2136            }
2137        }
2138        return id;
2139    }
2140
2141    private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
2142        Integer id;
2143        synchronized (accounts.signinRequiredNotificationIds) {
2144            id = accounts.signinRequiredNotificationIds.get(account);
2145            if (id == null) {
2146                id = mNotificationIds.incrementAndGet();
2147                accounts.signinRequiredNotificationIds.put(account, id);
2148            }
2149        }
2150        return id;
2151    }
2152
2153    @Override
2154    public void addAccount(final IAccountManagerResponse response, final String accountType,
2155            final String authTokenType, final String[] requiredFeatures,
2156            final boolean expectActivityLaunch, final Bundle optionsIn) {
2157        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2158            Log.v(TAG, "addAccount: accountType " + accountType
2159                    + ", response " + response
2160                    + ", authTokenType " + authTokenType
2161                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2162                    + ", expectActivityLaunch " + expectActivityLaunch
2163                    + ", caller's uid " + Binder.getCallingUid()
2164                    + ", pid " + Binder.getCallingPid());
2165        }
2166        if (response == null) throw new IllegalArgumentException("response is null");
2167        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2168
2169        // Is user disallowed from modifying accounts?
2170        final int uid = Binder.getCallingUid();
2171        final int userId = UserHandle.getUserId(uid);
2172        if (!canUserModifyAccounts(userId, uid)) {
2173            try {
2174                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2175                        "User is not allowed to add an account!");
2176            } catch (RemoteException re) {
2177            }
2178            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2179            return;
2180        }
2181        if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2182            try {
2183                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2184                        "User cannot modify accounts of this type (policy).");
2185            } catch (RemoteException re) {
2186            }
2187            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2188                    userId);
2189            return;
2190        }
2191
2192        final int pid = Binder.getCallingPid();
2193        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2194        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2195        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2196
2197        int usrId = UserHandle.getCallingUserId();
2198        long identityToken = clearCallingIdentity();
2199        try {
2200            UserAccounts accounts = getUserAccounts(usrId);
2201            logRecordWithUid(
2202                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
2203            new Session(accounts, response, accountType, expectActivityLaunch,
2204                    true /* stripAuthTokenFromResult */, null /* accountName */,
2205                    false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2206                @Override
2207                public void run() throws RemoteException {
2208                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2209                            options);
2210                }
2211
2212                @Override
2213                protected String toDebugString(long now) {
2214                    return super.toDebugString(now) + ", addAccount"
2215                            + ", accountType " + accountType
2216                            + ", requiredFeatures "
2217                            + (requiredFeatures != null
2218                              ? TextUtils.join(",", requiredFeatures)
2219                              : null);
2220                }
2221            }.bind();
2222        } finally {
2223            restoreCallingIdentity(identityToken);
2224        }
2225    }
2226
2227    @Override
2228    public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2229            final String authTokenType, final String[] requiredFeatures,
2230            final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
2231        int callingUid = Binder.getCallingUid();
2232        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2233            Log.v(TAG, "addAccount: accountType " + accountType
2234                    + ", response " + response
2235                    + ", authTokenType " + authTokenType
2236                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2237                    + ", expectActivityLaunch " + expectActivityLaunch
2238                    + ", caller's uid " + Binder.getCallingUid()
2239                    + ", pid " + Binder.getCallingPid()
2240                    + ", for user id " + userId);
2241        }
2242        if (response == null) throw new IllegalArgumentException("response is null");
2243        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2244        // Only allow the system process to add accounts of other users
2245        if (isCrossUser(callingUid, userId)) {
2246            throw new SecurityException(
2247                    String.format(
2248                            "User %s trying to add account for %s" ,
2249                            UserHandle.getCallingUserId(),
2250                            userId));
2251        }
2252
2253        // Is user disallowed from modifying accounts?
2254        if (!canUserModifyAccounts(userId, callingUid)) {
2255            try {
2256                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2257                        "User is not allowed to add an account!");
2258            } catch (RemoteException re) {
2259            }
2260            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2261            return;
2262        }
2263        if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
2264            try {
2265                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2266                        "User cannot modify accounts of this type (policy).");
2267            } catch (RemoteException re) {
2268            }
2269            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2270                    userId);
2271            return;
2272        }
2273
2274        final int pid = Binder.getCallingPid();
2275        final int uid = Binder.getCallingUid();
2276        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2277        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2278        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2279
2280        long identityToken = clearCallingIdentity();
2281        try {
2282            UserAccounts accounts = getUserAccounts(userId);
2283            logRecordWithUid(
2284                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
2285            new Session(accounts, response, accountType, expectActivityLaunch,
2286                    true /* stripAuthTokenFromResult */, null /* accountName */,
2287                    false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2288                @Override
2289                public void run() throws RemoteException {
2290                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2291                            options);
2292                }
2293
2294                @Override
2295                protected String toDebugString(long now) {
2296                    return super.toDebugString(now) + ", addAccount"
2297                            + ", accountType " + accountType
2298                            + ", requiredFeatures "
2299                            + (requiredFeatures != null
2300                              ? TextUtils.join(",", requiredFeatures)
2301                              : null);
2302                }
2303            }.bind();
2304        } finally {
2305            restoreCallingIdentity(identityToken);
2306        }
2307    }
2308
2309    @Override
2310    public void startAddAccountSession(
2311            final IAccountManagerResponse response,
2312            final String accountType,
2313            final String authTokenType,
2314            final String[] requiredFeatures,
2315            final boolean expectActivityLaunch,
2316            final Bundle optionsIn) {
2317        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2318            Log.v(TAG,
2319                    "startAddAccountSession: accountType " + accountType
2320                    + ", response " + response
2321                    + ", authTokenType " + authTokenType
2322                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2323                    + ", expectActivityLaunch " + expectActivityLaunch
2324                    + ", caller's uid " + Binder.getCallingUid()
2325                    + ", pid " + Binder.getCallingPid());
2326        }
2327        if (response == null) {
2328            throw new IllegalArgumentException("response is null");
2329        }
2330        if (accountType == null) {
2331            throw new IllegalArgumentException("accountType is null");
2332        }
2333
2334        final int uid = Binder.getCallingUid();
2335        // Only allow system to start session
2336        if (!isSystemUid(uid)) {
2337            String msg = String.format(
2338                    "uid %s cannot stat add account session.",
2339                    uid);
2340            throw new SecurityException(msg);
2341        }
2342
2343        final int userId = UserHandle.getUserId(uid);
2344        if (!canUserModifyAccounts(userId, uid)) {
2345            try {
2346                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2347                        "User is not allowed to add an account!");
2348            } catch (RemoteException re) {
2349            }
2350            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2351            return;
2352        }
2353        if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2354            try {
2355                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2356                        "User cannot modify accounts of this type (policy).");
2357            } catch (RemoteException re) {
2358            }
2359            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2360                    userId);
2361            return;
2362        }
2363
2364        final int pid = Binder.getCallingPid();
2365        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2366        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2367        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2368
2369        int usrId = UserHandle.getCallingUserId();
2370        long identityToken = clearCallingIdentity();
2371        try {
2372            UserAccounts accounts = getUserAccounts(usrId);
2373            logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2374                    TABLE_ACCOUNTS, uid);
2375            new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
2376                    null /* accountName */, false /* authDetailsRequired */,
2377                    true /* updateLastAuthenticationTime */) {
2378                @Override
2379                public void run() throws RemoteException {
2380                    mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2381                            requiredFeatures, options);
2382                }
2383
2384                @Override
2385                protected String toDebugString(long now) {
2386                    String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2387                    return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2388                            + accountType + ", requiredFeatures "
2389                            + (requiredFeatures != null ? requiredFeaturesStr : null);
2390                }
2391            }.bind();
2392        } finally {
2393            restoreCallingIdentity(identityToken);
2394        }
2395    }
2396
2397    /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2398    private abstract class StartAccountSession extends Session {
2399
2400        public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
2401                String accountType, boolean expectActivityLaunch, String accountName,
2402                boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
2403            super(accounts, response, accountType, expectActivityLaunch,
2404                    true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2405                    updateLastAuthenticationTime);
2406        }
2407
2408        @Override
2409        public void onResult(Bundle result) {
2410            mNumResults++;
2411            Intent intent = null;
2412
2413            if (result != null
2414                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2415                /*
2416                 * The Authenticator API allows third party authenticators to
2417                 * supply arbitrary intents to other apps that they can run,
2418                 * this can be very bad when those apps are in the system like
2419                 * the System Settings.
2420                 */
2421                int authenticatorUid = Binder.getCallingUid();
2422                long bid = Binder.clearCallingIdentity();
2423                try {
2424                    PackageManager pm = mContext.getPackageManager();
2425                    ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
2426                    int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
2427                    if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid,
2428                            targetUid)) {
2429                        throw new SecurityException("Activity to be started with KEY_INTENT must "
2430                                + "share Authenticator's signatures");
2431                    }
2432                } finally {
2433                    Binder.restoreCallingIdentity(bid);
2434                }
2435            }
2436
2437            IAccountManagerResponse response;
2438            if (mExpectActivityLaunch && result != null
2439                    && result.containsKey(AccountManager.KEY_INTENT)) {
2440                response = mResponse;
2441            } else {
2442                response = getResponseAndClose();
2443            }
2444            if (response == null) {
2445                return;
2446            }
2447            if (result == null) {
2448                if (Log.isLoggable(TAG, Log.VERBOSE)) {
2449                    Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2450                            + response);
2451                }
2452                sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2453                        "null bundle returned");
2454                return;
2455            }
2456
2457            if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2458                // All AccountManager error codes are greater
2459                // than 0
2460                sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2461                        result.getString(AccountManager.KEY_ERROR_MESSAGE));
2462                return;
2463            }
2464
2465            // Strip auth token from result.
2466            result.remove(AccountManager.KEY_AUTHTOKEN);
2467
2468            if (Log.isLoggable(TAG, Log.VERBOSE)) {
2469                Log.v(TAG,
2470                        getClass().getSimpleName() + " calling onResult() on response " + response);
2471            }
2472
2473            // Get the session bundle created by authenticator. The
2474            // bundle contains data necessary for finishing the session
2475            // later. The session bundle will be encrypted here and
2476            // decrypted later when trying to finish the session.
2477            Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
2478            if (sessionBundle != null) {
2479                String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2480                if (TextUtils.isEmpty(accountType)
2481                        || !mAccountType.equalsIgnoreCase(accountType)) {
2482                    Log.w(TAG, "Account type in session bundle doesn't match request.");
2483                }
2484                // Add accountType info to session bundle. This will
2485                // override any value set by authenticator.
2486                sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
2487
2488                // Encrypt session bundle before returning to caller.
2489                try {
2490                    CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2491                    Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
2492                    result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
2493                } catch (GeneralSecurityException e) {
2494                    if (Log.isLoggable(TAG, Log.DEBUG)) {
2495                        Log.v(TAG, "Failed to encrypt session bundle!", e);
2496                    }
2497                    sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2498                            "failed to encrypt session bundle");
2499                    return;
2500                }
2501            }
2502
2503            sendResponse(response, result);
2504        }
2505    }
2506
2507    @Override
2508    public void finishSessionAsUser(IAccountManagerResponse response,
2509            @NonNull Bundle sessionBundle,
2510            boolean expectActivityLaunch,
2511            Bundle appInfo,
2512            int userId) {
2513        int callingUid = Binder.getCallingUid();
2514        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2515            Log.v(TAG,
2516                    "finishSession: response "+ response
2517                            + ", expectActivityLaunch " + expectActivityLaunch
2518                            + ", caller's uid " + callingUid
2519                            + ", caller's user id " + UserHandle.getCallingUserId()
2520                            + ", pid " + Binder.getCallingPid()
2521                            + ", for user id " + userId);
2522        }
2523        if (response == null) {
2524            throw new IllegalArgumentException("response is null");
2525        }
2526
2527        // Session bundle is the encrypted bundle of the original bundle created by authenticator.
2528        // Account type is added to it before encryption.
2529        if (sessionBundle == null || sessionBundle.size() == 0) {
2530            throw new IllegalArgumentException("sessionBundle is empty");
2531        }
2532
2533        // Only allow the system process to finish session for other users
2534        if (isCrossUser(callingUid, userId)) {
2535            throw new SecurityException(
2536                    String.format(
2537                            "User %s trying to finish session for %s without cross user permission",
2538                            UserHandle.getCallingUserId(),
2539                            userId));
2540        }
2541
2542        // Only allow system to finish session
2543        if (!isSystemUid(callingUid)) {
2544            String msg = String.format(
2545                    "uid %s cannot finish session because it's not system uid.",
2546                    callingUid);
2547            throw new SecurityException(msg);
2548        }
2549
2550        if (!canUserModifyAccounts(userId, callingUid)) {
2551            sendErrorResponse(response,
2552                    AccountManager.ERROR_CODE_USER_RESTRICTED,
2553                    "User is not allowed to add an account!");
2554            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2555            return;
2556        }
2557
2558        final int pid = Binder.getCallingPid();
2559        final Bundle decryptedBundle;
2560        final String accountType;
2561        // First decrypt session bundle to get account type for checking permission.
2562        try {
2563            CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2564            decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
2565            if (decryptedBundle == null) {
2566                sendErrorResponse(
2567                        response,
2568                        AccountManager.ERROR_CODE_BAD_REQUEST,
2569                        "failed to decrypt session bundle");
2570                return;
2571            }
2572            accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2573            // Account type cannot be null. This should not happen if session bundle was created
2574            // properly by #StartAccountSession.
2575            if (TextUtils.isEmpty(accountType)) {
2576                sendErrorResponse(
2577                        response,
2578                        AccountManager.ERROR_CODE_BAD_ARGUMENTS,
2579                        "accountType is empty");
2580                return;
2581            }
2582
2583            // If by any chances, decryptedBundle contains colliding keys with
2584            // system info
2585            // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
2586            // update credentials flow, we should replace with the new values of the current call.
2587            if (appInfo != null) {
2588                decryptedBundle.putAll(appInfo);
2589            }
2590
2591            // Add info that may be used by add account or update credentials flow.
2592            decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
2593            decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
2594        } catch (GeneralSecurityException e) {
2595            if (Log.isLoggable(TAG, Log.DEBUG)) {
2596                Log.v(TAG, "Failed to decrypt session bundle!", e);
2597            }
2598            sendErrorResponse(
2599                    response,
2600                    AccountManager.ERROR_CODE_BAD_REQUEST,
2601                    "failed to decrypt session bundle");
2602            return;
2603        }
2604
2605        if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
2606            sendErrorResponse(
2607                    response,
2608                    AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2609                    "User cannot modify accounts of this type (policy).");
2610            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2611                    userId);
2612            return;
2613        }
2614
2615        long identityToken = clearCallingIdentity();
2616        try {
2617            UserAccounts accounts = getUserAccounts(userId);
2618            logRecordWithUid(
2619                    accounts,
2620                    DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
2621                    TABLE_ACCOUNTS,
2622                    callingUid);
2623            new Session(
2624                    accounts,
2625                    response,
2626                    accountType,
2627                    expectActivityLaunch,
2628                    true /* stripAuthTokenFromResult */,
2629                    null /* accountName */,
2630                    false /* authDetailsRequired */,
2631                    true /* updateLastAuthenticationTime */) {
2632                @Override
2633                public void run() throws RemoteException {
2634                    mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
2635                }
2636
2637                @Override
2638                protected String toDebugString(long now) {
2639                    return super.toDebugString(now)
2640                            + ", finishSession"
2641                            + ", accountType " + accountType;
2642                }
2643            }.bind();
2644        } finally {
2645            restoreCallingIdentity(identityToken);
2646        }
2647    }
2648
2649    private void showCantAddAccount(int errorCode, int userId) {
2650        Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2651        cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2652        cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2653        long identityToken = clearCallingIdentity();
2654        try {
2655            mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
2656        } finally {
2657            restoreCallingIdentity(identityToken);
2658        }
2659    }
2660
2661    @Override
2662    public void confirmCredentialsAsUser(
2663            IAccountManagerResponse response,
2664            final Account account,
2665            final Bundle options,
2666            final boolean expectActivityLaunch,
2667            int userId) {
2668        int callingUid = Binder.getCallingUid();
2669        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2670            Log.v(TAG, "confirmCredentials: " + account
2671                    + ", response " + response
2672                    + ", expectActivityLaunch " + expectActivityLaunch
2673                    + ", caller's uid " + callingUid
2674                    + ", pid " + Binder.getCallingPid());
2675        }
2676        // Only allow the system process to read accounts of other users
2677        if (isCrossUser(callingUid, userId)) {
2678            throw new SecurityException(
2679                    String.format(
2680                            "User %s trying to confirm account credentials for %s" ,
2681                            UserHandle.getCallingUserId(),
2682                            userId));
2683        }
2684        if (response == null) throw new IllegalArgumentException("response is null");
2685        if (account == null) throw new IllegalArgumentException("account is null");
2686        long identityToken = clearCallingIdentity();
2687        try {
2688            UserAccounts accounts = getUserAccounts(userId);
2689            new Session(accounts, response, account.type, expectActivityLaunch,
2690                    true /* stripAuthTokenFromResult */, account.name,
2691                    true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
2692                @Override
2693                public void run() throws RemoteException {
2694                    mAuthenticator.confirmCredentials(this, account, options);
2695                }
2696                @Override
2697                protected String toDebugString(long now) {
2698                    return super.toDebugString(now) + ", confirmCredentials"
2699                            + ", " + account;
2700                }
2701            }.bind();
2702        } finally {
2703            restoreCallingIdentity(identityToken);
2704        }
2705    }
2706
2707    @Override
2708    public void updateCredentials(IAccountManagerResponse response, final Account account,
2709            final String authTokenType, final boolean expectActivityLaunch,
2710            final Bundle loginOptions) {
2711        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2712            Log.v(TAG, "updateCredentials: " + account
2713                    + ", response " + response
2714                    + ", authTokenType " + authTokenType
2715                    + ", expectActivityLaunch " + expectActivityLaunch
2716                    + ", caller's uid " + Binder.getCallingUid()
2717                    + ", pid " + Binder.getCallingPid());
2718        }
2719        if (response == null) throw new IllegalArgumentException("response is null");
2720        if (account == null) throw new IllegalArgumentException("account is null");
2721        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2722        int userId = UserHandle.getCallingUserId();
2723        long identityToken = clearCallingIdentity();
2724        try {
2725            UserAccounts accounts = getUserAccounts(userId);
2726            new Session(accounts, response, account.type, expectActivityLaunch,
2727                    true /* stripAuthTokenFromResult */, account.name,
2728                    false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
2729                @Override
2730                public void run() throws RemoteException {
2731                    mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
2732                }
2733                @Override
2734                protected String toDebugString(long now) {
2735                    if (loginOptions != null) loginOptions.keySet();
2736                    return super.toDebugString(now) + ", updateCredentials"
2737                            + ", " + account
2738                            + ", authTokenType " + authTokenType
2739                            + ", loginOptions " + loginOptions;
2740                }
2741            }.bind();
2742        } finally {
2743            restoreCallingIdentity(identityToken);
2744        }
2745    }
2746
2747    @Override
2748    public void startUpdateCredentialsSession(
2749            IAccountManagerResponse response,
2750            final Account account,
2751            final String authTokenType,
2752            final boolean expectActivityLaunch,
2753            final Bundle loginOptions) {
2754        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2755            Log.v(TAG,
2756                    "startUpdateCredentialsSession: " + account + ", response " + response
2757                            + ", authTokenType " + authTokenType + ", expectActivityLaunch "
2758                            + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
2759                            + ", pid " + Binder.getCallingPid());
2760        }
2761        if (response == null) {
2762            throw new IllegalArgumentException("response is null");
2763        }
2764        if (account == null) {
2765            throw new IllegalArgumentException("account is null");
2766        }
2767
2768        final int uid = Binder.getCallingUid();
2769        // Only allow system to start session
2770        if (!isSystemUid(uid)) {
2771            String msg = String.format(
2772                    "uid %s cannot start update credentials session.",
2773                    uid);
2774            throw new SecurityException(msg);
2775        }
2776
2777        int userId = UserHandle.getCallingUserId();
2778        long identityToken = clearCallingIdentity();
2779        try {
2780            UserAccounts accounts = getUserAccounts(userId);
2781            new StartAccountSession(
2782                    accounts,
2783                    response,
2784                    account.type,
2785                    expectActivityLaunch,
2786                    account.name,
2787                    false /* authDetailsRequired */,
2788                    true /* updateLastCredentialTime */) {
2789                @Override
2790                public void run() throws RemoteException {
2791                    mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
2792                            loginOptions);
2793                }
2794
2795                @Override
2796                protected String toDebugString(long now) {
2797                    if (loginOptions != null)
2798                        loginOptions.keySet();
2799                    return super.toDebugString(now)
2800                            + ", startUpdateCredentialsSession"
2801                            + ", " + account
2802                            + ", authTokenType " + authTokenType
2803                            + ", loginOptions " + loginOptions;
2804                }
2805            }.bind();
2806        } finally {
2807            restoreCallingIdentity(identityToken);
2808        }
2809    }
2810
2811    @Override
2812    public void isCredentialsUpdateSuggested(
2813            IAccountManagerResponse response,
2814            final Account account,
2815            final String statusToken) {
2816        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2817            Log.v(TAG,
2818                    "isCredentialsUpdateSuggested: " + account + ", response " + response
2819                            + ", caller's uid " + Binder.getCallingUid()
2820                            + ", pid " + Binder.getCallingPid());
2821        }
2822        if (response == null) {
2823            throw new IllegalArgumentException("response is null");
2824        }
2825        if (account == null) {
2826            throw new IllegalArgumentException("account is null");
2827        }
2828        if (TextUtils.isEmpty(statusToken)) {
2829            throw new IllegalArgumentException("status token is empty");
2830        }
2831
2832        int uid = Binder.getCallingUid();
2833        // Only allow system to start session
2834        if (!isSystemUid(uid)) {
2835            String msg = String.format(
2836                    "uid %s cannot stat add account session.",
2837                    uid);
2838            throw new SecurityException(msg);
2839        }
2840
2841        int usrId = UserHandle.getCallingUserId();
2842        long identityToken = clearCallingIdentity();
2843        try {
2844            UserAccounts accounts = getUserAccounts(usrId);
2845            new Session(accounts, response, account.type, false /* expectActivityLaunch */,
2846                    false /* stripAuthTokenFromResult */, account.name,
2847                    false /* authDetailsRequired */) {
2848                @Override
2849                protected String toDebugString(long now) {
2850                    return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
2851                            + ", " + account;
2852                }
2853
2854                @Override
2855                public void run() throws RemoteException {
2856                    mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
2857                }
2858
2859                @Override
2860                public void onResult(Bundle result) {
2861                    IAccountManagerResponse response = getResponseAndClose();
2862                    if (response == null) {
2863                        return;
2864                    }
2865
2866                    if (result == null) {
2867                        sendErrorResponse(
2868                                response,
2869                                AccountManager.ERROR_CODE_INVALID_RESPONSE,
2870                                "null bundle");
2871                        return;
2872                    }
2873
2874                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
2875                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2876                                + response);
2877                    }
2878                    // Check to see if an error occurred. We know if an error occurred because all
2879                    // error codes are greater than 0.
2880                    if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
2881                        sendErrorResponse(response,
2882                                result.getInt(AccountManager.KEY_ERROR_CODE),
2883                                result.getString(AccountManager.KEY_ERROR_MESSAGE));
2884                        return;
2885                    }
2886                    if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
2887                        sendErrorResponse(
2888                                response,
2889                                AccountManager.ERROR_CODE_INVALID_RESPONSE,
2890                                "no result in response");
2891                        return;
2892                    }
2893                    final Bundle newResult = new Bundle();
2894                    newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
2895                            result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
2896                    sendResponse(response, newResult);
2897                }
2898            }.bind();
2899        } finally {
2900            restoreCallingIdentity(identityToken);
2901        }
2902    }
2903
2904    @Override
2905    public void editProperties(IAccountManagerResponse response, final String accountType,
2906            final boolean expectActivityLaunch) {
2907        final int callingUid = Binder.getCallingUid();
2908        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2909            Log.v(TAG, "editProperties: accountType " + accountType
2910                    + ", response " + response
2911                    + ", expectActivityLaunch " + expectActivityLaunch
2912                    + ", caller's uid " + callingUid
2913                    + ", pid " + Binder.getCallingPid());
2914        }
2915        if (response == null) throw new IllegalArgumentException("response is null");
2916        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2917        int userId = UserHandle.getCallingUserId();
2918        if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
2919            String msg = String.format(
2920                    "uid %s cannot edit authenticator properites for account type: %s",
2921                    callingUid,
2922                    accountType);
2923            throw new SecurityException(msg);
2924        }
2925        long identityToken = clearCallingIdentity();
2926        try {
2927            UserAccounts accounts = getUserAccounts(userId);
2928            new Session(accounts, response, accountType, expectActivityLaunch,
2929                    true /* stripAuthTokenFromResult */, null /* accountName */,
2930                    false /* authDetailsRequired */) {
2931                @Override
2932                public void run() throws RemoteException {
2933                    mAuthenticator.editProperties(this, mAccountType);
2934                }
2935                @Override
2936                protected String toDebugString(long now) {
2937                    return super.toDebugString(now) + ", editProperties"
2938                            + ", accountType " + accountType;
2939                }
2940            }.bind();
2941        } finally {
2942            restoreCallingIdentity(identityToken);
2943        }
2944    }
2945
2946    @Override
2947    public boolean someUserHasAccount(@NonNull final Account account) {
2948        if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
2949            throw new SecurityException("Only system can check for accounts across users");
2950        }
2951        final long token = Binder.clearCallingIdentity();
2952        try {
2953            AccountAndUser[] allAccounts = getAllAccounts();
2954            for (int i = allAccounts.length - 1; i >= 0; i--) {
2955                if (allAccounts[i].account.equals(account)) {
2956                    return true;
2957                }
2958            }
2959            return false;
2960        } finally {
2961            Binder.restoreCallingIdentity(token);
2962        }
2963    }
2964
2965    private class GetAccountsByTypeAndFeatureSession extends Session {
2966        private final String[] mFeatures;
2967        private volatile Account[] mAccountsOfType = null;
2968        private volatile ArrayList<Account> mAccountsWithFeatures = null;
2969        private volatile int mCurrentAccount = 0;
2970        private final int mCallingUid;
2971
2972        public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
2973                IAccountManagerResponse response, String type, String[] features, int callingUid) {
2974            super(accounts, response, type, false /* expectActivityLaunch */,
2975                    true /* stripAuthTokenFromResult */, null /* accountName */,
2976                    false /* authDetailsRequired */);
2977            mCallingUid = callingUid;
2978            mFeatures = features;
2979        }
2980
2981        @Override
2982        public void run() throws RemoteException {
2983            synchronized (mAccounts.cacheLock) {
2984                mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
2985                        null);
2986            }
2987            // check whether each account matches the requested features
2988            mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
2989            mCurrentAccount = 0;
2990
2991            checkAccount();
2992        }
2993
2994        public void checkAccount() {
2995            if (mCurrentAccount >= mAccountsOfType.length) {
2996                sendResult();
2997                return;
2998            }
2999
3000            final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3001            if (accountAuthenticator == null) {
3002                // It is possible that the authenticator has died, which is indicated by
3003                // mAuthenticator being set to null. If this happens then just abort.
3004                // There is no need to send back a result or error in this case since
3005                // that already happened when mAuthenticator was cleared.
3006                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3007                    Log.v(TAG, "checkAccount: aborting session since we are no longer"
3008                            + " connected to the authenticator, " + toDebugString());
3009                }
3010                return;
3011            }
3012            try {
3013                accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
3014            } catch (RemoteException e) {
3015                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
3016            }
3017        }
3018
3019        @Override
3020        public void onResult(Bundle result) {
3021            mNumResults++;
3022            if (result == null) {
3023                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
3024                return;
3025            }
3026            if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
3027                mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3028            }
3029            mCurrentAccount++;
3030            checkAccount();
3031        }
3032
3033        public void sendResult() {
3034            IAccountManagerResponse response = getResponseAndClose();
3035            if (response != null) {
3036                try {
3037                    Account[] accounts = new Account[mAccountsWithFeatures.size()];
3038                    for (int i = 0; i < accounts.length; i++) {
3039                        accounts[i] = mAccountsWithFeatures.get(i);
3040                    }
3041                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3042                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3043                                + response);
3044                    }
3045                    Bundle result = new Bundle();
3046                    result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3047                    response.onResult(result);
3048                } catch (RemoteException e) {
3049                    // if the caller is dead then there is no one to care about remote exceptions
3050                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3051                        Log.v(TAG, "failure while notifying response", e);
3052                    }
3053                }
3054            }
3055        }
3056
3057
3058        @Override
3059        protected String toDebugString(long now) {
3060            return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3061                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3062        }
3063    }
3064
3065    /**
3066     * Returns the accounts visible to the client within the context of a specific user
3067     * @hide
3068     */
3069    @NonNull
3070    public Account[] getAccounts(int userId, String opPackageName) {
3071        int callingUid = Binder.getCallingUid();
3072        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3073                opPackageName);
3074        if (visibleAccountTypes.isEmpty()) {
3075            return new Account[0];
3076        }
3077        long identityToken = clearCallingIdentity();
3078        try {
3079            UserAccounts accounts = getUserAccounts(userId);
3080            return getAccountsInternal(
3081                    accounts,
3082                    callingUid,
3083                    null,  // packageName
3084                    visibleAccountTypes);
3085        } finally {
3086            restoreCallingIdentity(identityToken);
3087        }
3088    }
3089
3090    /**
3091     * Returns accounts for all running users.
3092     *
3093     * @hide
3094     */
3095    @NonNull
3096    public AccountAndUser[] getRunningAccounts() {
3097        final int[] runningUserIds;
3098        try {
3099            runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3100        } catch (RemoteException e) {
3101            // Running in system_server; should never happen
3102            throw new RuntimeException(e);
3103        }
3104        return getAccounts(runningUserIds);
3105    }
3106
3107    /** {@hide} */
3108    @NonNull
3109    public AccountAndUser[] getAllAccounts() {
3110        final List<UserInfo> users = getUserManager().getUsers();
3111        final int[] userIds = new int[users.size()];
3112        for (int i = 0; i < userIds.length; i++) {
3113            userIds[i] = users.get(i).id;
3114        }
3115        return getAccounts(userIds);
3116    }
3117
3118    @NonNull
3119    private AccountAndUser[] getAccounts(int[] userIds) {
3120        final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
3121        for (int userId : userIds) {
3122            UserAccounts userAccounts = getUserAccounts(userId);
3123            if (userAccounts == null) continue;
3124            synchronized (userAccounts.cacheLock) {
3125                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3126                        Binder.getCallingUid(), null);
3127                for (int a = 0; a < accounts.length; a++) {
3128                    runningAccounts.add(new AccountAndUser(accounts[a], userId));
3129                }
3130            }
3131        }
3132
3133        AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3134        return runningAccounts.toArray(accountsArray);
3135    }
3136
3137    @Override
3138    @NonNull
3139    public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3140        return getAccountsAsUser(type, userId, null, -1, opPackageName);
3141    }
3142
3143    @NonNull
3144    private Account[] getAccountsAsUser(
3145            String type,
3146            int userId,
3147            String callingPackage,
3148            int packageUid,
3149            String opPackageName) {
3150        int callingUid = Binder.getCallingUid();
3151        // Only allow the system process to read accounts of other users
3152        if (userId != UserHandle.getCallingUserId()
3153                && callingUid != Process.myUid()
3154                && mContext.checkCallingOrSelfPermission(
3155                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3156                    != PackageManager.PERMISSION_GRANTED) {
3157            throw new SecurityException("User " + UserHandle.getCallingUserId()
3158                    + " trying to get account for " + userId);
3159        }
3160
3161        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3162            Log.v(TAG, "getAccounts: accountType " + type
3163                    + ", caller's uid " + Binder.getCallingUid()
3164                    + ", pid " + Binder.getCallingPid());
3165        }
3166        // If the original calling app was using the framework account chooser activity, we'll
3167        // be passed in the original caller's uid here, which is what should be used for filtering.
3168        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3169            callingUid = packageUid;
3170            opPackageName = callingPackage;
3171        }
3172
3173        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3174                opPackageName);
3175        if (visibleAccountTypes.isEmpty()
3176                || (type != null && !visibleAccountTypes.contains(type))) {
3177            return new Account[0];
3178        } else if (visibleAccountTypes.contains(type)) {
3179            // Prune the list down to just the requested type.
3180            visibleAccountTypes = new ArrayList<>();
3181            visibleAccountTypes.add(type);
3182        } // else aggregate all the visible accounts (it won't matter if the
3183          // list is empty).
3184
3185        long identityToken = clearCallingIdentity();
3186        try {
3187            UserAccounts accounts = getUserAccounts(userId);
3188            return getAccountsInternal(
3189                    accounts,
3190                    callingUid,
3191                    callingPackage,
3192                    visibleAccountTypes);
3193        } finally {
3194            restoreCallingIdentity(identityToken);
3195        }
3196    }
3197
3198    @NonNull
3199    private Account[] getAccountsInternal(
3200            UserAccounts userAccounts,
3201            int callingUid,
3202            String callingPackage,
3203            List<String> visibleAccountTypes) {
3204        synchronized (userAccounts.cacheLock) {
3205            ArrayList<Account> visibleAccounts = new ArrayList<>();
3206            for (String visibleType : visibleAccountTypes) {
3207                Account[] accountsForType = getAccountsFromCacheLocked(
3208                        userAccounts, visibleType, callingUid, callingPackage);
3209                if (accountsForType != null) {
3210                    visibleAccounts.addAll(Arrays.asList(accountsForType));
3211                }
3212            }
3213            Account[] result = new Account[visibleAccounts.size()];
3214            for (int i = 0; i < visibleAccounts.size(); i++) {
3215                result[i] = visibleAccounts.get(i);
3216            }
3217            return result;
3218        }
3219    }
3220
3221    @Override
3222    public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3223        checkManageUsersPermission("addSharedAccountsFromParentUser");
3224        Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3225        for (Account account : accounts) {
3226            addSharedAccountAsUser(account, userId);
3227        }
3228    }
3229
3230    private boolean addSharedAccountAsUser(Account account, int userId) {
3231        userId = handleIncomingUser(userId);
3232        UserAccounts accounts = getUserAccounts(userId);
3233        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3234        ContentValues values = new ContentValues();
3235        values.put(ACCOUNTS_NAME, account.name);
3236        values.put(ACCOUNTS_TYPE, account.type);
3237        db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3238                new String[] {account.name, account.type});
3239        long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3240        if (accountId < 0) {
3241            Log.w(TAG, "insertAccountIntoDatabase: " + account
3242                    + ", skipping the DB insert failed");
3243            return false;
3244        }
3245        logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
3246        return true;
3247    }
3248
3249    @Override
3250    public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3251        userId = handleIncomingUser(userId);
3252        UserAccounts accounts = getUserAccounts(userId);
3253        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3254        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3255        final ContentValues values = new ContentValues();
3256        values.put(ACCOUNTS_NAME, newName);
3257        values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
3258        int r = db.update(
3259                TABLE_SHARED_ACCOUNTS,
3260                values,
3261                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3262                new String[] { account.name, account.type });
3263        if (r > 0) {
3264            int callingUid = getCallingUid();
3265            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3266                    sharedTableAccountId, accounts, callingUid);
3267            // Recursively rename the account.
3268            renameAccountInternal(accounts, account, newName);
3269        }
3270        return r > 0;
3271    }
3272
3273    @Override
3274    public boolean removeSharedAccountAsUser(Account account, int userId) {
3275        return removeSharedAccountAsUser(account, userId, getCallingUid());
3276    }
3277
3278    private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
3279        userId = handleIncomingUser(userId);
3280        UserAccounts accounts = getUserAccounts(userId);
3281        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3282        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3283        int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3284                new String[] {account.name, account.type});
3285        if (r > 0) {
3286            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3287                    sharedTableAccountId, accounts, callingUid);
3288            removeAccountInternal(accounts, account, callingUid);
3289        }
3290        return r > 0;
3291    }
3292
3293    @Override
3294    public Account[] getSharedAccountsAsUser(int userId) {
3295        userId = handleIncomingUser(userId);
3296        UserAccounts accounts = getUserAccounts(userId);
3297        ArrayList<Account> accountList = new ArrayList<Account>();
3298        Cursor cursor = null;
3299        try {
3300            cursor = accounts.openHelper.getReadableDatabase()
3301                    .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3302                    null, null, null, null, null);
3303            if (cursor != null && cursor.moveToFirst()) {
3304                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3305                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3306                do {
3307                    accountList.add(new Account(cursor.getString(nameIndex),
3308                            cursor.getString(typeIndex)));
3309                } while (cursor.moveToNext());
3310            }
3311        } finally {
3312            if (cursor != null) {
3313                cursor.close();
3314            }
3315        }
3316        Account[] accountArray = new Account[accountList.size()];
3317        accountList.toArray(accountArray);
3318        return accountArray;
3319    }
3320
3321    @Override
3322    @NonNull
3323    public Account[] getAccounts(String type, String opPackageName) {
3324        return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
3325    }
3326
3327    @Override
3328    @NonNull
3329    public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
3330        int callingUid = Binder.getCallingUid();
3331        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3332            throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3333                    + callingUid + " with uid=" + uid);
3334        }
3335        return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3336                opPackageName);
3337    }
3338
3339    @Override
3340    @NonNull
3341    public Account[] getAccountsByTypeForPackage(String type, String packageName,
3342            String opPackageName) {
3343        int packageUid = -1;
3344        try {
3345            packageUid = AppGlobals.getPackageManager().getPackageUid(
3346                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3347                    UserHandle.getCallingUserId());
3348        } catch (RemoteException re) {
3349            Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3350            return new Account[0];
3351        }
3352        return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3353                packageUid, opPackageName);
3354    }
3355
3356    @Override
3357    public void getAccountsByFeatures(
3358            IAccountManagerResponse response,
3359            String type,
3360            String[] features,
3361            String opPackageName) {
3362        int callingUid = Binder.getCallingUid();
3363        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3364            Log.v(TAG, "getAccounts: accountType " + type
3365                    + ", response " + response
3366                    + ", features " + stringArrayToString(features)
3367                    + ", caller's uid " + callingUid
3368                    + ", pid " + Binder.getCallingPid());
3369        }
3370        if (response == null) throw new IllegalArgumentException("response is null");
3371        if (type == null) throw new IllegalArgumentException("accountType is null");
3372        int userId = UserHandle.getCallingUserId();
3373
3374        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3375                opPackageName);
3376        if (!visibleAccountTypes.contains(type)) {
3377            Bundle result = new Bundle();
3378            // Need to return just the accounts that are from matching signatures.
3379            result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
3380            try {
3381                response.onResult(result);
3382            } catch (RemoteException e) {
3383                Log.e(TAG, "Cannot respond to caller do to exception." , e);
3384            }
3385            return;
3386        }
3387        long identityToken = clearCallingIdentity();
3388        try {
3389            UserAccounts userAccounts = getUserAccounts(userId);
3390            if (features == null || features.length == 0) {
3391                Account[] accounts;
3392                synchronized (userAccounts.cacheLock) {
3393                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
3394                }
3395                Bundle result = new Bundle();
3396                result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3397                onResult(response, result);
3398                return;
3399            }
3400            new GetAccountsByTypeAndFeatureSession(
3401                    userAccounts,
3402                    response,
3403                    type,
3404                    features,
3405                    callingUid).bind();
3406        } finally {
3407            restoreCallingIdentity(identityToken);
3408        }
3409    }
3410
3411    private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
3412        Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
3413                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3414        try {
3415            if (cursor.moveToNext()) {
3416                return cursor.getLong(0);
3417            }
3418            return -1;
3419        } finally {
3420            cursor.close();
3421        }
3422    }
3423
3424    private long getAccountIdLocked(SQLiteDatabase db, Account account) {
3425        Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
3426                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3427        try {
3428            if (cursor.moveToNext()) {
3429                return cursor.getLong(0);
3430            }
3431            return -1;
3432        } finally {
3433            cursor.close();
3434        }
3435    }
3436
3437    private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
3438        Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
3439                EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
3440                new String[]{key}, null, null, null);
3441        try {
3442            if (cursor.moveToNext()) {
3443                return cursor.getLong(0);
3444            }
3445            return -1;
3446        } finally {
3447            cursor.close();
3448        }
3449    }
3450
3451    private abstract class Session extends IAccountAuthenticatorResponse.Stub
3452            implements IBinder.DeathRecipient, ServiceConnection {
3453        IAccountManagerResponse mResponse;
3454        final String mAccountType;
3455        final boolean mExpectActivityLaunch;
3456        final long mCreationTime;
3457        final String mAccountName;
3458        // Indicates if we need to add auth details(like last credential time)
3459        final boolean mAuthDetailsRequired;
3460        // If set, we need to update the last authenticated time. This is
3461        // currently
3462        // used on
3463        // successful confirming credentials.
3464        final boolean mUpdateLastAuthenticatedTime;
3465
3466        public int mNumResults = 0;
3467        private int mNumRequestContinued = 0;
3468        private int mNumErrors = 0;
3469
3470        IAccountAuthenticator mAuthenticator = null;
3471
3472        private final boolean mStripAuthTokenFromResult;
3473        protected final UserAccounts mAccounts;
3474
3475        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3476                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3477                boolean authDetailsRequired) {
3478            this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
3479                    accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
3480        }
3481
3482        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3483                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3484                boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
3485            super();
3486            //if (response == null) throw new IllegalArgumentException("response is null");
3487            if (accountType == null) throw new IllegalArgumentException("accountType is null");
3488            mAccounts = accounts;
3489            mStripAuthTokenFromResult = stripAuthTokenFromResult;
3490            mResponse = response;
3491            mAccountType = accountType;
3492            mExpectActivityLaunch = expectActivityLaunch;
3493            mCreationTime = SystemClock.elapsedRealtime();
3494            mAccountName = accountName;
3495            mAuthDetailsRequired = authDetailsRequired;
3496            mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
3497
3498            synchronized (mSessions) {
3499                mSessions.put(toString(), this);
3500            }
3501            if (response != null) {
3502                try {
3503                    response.asBinder().linkToDeath(this, 0 /* flags */);
3504                } catch (RemoteException e) {
3505                    mResponse = null;
3506                    binderDied();
3507                }
3508            }
3509        }
3510
3511        IAccountManagerResponse getResponseAndClose() {
3512            if (mResponse == null) {
3513                // this session has already been closed
3514                return null;
3515            }
3516            IAccountManagerResponse response = mResponse;
3517            close(); // this clears mResponse so we need to save the response before this call
3518            return response;
3519        }
3520
3521        private void close() {
3522            synchronized (mSessions) {
3523                if (mSessions.remove(toString()) == null) {
3524                    // the session was already closed, so bail out now
3525                    return;
3526                }
3527            }
3528            if (mResponse != null) {
3529                // stop listening for response deaths
3530                mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
3531
3532                // clear this so that we don't accidentally send any further results
3533                mResponse = null;
3534            }
3535            cancelTimeout();
3536            unbind();
3537        }
3538
3539        @Override
3540        public void binderDied() {
3541            mResponse = null;
3542            close();
3543        }
3544
3545        protected String toDebugString() {
3546            return toDebugString(SystemClock.elapsedRealtime());
3547        }
3548
3549        protected String toDebugString(long now) {
3550            return "Session: expectLaunch " + mExpectActivityLaunch
3551                    + ", connected " + (mAuthenticator != null)
3552                    + ", stats (" + mNumResults + "/" + mNumRequestContinued
3553                    + "/" + mNumErrors + ")"
3554                    + ", lifetime " + ((now - mCreationTime) / 1000.0);
3555        }
3556
3557        void bind() {
3558            if (Log.isLoggable(TAG, Log.VERBOSE)) {
3559                Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
3560            }
3561            if (!bindToAuthenticator(mAccountType)) {
3562                Log.d(TAG, "bind attempt failed for " + toDebugString());
3563                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
3564            }
3565        }
3566
3567        private void unbind() {
3568            if (mAuthenticator != null) {
3569                mAuthenticator = null;
3570                mContext.unbindService(this);
3571            }
3572        }
3573
3574        public void cancelTimeout() {
3575            mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
3576        }
3577
3578        @Override
3579        public void onServiceConnected(ComponentName name, IBinder service) {
3580            mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
3581            try {
3582                run();
3583            } catch (RemoteException e) {
3584                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3585                        "remote exception");
3586            }
3587        }
3588
3589        @Override
3590        public void onServiceDisconnected(ComponentName name) {
3591            mAuthenticator = null;
3592            IAccountManagerResponse response = getResponseAndClose();
3593            if (response != null) {
3594                try {
3595                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3596                            "disconnected");
3597                } catch (RemoteException e) {
3598                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3599                        Log.v(TAG, "Session.onServiceDisconnected: "
3600                                + "caught RemoteException while responding", e);
3601                    }
3602                }
3603            }
3604        }
3605
3606        public abstract void run() throws RemoteException;
3607
3608        public void onTimedOut() {
3609            IAccountManagerResponse response = getResponseAndClose();
3610            if (response != null) {
3611                try {
3612                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3613                            "timeout");
3614                } catch (RemoteException e) {
3615                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3616                        Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
3617                                e);
3618                    }
3619                }
3620            }
3621        }
3622
3623        @Override
3624        public void onResult(Bundle result) {
3625            mNumResults++;
3626            Intent intent = null;
3627            if (result != null) {
3628                boolean isSuccessfulConfirmCreds = result.getBoolean(
3629                        AccountManager.KEY_BOOLEAN_RESULT, false);
3630                boolean isSuccessfulUpdateCredsOrAddAccount =
3631                        result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
3632                        && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
3633                // We should only update lastAuthenticated time, if
3634                // mUpdateLastAuthenticatedTime is true and the confirmRequest
3635                // or updateRequest was successful
3636                boolean needUpdate = mUpdateLastAuthenticatedTime
3637                        && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
3638                if (needUpdate || mAuthDetailsRequired) {
3639                    boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
3640                    if (needUpdate && accountPresent) {
3641                        updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
3642                    }
3643                    if (mAuthDetailsRequired) {
3644                        long lastAuthenticatedTime = -1;
3645                        if (accountPresent) {
3646                            lastAuthenticatedTime = DatabaseUtils.longForQuery(
3647                                    mAccounts.openHelper.getReadableDatabase(),
3648                                    "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
3649                                            + " from " +
3650                                            TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
3651                                            + ACCOUNTS_TYPE + "=?",
3652                                    new String[] {
3653                                            mAccountName, mAccountType
3654                                    });
3655                        }
3656                        result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
3657                                lastAuthenticatedTime);
3658                    }
3659                }
3660            }
3661            if (result != null
3662                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
3663                /*
3664                 * The Authenticator API allows third party authenticators to
3665                 * supply arbitrary intents to other apps that they can run,
3666                 * this can be very bad when those apps are in the system like
3667                 * the System Settings.
3668                 */
3669                int authenticatorUid = Binder.getCallingUid();
3670                long bid = Binder.clearCallingIdentity();
3671                try {
3672                    PackageManager pm = mContext.getPackageManager();
3673                    ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
3674                    int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
3675                    if (PackageManager.SIGNATURE_MATCH !=
3676                            pm.checkSignatures(authenticatorUid, targetUid)) {
3677                        throw new SecurityException(
3678                                "Activity to be started with KEY_INTENT must " +
3679                               "share Authenticator's signatures");
3680                    }
3681                } finally {
3682                    Binder.restoreCallingIdentity(bid);
3683                }
3684            }
3685            if (result != null
3686                    && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
3687                String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
3688                String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
3689                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
3690                    Account account = new Account(accountName, accountType);
3691                    cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
3692                            new UserHandle(mAccounts.userId));
3693                }
3694            }
3695            IAccountManagerResponse response;
3696            if (mExpectActivityLaunch && result != null
3697                    && result.containsKey(AccountManager.KEY_INTENT)) {
3698                response = mResponse;
3699            } else {
3700                response = getResponseAndClose();
3701            }
3702            if (response != null) {
3703                try {
3704                    if (result == null) {
3705                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3706                            Log.v(TAG, getClass().getSimpleName()
3707                                    + " calling onError() on response " + response);
3708                        }
3709                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
3710                                "null bundle returned");
3711                    } else {
3712                        if (mStripAuthTokenFromResult) {
3713                            result.remove(AccountManager.KEY_AUTHTOKEN);
3714                        }
3715                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3716                            Log.v(TAG, getClass().getSimpleName()
3717                                    + " calling onResult() on response " + response);
3718                        }
3719                        if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
3720                                (intent == null)) {
3721                            // All AccountManager error codes are greater than 0
3722                            response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
3723                                    result.getString(AccountManager.KEY_ERROR_MESSAGE));
3724                        } else {
3725                            response.onResult(result);
3726                        }
3727                    }
3728                } catch (RemoteException e) {
3729                    // if the caller is dead then there is no one to care about remote exceptions
3730                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3731                        Log.v(TAG, "failure while notifying response", e);
3732                    }
3733                }
3734            }
3735        }
3736
3737        @Override
3738        public void onRequestContinued() {
3739            mNumRequestContinued++;
3740        }
3741
3742        @Override
3743        public void onError(int errorCode, String errorMessage) {
3744            mNumErrors++;
3745            IAccountManagerResponse response = getResponseAndClose();
3746            if (response != null) {
3747                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3748                    Log.v(TAG, getClass().getSimpleName()
3749                            + " calling onError() on response " + response);
3750                }
3751                try {
3752                    response.onError(errorCode, errorMessage);
3753                } catch (RemoteException e) {
3754                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3755                        Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
3756                    }
3757                }
3758            } else {
3759                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3760                    Log.v(TAG, "Session.onError: already closed");
3761                }
3762            }
3763        }
3764
3765        /**
3766         * find the component name for the authenticator and initiate a bind
3767         * if no authenticator or the bind fails then return false, otherwise return true
3768         */
3769        private boolean bindToAuthenticator(String authenticatorType) {
3770            final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
3771            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
3772                    AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
3773            if (authenticatorInfo == null) {
3774                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3775                    Log.v(TAG, "there is no authenticator for " + authenticatorType
3776                            + ", bailing out");
3777                }
3778                return false;
3779            }
3780
3781            final ActivityManager am = mContext.getSystemService(ActivityManager.class);
3782            if (am.isUserRunningAndLocked(mAccounts.userId)
3783                    && !authenticatorInfo.componentInfo.encryptionAware) {
3784                Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
3785                        + " which isn't encryption aware");
3786                return false;
3787            }
3788
3789            Intent intent = new Intent();
3790            intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
3791            intent.setComponent(authenticatorInfo.componentName);
3792            if (Log.isLoggable(TAG, Log.VERBOSE)) {
3793                Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
3794            }
3795            if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
3796                    new UserHandle(mAccounts.userId))) {
3797                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3798                    Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
3799                }
3800                return false;
3801            }
3802
3803            return true;
3804        }
3805    }
3806
3807    private class MessageHandler extends Handler {
3808        MessageHandler(Looper looper) {
3809            super(looper);
3810        }
3811
3812        @Override
3813        public void handleMessage(Message msg) {
3814            switch (msg.what) {
3815                case MESSAGE_TIMED_OUT:
3816                    Session session = (Session)msg.obj;
3817                    session.onTimedOut();
3818                    break;
3819
3820                case MESSAGE_COPY_SHARED_ACCOUNT:
3821                    copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
3822                    break;
3823
3824                default:
3825                    throw new IllegalStateException("unhandled message: " + msg.what);
3826            }
3827        }
3828    }
3829
3830    private static String getDatabaseName(int userId) {
3831        File systemDir = Environment.getDataSystemDirectory();
3832        File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
3833        if (userId == 0) {
3834            // Migrate old file, if it exists, to the new location.
3835            // Make sure the new file doesn't already exist. A dummy file could have been
3836            // accidentally created in the old location, causing the new one to become corrupted
3837            // as well.
3838            File oldFile = new File(systemDir, DATABASE_NAME);
3839            if (oldFile.exists() && !databaseFile.exists()) {
3840                // Check for use directory; create if it doesn't exist, else renameTo will fail
3841                File userDir = Environment.getUserSystemDirectory(userId);
3842                if (!userDir.exists()) {
3843                    if (!userDir.mkdirs()) {
3844                        throw new IllegalStateException("User dir cannot be created: " + userDir);
3845                    }
3846                }
3847                if (!oldFile.renameTo(databaseFile)) {
3848                    throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
3849                }
3850            }
3851        }
3852        return databaseFile.getPath();
3853    }
3854
3855    private static class DebugDbHelper{
3856        private DebugDbHelper() {
3857        }
3858
3859        private static String TABLE_DEBUG = "debug_table";
3860
3861        // Columns for the table
3862        private static String ACTION_TYPE = "action_type";
3863        private static String TIMESTAMP = "time";
3864        private static String CALLER_UID = "caller_uid";
3865        private static String TABLE_NAME = "table_name";
3866        private static String KEY = "primary_key";
3867
3868        // These actions correspond to the occurrence of real actions. Since
3869        // these are called by the authenticators, the uid associated will be
3870        // of the authenticator.
3871        private static String ACTION_SET_PASSWORD = "action_set_password";
3872        private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
3873        private static String ACTION_ACCOUNT_ADD = "action_account_add";
3874        private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
3875        private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
3876        private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
3877
3878        // These actions don't necessarily correspond to any action on
3879        // accountDb taking place. As an example, there might be a request for
3880        // addingAccount, which might not lead to addition of account on grounds
3881        // of bad authentication. We will still be logging it to keep track of
3882        // who called.
3883        private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
3884        private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
3885
3886        //This action doesn't add account to accountdb. Account is only
3887        // added in finishSession which may be in a different user profile.
3888        private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
3889        private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
3890                "action_called_account_session_finish";
3891
3892        private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3893
3894        private static void createDebugTable(SQLiteDatabase db) {
3895            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
3896                    + ACCOUNTS_ID + " INTEGER,"
3897                    + ACTION_TYPE + " TEXT NOT NULL, "
3898                    + TIMESTAMP + " DATETIME,"
3899                    + CALLER_UID + " INTEGER NOT NULL,"
3900                    + TABLE_NAME + " TEXT NOT NULL,"
3901                    + KEY + " INTEGER PRIMARY KEY)");
3902            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
3903        }
3904    }
3905
3906    private void logRecord(UserAccounts accounts, String action, String tableName) {
3907        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3908        logRecord(db, action, tableName, -1, accounts);
3909    }
3910
3911    private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
3912        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3913        logRecord(db, action, tableName, -1, accounts, uid);
3914    }
3915
3916    /*
3917     * This function receives an opened writable database.
3918     */
3919    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3920            UserAccounts userAccount) {
3921        logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
3922    }
3923
3924    /*
3925     * This function receives an opened writable database.
3926     */
3927    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3928            UserAccounts userAccount, int callingUid) {
3929        SQLiteStatement logStatement = userAccount.statementForLogging;
3930        logStatement.bindLong(1, accountId);
3931        logStatement.bindString(2, action);
3932        logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
3933        logStatement.bindLong(4, callingUid);
3934        logStatement.bindString(5, tableName);
3935        logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
3936        logStatement.execute();
3937        logStatement.clearBindings();
3938        userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
3939                % MAX_DEBUG_DB_SIZE;
3940    }
3941
3942    /*
3943     * This should only be called once to compile the sql statement for logging
3944     * and to find the insertion point.
3945     */
3946    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
3947            UserAccounts userAccount) {
3948        // Initialize the count if not done earlier.
3949        int size = (int) getDebugTableRowCount(db);
3950        if (size >= MAX_DEBUG_DB_SIZE) {
3951            // Table is full, and we need to find the point where to insert.
3952            userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
3953        } else {
3954            userAccount.debugDbInsertionPoint = size;
3955        }
3956        compileSqlStatementForLogging(db, userAccount);
3957    }
3958
3959    private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
3960        String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
3961                + " VALUES (?,?,?,?,?,?)";
3962        userAccount.statementForLogging = db.compileStatement(sql);
3963    }
3964
3965    private long getDebugTableRowCount(SQLiteDatabase db) {
3966        String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
3967        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3968    }
3969
3970    /*
3971     * Finds the row key where the next insertion should take place. This should
3972     * be invoked only if the table has reached its full capacity.
3973     */
3974    private long getDebugTableInsertionPoint(SQLiteDatabase db) {
3975        // This query finds the smallest timestamp value (and if 2 records have
3976        // same timestamp, the choose the lower id).
3977        String queryCountDebugDbRows = new StringBuilder()
3978                .append("SELECT ").append(DebugDbHelper.KEY)
3979                .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
3980                .append(" ORDER BY ")
3981                .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
3982                .append(" LIMIT 1")
3983                .toString();
3984        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3985    }
3986
3987    static class DatabaseHelper extends SQLiteOpenHelper {
3988
3989        public DatabaseHelper(Context context, int userId) {
3990            super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
3991        }
3992
3993        /**
3994         * This call needs to be made while the mCacheLock is held. The way to
3995         * ensure this is to get the lock any time a method is called ont the DatabaseHelper
3996         * @param db The database.
3997         */
3998        @Override
3999        public void onCreate(SQLiteDatabase db) {
4000            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4001                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4002                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4003                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4004                    + ACCOUNTS_PASSWORD + " TEXT, "
4005                    + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
4006                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
4007                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4008
4009            db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
4010                    + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
4011                    + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4012                    + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
4013                    + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
4014                    + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
4015
4016            createGrantsTable(db);
4017
4018            db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
4019                    + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4020                    + EXTRAS_ACCOUNTS_ID + " INTEGER, "
4021                    + EXTRAS_KEY + " TEXT NOT NULL, "
4022                    + EXTRAS_VALUE + " TEXT, "
4023                    + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
4024
4025            db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4026                    + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4027                    + META_VALUE + " TEXT)");
4028
4029            createSharedAccountsTable(db);
4030
4031            createAccountsDeletionTrigger(db);
4032
4033            DebugDbHelper.createDebugTable(db);
4034        }
4035
4036        private void createSharedAccountsTable(SQLiteDatabase db) {
4037            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4038                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4039                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4040                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4041                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4042        }
4043
4044        private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4045            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4046                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4047        }
4048
4049        private void addOldAccountNameColumn(SQLiteDatabase db) {
4050            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4051        }
4052
4053        private void addDebugTable(SQLiteDatabase db) {
4054            DebugDbHelper.createDebugTable(db);
4055        }
4056
4057        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4058            db.execSQL(""
4059                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4060                    + " BEGIN"
4061                    + "   DELETE FROM " + TABLE_AUTHTOKENS
4062                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4063                    + "   DELETE FROM " + TABLE_EXTRAS
4064                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4065                    + "   DELETE FROM " + TABLE_GRANTS
4066                    + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4067                    + " END");
4068        }
4069
4070        private void createGrantsTable(SQLiteDatabase db) {
4071            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4072                    + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4073                    + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4074                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4075                    + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4076                    +   "," + GRANTS_GRANTEE_UID + "))");
4077        }
4078
4079        @Override
4080        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4081            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4082
4083            if (oldVersion == 1) {
4084                // no longer need to do anything since the work is done
4085                // when upgrading from version 2
4086                oldVersion++;
4087            }
4088
4089            if (oldVersion == 2) {
4090                createGrantsTable(db);
4091                db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4092                createAccountsDeletionTrigger(db);
4093                oldVersion++;
4094            }
4095
4096            if (oldVersion == 3) {
4097                db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4098                        " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4099                oldVersion++;
4100            }
4101
4102            if (oldVersion == 4) {
4103                createSharedAccountsTable(db);
4104                oldVersion++;
4105            }
4106
4107            if (oldVersion == 5) {
4108                addOldAccountNameColumn(db);
4109                oldVersion++;
4110            }
4111
4112            if (oldVersion == 6) {
4113                addLastSuccessfullAuthenticatedTimeColumn(db);
4114                oldVersion++;
4115            }
4116
4117            if (oldVersion == 7) {
4118                addDebugTable(db);
4119                oldVersion++;
4120            }
4121
4122            if (oldVersion != newVersion) {
4123                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4124            }
4125        }
4126
4127        @Override
4128        public void onOpen(SQLiteDatabase db) {
4129            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4130        }
4131    }
4132
4133    public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
4134        return asBinder();
4135    }
4136
4137    /**
4138     * Searches array of arguments for the specified string
4139     * @param args array of argument strings
4140     * @param value value to search for
4141     * @return true if the value is contained in the array
4142     */
4143    private static boolean scanArgs(String[] args, String value) {
4144        if (args != null) {
4145            for (String arg : args) {
4146                if (value.equals(arg)) {
4147                    return true;
4148                }
4149            }
4150        }
4151        return false;
4152    }
4153
4154    @Override
4155    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
4156        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4157                != PackageManager.PERMISSION_GRANTED) {
4158            fout.println("Permission Denial: can't dump AccountsManager from from pid="
4159                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4160                    + " without permission " + android.Manifest.permission.DUMP);
4161            return;
4162        }
4163        final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
4164        final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
4165
4166        final List<UserInfo> users = getUserManager().getUsers();
4167        for (UserInfo user : users) {
4168            ipw.println("User " + user + ":");
4169            ipw.increaseIndent();
4170            dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4171            ipw.println();
4172            ipw.decreaseIndent();
4173        }
4174    }
4175
4176    private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4177            String[] args, boolean isCheckinRequest) {
4178        synchronized (userAccounts.cacheLock) {
4179            final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
4180
4181            if (isCheckinRequest) {
4182                // This is a checkin request. *Only* upload the account types and the count of each.
4183                Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
4184                        null, null, ACCOUNTS_TYPE, null, null);
4185                try {
4186                    while (cursor.moveToNext()) {
4187                        // print type,count
4188                        fout.println(cursor.getString(0) + "," + cursor.getString(1));
4189                    }
4190                } finally {
4191                    if (cursor != null) {
4192                        cursor.close();
4193                    }
4194                }
4195            } else {
4196                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
4197                        Process.myUid(), null);
4198                fout.println("Accounts: " + accounts.length);
4199                for (Account account : accounts) {
4200                    fout.println("  " + account);
4201                }
4202
4203                // Add debug information.
4204                fout.println();
4205                Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
4206                        null, null, null, null, DebugDbHelper.TIMESTAMP);
4207                fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
4208                fout.println("Accounts History");
4209                try {
4210                    while (cursor.moveToNext()) {
4211                        // print type,count
4212                        fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
4213                                cursor.getString(2) + "," + cursor.getString(3) + ","
4214                                + cursor.getString(4) + "," + cursor.getString(5));
4215                    }
4216                } finally {
4217                    cursor.close();
4218                }
4219
4220                fout.println();
4221                synchronized (mSessions) {
4222                    final long now = SystemClock.elapsedRealtime();
4223                    fout.println("Active Sessions: " + mSessions.size());
4224                    for (Session session : mSessions.values()) {
4225                        fout.println("  " + session.toDebugString(now));
4226                    }
4227                }
4228
4229                fout.println();
4230                mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
4231            }
4232        }
4233    }
4234
4235    private void doNotification(UserAccounts accounts, Account account, CharSequence message,
4236            Intent intent, int userId) {
4237        long identityToken = clearCallingIdentity();
4238        try {
4239            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4240                Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4241            }
4242
4243            if (intent.getComponent() != null &&
4244                    GrantCredentialsPermissionActivity.class.getName().equals(
4245                            intent.getComponent().getClassName())) {
4246                createNoCredentialsPermissionNotification(account, intent, userId);
4247            } else {
4248                final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
4249                intent.addCategory(String.valueOf(notificationId));
4250                UserHandle user = new UserHandle(userId);
4251                Context contextForUser = getContextForUser(user);
4252                final String notificationTitleFormat =
4253                        contextForUser.getText(R.string.notification_title).toString();
4254                Notification n = new Notification.Builder(contextForUser)
4255                        .setWhen(0)
4256                        .setSmallIcon(android.R.drawable.stat_sys_warning)
4257                        .setColor(contextForUser.getColor(
4258                                com.android.internal.R.color.system_notification_accent_color))
4259                        .setContentTitle(String.format(notificationTitleFormat, account.name))
4260                        .setContentText(message)
4261                        .setContentIntent(PendingIntent.getActivityAsUser(
4262                                mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
4263                                null, user))
4264                        .build();
4265                installNotification(notificationId, n, user);
4266            }
4267        } finally {
4268            restoreCallingIdentity(identityToken);
4269        }
4270    }
4271
4272    protected void installNotification(final int notificationId, final Notification n,
4273            UserHandle user) {
4274        ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
4275                .notifyAsUser(null, notificationId, n, user);
4276    }
4277
4278    protected void cancelNotification(int id, UserHandle user) {
4279        long identityToken = clearCallingIdentity();
4280        try {
4281            ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
4282                .cancelAsUser(null, id, user);
4283        } finally {
4284            restoreCallingIdentity(identityToken);
4285        }
4286    }
4287
4288    private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4289        for (String perm : permissions) {
4290            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4291                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4292                    Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
4293                }
4294                final int opCode = AppOpsManager.permissionToOpCode(perm);
4295                if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4296                        opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4297                    return true;
4298                }
4299            }
4300        }
4301        return false;
4302    }
4303
4304    private int handleIncomingUser(int userId) {
4305        try {
4306            return ActivityManagerNative.getDefault().handleIncomingUser(
4307                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4308        } catch (RemoteException re) {
4309            // Shouldn't happen, local.
4310        }
4311        return userId;
4312    }
4313
4314    private boolean isPrivileged(int callingUid) {
4315        final int callingUserId = UserHandle.getUserId(callingUid);
4316
4317        final PackageManager userPackageManager;
4318        try {
4319            userPackageManager = mContext.createPackageContextAsUser(
4320                    "android", 0, new UserHandle(callingUserId)).getPackageManager();
4321        } catch (NameNotFoundException e) {
4322            return false;
4323        }
4324
4325        String[] packages = userPackageManager.getPackagesForUid(callingUid);
4326        for (String name : packages) {
4327            try {
4328                PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
4329                if (packageInfo != null
4330                        && (packageInfo.applicationInfo.privateFlags
4331                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
4332                    return true;
4333                }
4334            } catch (PackageManager.NameNotFoundException e) {
4335                return false;
4336            }
4337        }
4338        return false;
4339    }
4340
4341    private boolean permissionIsGranted(
4342            Account account, String authTokenType, int callerUid, int userId) {
4343        final boolean isPrivileged = isPrivileged(callerUid);
4344        final boolean fromAuthenticator = account != null
4345                && isAccountManagedByCaller(account.type, callerUid, userId);
4346        final boolean hasExplicitGrants = account != null
4347                && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
4348        if (Log.isLoggable(TAG, Log.VERBOSE)) {
4349            Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
4350                    + callerUid + ", " + account
4351                    + ": is authenticator? " + fromAuthenticator
4352                    + ", has explicit permission? " + hasExplicitGrants);
4353        }
4354        return fromAuthenticator || hasExplicitGrants || isPrivileged;
4355    }
4356
4357    private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
4358            String opPackageName) {
4359        if (accountType == null) {
4360            return false;
4361        } else {
4362            return getTypesVisibleToCaller(callingUid, userId,
4363                    opPackageName).contains(accountType);
4364        }
4365    }
4366
4367    private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
4368        if (accountType == null) {
4369            return false;
4370        } else {
4371            return getTypesManagedByCaller(callingUid, userId).contains(accountType);
4372        }
4373    }
4374
4375    private List<String> getTypesVisibleToCaller(int callingUid, int userId,
4376            String opPackageName) {
4377        boolean isPermitted =
4378                isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
4379                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
4380        return getTypesForCaller(callingUid, userId, isPermitted);
4381    }
4382
4383    private List<String> getTypesManagedByCaller(int callingUid, int userId) {
4384        return getTypesForCaller(callingUid, userId, false);
4385    }
4386
4387    private List<String> getTypesForCaller(
4388            int callingUid, int userId, boolean isOtherwisePermitted) {
4389        List<String> managedAccountTypes = new ArrayList<>();
4390        long identityToken = Binder.clearCallingIdentity();
4391        Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
4392        try {
4393            serviceInfos = mAuthenticatorCache.getAllServices(userId);
4394        } finally {
4395            Binder.restoreCallingIdentity(identityToken);
4396        }
4397        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
4398                serviceInfos) {
4399            final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
4400            if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
4401                managedAccountTypes.add(serviceInfo.type.type);
4402            }
4403        }
4404        return managedAccountTypes;
4405    }
4406
4407    private boolean isAccountPresentForCaller(String accountName, String accountType) {
4408        if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
4409            for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
4410                if (account.name.equals(accountName)) {
4411                    return true;
4412                }
4413            }
4414        }
4415        return false;
4416    }
4417
4418    private static void checkManageUsersPermission(String message) {
4419        if (ActivityManager.checkComponentPermission(
4420                android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
4421                != PackageManager.PERMISSION_GRANTED) {
4422            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
4423        }
4424    }
4425
4426    private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
4427            int callerUid) {
4428        if (callerUid == Process.SYSTEM_UID) {
4429            return true;
4430        }
4431        UserAccounts accounts = getUserAccountsForCaller();
4432        synchronized (accounts.cacheLock) {
4433            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4434            String[] args = { String.valueOf(callerUid), authTokenType,
4435                    account.name, account.type};
4436            final boolean permissionGranted =
4437                    DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
4438            if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
4439                // TODO: Skip this check when running automated tests. Replace this
4440                // with a more general solution.
4441                Log.d(TAG, "no credentials permission for usage of " + account + ", "
4442                        + authTokenType + " by uid " + callerUid
4443                        + " but ignoring since device is in test harness.");
4444                return true;
4445            }
4446            return permissionGranted;
4447        }
4448    }
4449
4450    private boolean isSystemUid(int callingUid) {
4451        String[] packages = null;
4452        long ident = Binder.clearCallingIdentity();
4453        try {
4454            packages = mPackageManager.getPackagesForUid(callingUid);
4455        } finally {
4456            Binder.restoreCallingIdentity(ident);
4457        }
4458        if (packages != null) {
4459            for (String name : packages) {
4460                try {
4461                    PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
4462                    if (packageInfo != null
4463                            && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
4464                                    != 0) {
4465                        return true;
4466                    }
4467                } catch (PackageManager.NameNotFoundException e) {
4468                    Log.w(TAG, String.format("Could not find package [%s]", name), e);
4469                }
4470            }
4471        } else {
4472            Log.w(TAG, "No known packages with uid " + callingUid);
4473        }
4474        return false;
4475    }
4476
4477    /** Succeeds if any of the specified permissions are granted. */
4478    private void checkReadAccountsPermitted(
4479            int callingUid,
4480            String accountType,
4481            int userId,
4482            String opPackageName) {
4483        if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
4484            String msg = String.format(
4485                    "caller uid %s cannot access %s accounts",
4486                    callingUid,
4487                    accountType);
4488            Log.w(TAG, "  " + msg);
4489            throw new SecurityException(msg);
4490        }
4491    }
4492
4493    private boolean canUserModifyAccounts(int userId, int callingUid) {
4494        // the managing app can always modify accounts
4495        if (isProfileOwner(callingUid)) {
4496            return true;
4497        }
4498        if (getUserManager().getUserRestrictions(new UserHandle(userId))
4499                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
4500            return false;
4501        }
4502        return true;
4503    }
4504
4505    private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
4506        // the managing app can always modify accounts
4507        if (isProfileOwner(callingUid)) {
4508            return true;
4509        }
4510        DevicePolicyManager dpm = (DevicePolicyManager) mContext
4511                .getSystemService(Context.DEVICE_POLICY_SERVICE);
4512        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
4513        if (typesArray == null) {
4514            return true;
4515        }
4516        for (String forbiddenType : typesArray) {
4517            if (forbiddenType.equals(accountType)) {
4518                return false;
4519            }
4520        }
4521        return true;
4522    }
4523
4524    private boolean isProfileOwner(int uid) {
4525        final DevicePolicyManagerInternal dpmi =
4526                LocalServices.getService(DevicePolicyManagerInternal.class);
4527        return (dpmi != null)
4528                && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
4529    }
4530
4531    @Override
4532    public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
4533            throws RemoteException {
4534        final int callingUid = getCallingUid();
4535
4536        if (callingUid != Process.SYSTEM_UID) {
4537            throw new SecurityException();
4538        }
4539
4540        if (value) {
4541            grantAppPermission(account, authTokenType, uid);
4542        } else {
4543            revokeAppPermission(account, authTokenType, uid);
4544        }
4545    }
4546
4547    /**
4548     * Allow callers with the given uid permission to get credentials for account/authTokenType.
4549     * <p>
4550     * Although this is public it can only be accessed via the AccountManagerService object
4551     * which is in the system. This means we don't need to protect it with permissions.
4552     * @hide
4553     */
4554    private void grantAppPermission(Account account, String authTokenType, int uid) {
4555        if (account == null || authTokenType == null) {
4556            Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
4557            return;
4558        }
4559        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
4560        synchronized (accounts.cacheLock) {
4561            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4562            db.beginTransaction();
4563            try {
4564                long accountId = getAccountIdLocked(db, account);
4565                if (accountId >= 0) {
4566                    ContentValues values = new ContentValues();
4567                    values.put(GRANTS_ACCOUNTS_ID, accountId);
4568                    values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
4569                    values.put(GRANTS_GRANTEE_UID, uid);
4570                    db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
4571                    db.setTransactionSuccessful();
4572                }
4573            } finally {
4574                db.endTransaction();
4575            }
4576            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4577                    new UserHandle(accounts.userId));
4578        }
4579    }
4580
4581    /**
4582     * Don't allow callers with the given uid permission to get credentials for
4583     * account/authTokenType.
4584     * <p>
4585     * Although this is public it can only be accessed via the AccountManagerService object
4586     * which is in the system. This means we don't need to protect it with permissions.
4587     * @hide
4588     */
4589    private void revokeAppPermission(Account account, String authTokenType, int uid) {
4590        if (account == null || authTokenType == null) {
4591            Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
4592            return;
4593        }
4594        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
4595        synchronized (accounts.cacheLock) {
4596            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4597            db.beginTransaction();
4598            try {
4599                long accountId = getAccountIdLocked(db, account);
4600                if (accountId >= 0) {
4601                    db.delete(TABLE_GRANTS,
4602                            GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
4603                                    + GRANTS_GRANTEE_UID + "=?",
4604                            new String[]{String.valueOf(accountId), authTokenType,
4605                                    String.valueOf(uid)});
4606                    db.setTransactionSuccessful();
4607                }
4608            } finally {
4609                db.endTransaction();
4610            }
4611            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4612                    new UserHandle(accounts.userId));
4613        }
4614    }
4615
4616    static final private String stringArrayToString(String[] value) {
4617        return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
4618    }
4619
4620    private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
4621        final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
4622        if (oldAccountsForType != null) {
4623            ArrayList<Account> newAccountsList = new ArrayList<Account>();
4624            for (Account curAccount : oldAccountsForType) {
4625                if (!curAccount.equals(account)) {
4626                    newAccountsList.add(curAccount);
4627                }
4628            }
4629            if (newAccountsList.isEmpty()) {
4630                accounts.accountCache.remove(account.type);
4631            } else {
4632                Account[] newAccountsForType = new Account[newAccountsList.size()];
4633                newAccountsForType = newAccountsList.toArray(newAccountsForType);
4634                accounts.accountCache.put(account.type, newAccountsForType);
4635            }
4636        }
4637        accounts.userDataCache.remove(account);
4638        accounts.authTokenCache.remove(account);
4639        accounts.previousNameCache.remove(account);
4640    }
4641
4642    /**
4643     * This assumes that the caller has already checked that the account is not already present.
4644     */
4645    private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
4646        Account[] accountsForType = accounts.accountCache.get(account.type);
4647        int oldLength = (accountsForType != null) ? accountsForType.length : 0;
4648        Account[] newAccountsForType = new Account[oldLength + 1];
4649        if (accountsForType != null) {
4650            System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
4651        }
4652        newAccountsForType[oldLength] = account;
4653        accounts.accountCache.put(account.type, newAccountsForType);
4654    }
4655
4656    private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
4657            int callingUid, String callingPackage) {
4658        if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
4659                || callingUid == Process.myUid()) {
4660            return unfiltered;
4661        }
4662        UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
4663        if (user != null && user.isRestricted()) {
4664            String[] packages = mPackageManager.getPackagesForUid(callingUid);
4665            // If any of the packages is a white listed package, return the full set,
4666            // otherwise return non-shared accounts only.
4667            // This might be a temporary way to specify a whitelist
4668            String whiteList = mContext.getResources().getString(
4669                    com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
4670            for (String packageName : packages) {
4671                if (whiteList.contains(";" + packageName + ";")) {
4672                    return unfiltered;
4673                }
4674            }
4675            ArrayList<Account> allowed = new ArrayList<Account>();
4676            Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
4677            if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
4678            String requiredAccountType = "";
4679            try {
4680                // If there's an explicit callingPackage specified, check if that package
4681                // opted in to see restricted accounts.
4682                if (callingPackage != null) {
4683                    PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
4684                    if (pi != null && pi.restrictedAccountType != null) {
4685                        requiredAccountType = pi.restrictedAccountType;
4686                    }
4687                } else {
4688                    // Otherwise check if the callingUid has a package that has opted in
4689                    for (String packageName : packages) {
4690                        PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
4691                        if (pi != null && pi.restrictedAccountType != null) {
4692                            requiredAccountType = pi.restrictedAccountType;
4693                            break;
4694                        }
4695                    }
4696                }
4697            } catch (NameNotFoundException nnfe) {
4698            }
4699            for (Account account : unfiltered) {
4700                if (account.type.equals(requiredAccountType)) {
4701                    allowed.add(account);
4702                } else {
4703                    boolean found = false;
4704                    for (Account shared : sharedAccounts) {
4705                        if (shared.equals(account)) {
4706                            found = true;
4707                            break;
4708                        }
4709                    }
4710                    if (!found) {
4711                        allowed.add(account);
4712                    }
4713                }
4714            }
4715            Account[] filtered = new Account[allowed.size()];
4716            allowed.toArray(filtered);
4717            return filtered;
4718        } else {
4719            return unfiltered;
4720        }
4721    }
4722
4723    /*
4724     * packageName can be null. If not null, it should be used to filter out restricted accounts
4725     * that the package is not allowed to access.
4726     */
4727    protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
4728            int callingUid, String callingPackage) {
4729        if (accountType != null) {
4730            final Account[] accounts = userAccounts.accountCache.get(accountType);
4731            if (accounts == null) {
4732                return EMPTY_ACCOUNT_ARRAY;
4733            } else {
4734                return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
4735                        callingUid, callingPackage);
4736            }
4737        } else {
4738            int totalLength = 0;
4739            for (Account[] accounts : userAccounts.accountCache.values()) {
4740                totalLength += accounts.length;
4741            }
4742            if (totalLength == 0) {
4743                return EMPTY_ACCOUNT_ARRAY;
4744            }
4745            Account[] accounts = new Account[totalLength];
4746            totalLength = 0;
4747            for (Account[] accountsOfType : userAccounts.accountCache.values()) {
4748                System.arraycopy(accountsOfType, 0, accounts, totalLength,
4749                        accountsOfType.length);
4750                totalLength += accountsOfType.length;
4751            }
4752            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
4753        }
4754    }
4755
4756    protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4757            Account account, String key, String value) {
4758        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
4759        if (userDataForAccount == null) {
4760            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
4761            accounts.userDataCache.put(account, userDataForAccount);
4762        }
4763        if (value == null) {
4764            userDataForAccount.remove(key);
4765        } else {
4766            userDataForAccount.put(key, value);
4767        }
4768    }
4769
4770    protected String readCachedTokenInternal(
4771            UserAccounts accounts,
4772            Account account,
4773            String tokenType,
4774            String callingPackage,
4775            byte[] pkgSigDigest) {
4776        synchronized (accounts.cacheLock) {
4777            return accounts.accountTokenCaches.get(
4778                    account, tokenType, callingPackage, pkgSigDigest);
4779        }
4780    }
4781
4782    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4783            Account account, String key, String value) {
4784        HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
4785        if (authTokensForAccount == null) {
4786            authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
4787            accounts.authTokenCache.put(account, authTokensForAccount);
4788        }
4789        if (value == null) {
4790            authTokensForAccount.remove(key);
4791        } else {
4792            authTokensForAccount.put(key, value);
4793        }
4794    }
4795
4796    protected String readAuthTokenInternal(UserAccounts accounts, Account account,
4797            String authTokenType) {
4798        synchronized (accounts.cacheLock) {
4799            HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
4800            if (authTokensForAccount == null) {
4801                // need to populate the cache for this account
4802                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4803                authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
4804                accounts.authTokenCache.put(account, authTokensForAccount);
4805            }
4806            return authTokensForAccount.get(authTokenType);
4807        }
4808    }
4809
4810    protected String readUserDataInternalLocked(
4811            UserAccounts accounts, Account account, String key) {
4812        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
4813        if (userDataForAccount == null) {
4814            // need to populate the cache for this account
4815            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4816            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
4817            accounts.userDataCache.put(account, userDataForAccount);
4818        }
4819        return userDataForAccount.get(key);
4820    }
4821
4822    protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
4823            final SQLiteDatabase db, Account account) {
4824        HashMap<String, String> userDataForAccount = new HashMap<String, String>();
4825        Cursor cursor = db.query(TABLE_EXTRAS,
4826                COLUMNS_EXTRAS_KEY_AND_VALUE,
4827                SELECTION_USERDATA_BY_ACCOUNT,
4828                new String[]{account.name, account.type},
4829                null, null, null);
4830        try {
4831            while (cursor.moveToNext()) {
4832                final String tmpkey = cursor.getString(0);
4833                final String value = cursor.getString(1);
4834                userDataForAccount.put(tmpkey, value);
4835            }
4836        } finally {
4837            cursor.close();
4838        }
4839        return userDataForAccount;
4840    }
4841
4842    protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
4843            final SQLiteDatabase db, Account account) {
4844        HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
4845        Cursor cursor = db.query(TABLE_AUTHTOKENS,
4846                COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
4847                SELECTION_AUTHTOKENS_BY_ACCOUNT,
4848                new String[]{account.name, account.type},
4849                null, null, null);
4850        try {
4851            while (cursor.moveToNext()) {
4852                final String type = cursor.getString(0);
4853                final String authToken = cursor.getString(1);
4854                authTokensForAccount.put(type, authToken);
4855            }
4856        } finally {
4857            cursor.close();
4858        }
4859        return authTokensForAccount;
4860    }
4861
4862    private Context getContextForUser(UserHandle user) {
4863        try {
4864            return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4865        } catch (NameNotFoundException e) {
4866            // Default to mContext, not finding the package system is running as is unlikely.
4867            return mContext;
4868        }
4869    }
4870
4871    private void sendResponse(IAccountManagerResponse response, Bundle result) {
4872        try {
4873            response.onResult(result);
4874        } catch (RemoteException e) {
4875            // if the caller is dead then there is no one to care about remote
4876            // exceptions
4877            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4878                Log.v(TAG, "failure while notifying response", e);
4879            }
4880        }
4881    }
4882
4883    private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
4884            String errorMessage) {
4885        try {
4886            response.onError(errorCode, errorMessage);
4887        } catch (RemoteException e) {
4888            // if the caller is dead then there is no one to care about remote
4889            // exceptions
4890            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4891                Log.v(TAG, "failure while notifying response", e);
4892            }
4893        }
4894    }
4895}
4896