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