AccountManagerService.java revision 5f6e3d2b4de4291f0585a1c6d3120aa3fe2e0306
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.IAccountAuthenticator;
30import android.accounts.IAccountAuthenticatorResponse;
31import android.accounts.IAccountManager;
32import android.accounts.IAccountManagerResponse;
33import android.annotation.IntRange;
34import android.annotation.NonNull;
35import android.annotation.Nullable;
36import android.app.ActivityManager;
37import android.app.ActivityManagerNative;
38import android.app.ActivityThread;
39import android.app.AppGlobals;
40import android.app.AppOpsManager;
41import android.app.INotificationManager;
42import android.app.Notification;
43import android.app.NotificationManager;
44import android.app.PendingIntent;
45import android.app.admin.DeviceAdminInfo;
46import android.app.admin.DevicePolicyManager;
47import android.app.admin.DevicePolicyManagerInternal;
48import android.content.BroadcastReceiver;
49import android.content.ComponentName;
50import android.content.ContentValues;
51import android.content.Context;
52import android.content.Intent;
53import android.content.IntentFilter;
54import android.content.IntentSender;
55import android.content.ServiceConnection;
56import android.content.pm.ActivityInfo;
57import android.content.pm.ApplicationInfo;
58import android.content.pm.IPackageManager;
59import android.content.pm.PackageInfo;
60import android.content.pm.PackageManager;
61import android.content.pm.PackageManager.NameNotFoundException;
62import android.content.pm.RegisteredServicesCache;
63import android.content.pm.RegisteredServicesCacheListener;
64import android.content.pm.ResolveInfo;
65import android.content.pm.Signature;
66import android.content.pm.UserInfo;
67import android.database.Cursor;
68import android.database.DatabaseUtils;
69import android.database.sqlite.SQLiteDatabase;
70import android.database.sqlite.SQLiteOpenHelper;
71import android.database.sqlite.SQLiteStatement;
72import android.os.Binder;
73import android.os.Bundle;
74import android.os.Environment;
75import android.os.FileUtils;
76import android.os.Handler;
77import android.os.IBinder;
78import android.os.Looper;
79import android.os.Message;
80import android.os.Parcel;
81import android.os.Process;
82import android.os.RemoteCallback;
83import android.os.RemoteException;
84import android.os.ServiceManager;
85import android.os.SystemClock;
86import android.os.UserHandle;
87import android.os.UserManager;
88import android.os.storage.StorageManager;
89import android.text.TextUtils;
90import android.util.Log;
91import android.util.Pair;
92import android.util.Slog;
93import android.util.SparseArray;
94import android.util.SparseBooleanArray;
95
96import com.android.internal.R;
97import com.android.internal.annotations.GuardedBy;
98import com.android.internal.annotations.VisibleForTesting;
99import com.android.internal.content.PackageMonitor;
100import com.android.internal.util.ArrayUtils;
101import com.android.internal.util.IndentingPrintWriter;
102import com.android.internal.util.Preconditions;
103import com.android.server.FgThread;
104import com.android.server.LocalServices;
105import com.android.server.SystemService;
106
107import com.google.android.collect.Lists;
108import com.google.android.collect.Sets;
109
110import java.io.File;
111import java.io.FileDescriptor;
112import java.io.IOException;
113import java.io.PrintWriter;
114import java.security.GeneralSecurityException;
115import java.security.MessageDigest;
116import java.security.NoSuchAlgorithmException;
117import java.text.SimpleDateFormat;
118import java.util.ArrayList;
119import java.util.Arrays;
120import java.util.Collection;
121import java.util.Date;
122import java.util.HashMap;
123import java.util.HashSet;
124import java.util.Iterator;
125import java.util.LinkedHashMap;
126import java.util.List;
127import java.util.Map;
128import java.util.Map.Entry;
129import java.util.Objects;
130import java.util.UUID;
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                                UUID.randomUUID().toString());
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.putString(AccountManager.KEY_ACCOUNT_ACCESS_ID,
1506                    resultingAccount.getAccessId());
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             * The account is now being tracked for remote access.
1566             */
1567            renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount);
1568
1569            /*
1570             * Extract the data and token caches before removing the
1571             * old account to preserve the user data associated with
1572             * the account.
1573             */
1574            HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1575            HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1576            removeAccountFromCacheLocked(accounts, accountToRename);
1577            /*
1578             * Update the cached data associated with the renamed
1579             * account.
1580             */
1581            accounts.userDataCache.put(renamedAccount, tmpData);
1582            accounts.authTokenCache.put(renamedAccount, tmpTokens);
1583            accounts.previousNameCache.put(
1584                    renamedAccount,
1585                    new AtomicReference<String>(accountToRename.name));
1586            resultAccount = renamedAccount;
1587
1588            int parentUserId = accounts.userId;
1589            if (canHaveProfile(parentUserId)) {
1590                /*
1591                 * Owner or system user account was renamed, rename the account for
1592                 * those users with which the account was shared.
1593                 */
1594                List<UserInfo> users = getUserManager().getUsers(true);
1595                for (UserInfo user : users) {
1596                    if (user.isRestricted()
1597                            && (user.restrictedProfileParentId == parentUserId)) {
1598                        renameSharedAccountAsUser(accountToRename, newName, user.id);
1599                    }
1600                }
1601            }
1602            sendAccountsChangedBroadcast(accounts.userId);
1603        }
1604        return resultAccount;
1605    }
1606
1607    private boolean canHaveProfile(final int parentUserId) {
1608        final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
1609        return userInfo != null && userInfo.canHaveProfile();
1610    }
1611
1612    @Override
1613    public void removeAccount(IAccountManagerResponse response, Account account,
1614            boolean expectActivityLaunch) {
1615        removeAccountAsUser(
1616                response,
1617                account,
1618                expectActivityLaunch,
1619                UserHandle.getCallingUserId());
1620    }
1621
1622    @Override
1623    public void removeAccountAsUser(IAccountManagerResponse response, Account account,
1624            boolean expectActivityLaunch, int userId) {
1625        final int callingUid = Binder.getCallingUid();
1626        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1627            Log.v(TAG, "removeAccount: " + account
1628                    + ", response " + response
1629                    + ", caller's uid " + callingUid
1630                    + ", pid " + Binder.getCallingPid()
1631                    + ", for user id " + userId);
1632        }
1633        if (response == null) throw new IllegalArgumentException("response is null");
1634        if (account == null) throw new IllegalArgumentException("account is null");
1635        // Only allow the system process to modify accounts of other users
1636        if (isCrossUser(callingUid, userId)) {
1637            throw new SecurityException(
1638                    String.format(
1639                            "User %s tying remove account for %s" ,
1640                            UserHandle.getCallingUserId(),
1641                            userId));
1642        }
1643        /*
1644         * Only the system or authenticator should be allowed to remove accounts for that
1645         * authenticator.  This will let users remove accounts (via Settings in the system) but not
1646         * arbitrary applications (like competing authenticators).
1647         */
1648        UserHandle user = UserHandle.of(userId);
1649        if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1650                && !isSystemUid(callingUid)) {
1651            String msg = String.format(
1652                    "uid %s cannot remove accounts of type: %s",
1653                    callingUid,
1654                    account.type);
1655            throw new SecurityException(msg);
1656        }
1657        if (!canUserModifyAccounts(userId, callingUid)) {
1658            try {
1659                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1660                        "User cannot modify accounts");
1661            } catch (RemoteException re) {
1662            }
1663            return;
1664        }
1665        if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
1666            try {
1667                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1668                        "User cannot modify accounts of this type (policy).");
1669            } catch (RemoteException re) {
1670            }
1671            return;
1672        }
1673        long identityToken = clearCallingIdentity();
1674        UserAccounts accounts = getUserAccounts(userId);
1675        cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
1676        synchronized(accounts.credentialsPermissionNotificationIds) {
1677            for (Pair<Pair<Account, String>, Integer> pair:
1678                accounts.credentialsPermissionNotificationIds.keySet()) {
1679                if (account.equals(pair.first.first)) {
1680                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
1681                    cancelNotification(id, user);
1682                }
1683            }
1684        }
1685        SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
1686        final long accountId = getAccountIdLocked(db, account);
1687        logRecord(
1688                db,
1689                DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
1690                TABLE_ACCOUNTS,
1691                accountId,
1692                accounts,
1693                callingUid);
1694        try {
1695            new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1696        } finally {
1697            restoreCallingIdentity(identityToken);
1698        }
1699    }
1700
1701    @Override
1702    public boolean removeAccountExplicitly(Account account) {
1703        final int callingUid = Binder.getCallingUid();
1704        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1705            Log.v(TAG, "removeAccountExplicitly: " + account
1706                    + ", caller's uid " + callingUid
1707                    + ", pid " + Binder.getCallingPid());
1708        }
1709        int userId = Binder.getCallingUserHandle().getIdentifier();
1710        if (account == null) {
1711            /*
1712             * Null accounts should result in returning false, as per
1713             * AccountManage.addAccountExplicitly(...) java doc.
1714             */
1715            Log.e(TAG, "account is null");
1716            return false;
1717        } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1718            String msg = String.format(
1719                    "uid %s cannot explicitly add accounts of type: %s",
1720                    callingUid,
1721                    account.type);
1722            throw new SecurityException(msg);
1723        }
1724        UserAccounts accounts = getUserAccountsForCaller();
1725        SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
1726        final long accountId = getAccountIdLocked(db, account);
1727        logRecord(
1728                db,
1729                DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
1730                TABLE_ACCOUNTS,
1731                accountId,
1732                accounts,
1733                callingUid);
1734        long identityToken = clearCallingIdentity();
1735        try {
1736            return removeAccountInternal(accounts, account, callingUid);
1737        } finally {
1738            restoreCallingIdentity(identityToken);
1739        }
1740    }
1741
1742    private class RemoveAccountSession extends Session {
1743        final Account mAccount;
1744        public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
1745                Account account, boolean expectActivityLaunch) {
1746            super(accounts, response, account.type, expectActivityLaunch,
1747                    true /* stripAuthTokenFromResult */, account.name,
1748                    false /* authDetailsRequired */);
1749            mAccount = account;
1750        }
1751
1752        @Override
1753        protected String toDebugString(long now) {
1754            return super.toDebugString(now) + ", removeAccount"
1755                    + ", account " + mAccount;
1756        }
1757
1758        @Override
1759        public void run() throws RemoteException {
1760            mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1761        }
1762
1763        @Override
1764        public void onResult(Bundle result) {
1765            Bundle.setDefusable(result, true);
1766            if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1767                    && !result.containsKey(AccountManager.KEY_INTENT)) {
1768                final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
1769                if (removalAllowed) {
1770                    removeAccountInternal(mAccounts, mAccount, getCallingUid());
1771                }
1772                IAccountManagerResponse response = getResponseAndClose();
1773                if (response != null) {
1774                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1775                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1776                                + response);
1777                    }
1778                    Bundle result2 = new Bundle();
1779                    result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
1780                    try {
1781                        response.onResult(result2);
1782                    } catch (RemoteException e) {
1783                        // ignore
1784                    }
1785                }
1786            }
1787            super.onResult(result);
1788        }
1789    }
1790
1791    @VisibleForTesting
1792    protected void removeAccountInternal(Account account) {
1793        removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
1794    }
1795
1796    private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
1797        boolean isChanged = false;
1798        boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
1799        if (!userUnlocked) {
1800            Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
1801                    + " is still locked. CE data will be removed later");
1802        }
1803        synchronized (accounts.cacheLock) {
1804            final SQLiteDatabase db = userUnlocked
1805                    ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
1806                    : accounts.openHelper.getWritableDatabase();
1807            db.beginTransaction();
1808            // Set to a dummy value, this will only be used if the database
1809            // transaction succeeds.
1810            long accountId = -1;
1811            try {
1812                accountId = getAccountIdLocked(db, account);
1813                if (accountId >= 0) {
1814                    db.delete(
1815                            TABLE_ACCOUNTS,
1816                            ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1817                            new String[]{ account.name, account.type });
1818                    if (userUnlocked) {
1819                        // Delete from CE table
1820                        db.delete(
1821                                CE_TABLE_ACCOUNTS,
1822                                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1823                                new String[]{ account.name, account.type });
1824                    }
1825                    db.setTransactionSuccessful();
1826                    isChanged = true;
1827                }
1828            } finally {
1829                db.endTransaction();
1830            }
1831            if (isChanged) {
1832                removeAccountFromCacheLocked(accounts, account);
1833                // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
1834                sendAccountsChangedBroadcast(accounts.userId);
1835                String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
1836                        : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
1837                logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
1838            }
1839        }
1840        long id = Binder.clearCallingIdentity();
1841        try {
1842            int parentUserId = accounts.userId;
1843            if (canHaveProfile(parentUserId)) {
1844                // Remove from any restricted profiles that are sharing this account.
1845                List<UserInfo> users = getUserManager().getUsers(true);
1846                for (UserInfo user : users) {
1847                    if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
1848                        removeSharedAccountAsUser(account, user.id, callingUid);
1849                    }
1850                }
1851            }
1852        } finally {
1853            Binder.restoreCallingIdentity(id);
1854        }
1855
1856        if (isChanged) {
1857            synchronized (accounts.credentialsPermissionNotificationIds) {
1858                for (Pair<Pair<Account, String>, Integer> key
1859                        : accounts.credentialsPermissionNotificationIds.keySet()) {
1860                    if (account.equals(key.first.first)
1861                            && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
1862                        final int uid = (Integer) key.second;
1863                        mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
1864                                account, uid, false));
1865                    }
1866                }
1867            }
1868        }
1869
1870        return isChanged;
1871    }
1872
1873    @Override
1874    public void invalidateAuthToken(String accountType, String authToken) {
1875        int callerUid = Binder.getCallingUid();
1876        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1877            Log.v(TAG, "invalidateAuthToken: accountType " + accountType
1878                    + ", caller's uid " + callerUid
1879                    + ", pid " + Binder.getCallingPid());
1880        }
1881        if (accountType == null) throw new IllegalArgumentException("accountType is null");
1882        if (authToken == null) throw new IllegalArgumentException("authToken is null");
1883        int userId = UserHandle.getCallingUserId();
1884        long identityToken = clearCallingIdentity();
1885        try {
1886            UserAccounts accounts = getUserAccounts(userId);
1887            synchronized (accounts.cacheLock) {
1888                final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1889                db.beginTransaction();
1890                try {
1891                    invalidateAuthTokenLocked(accounts, db, accountType, authToken);
1892                    invalidateCustomTokenLocked(accounts, accountType, authToken);
1893                    db.setTransactionSuccessful();
1894                } finally {
1895                    db.endTransaction();
1896                }
1897            }
1898        } finally {
1899            restoreCallingIdentity(identityToken);
1900        }
1901    }
1902
1903    private void invalidateCustomTokenLocked(
1904            UserAccounts accounts,
1905            String accountType,
1906            String authToken) {
1907        if (authToken == null || accountType == null) {
1908            return;
1909        }
1910        // Also wipe out cached token in memory.
1911        accounts.accountTokenCaches.remove(accountType, authToken);
1912    }
1913
1914    private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1915            String accountType, String authToken) {
1916        if (authToken == null || accountType == null) {
1917            return;
1918        }
1919        Cursor cursor = db.rawQuery(
1920                "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1921                        + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1922                        + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1923                        + " FROM " + CE_TABLE_ACCOUNTS
1924                        + " JOIN " + CE_TABLE_AUTHTOKENS
1925                        + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1926                        + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
1927                        + " WHERE " + CE_TABLE_AUTHTOKENS + "."  + AUTHTOKENS_AUTHTOKEN
1928                        + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1929                new String[]{authToken, accountType});
1930        try {
1931            while (cursor.moveToNext()) {
1932                long authTokenId = cursor.getLong(0);
1933                String accountName = cursor.getString(1);
1934                String authTokenType = cursor.getString(2);
1935                db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
1936                writeAuthTokenIntoCacheLocked(
1937                        accounts,
1938                        db,
1939                        new Account(accountName, accountType),
1940                        authTokenType,
1941                        null);
1942            }
1943        } finally {
1944            cursor.close();
1945        }
1946    }
1947
1948    private void saveCachedToken(
1949            UserAccounts accounts,
1950            Account account,
1951            String callerPkg,
1952            byte[] callerSigDigest,
1953            String tokenType,
1954            String token,
1955            long expiryMillis) {
1956
1957        if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1958            return;
1959        }
1960        cancelNotification(getSigninRequiredNotificationId(accounts, account),
1961                UserHandle.of(accounts.userId));
1962        synchronized (accounts.cacheLock) {
1963            accounts.accountTokenCaches.put(
1964                    account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
1965        }
1966    }
1967
1968    private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1969            String authToken) {
1970        if (account == null || type == null) {
1971            return false;
1972        }
1973        cancelNotification(getSigninRequiredNotificationId(accounts, account),
1974                UserHandle.of(accounts.userId));
1975        synchronized (accounts.cacheLock) {
1976            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1977            db.beginTransaction();
1978            try {
1979                long accountId = getAccountIdLocked(db, account);
1980                if (accountId < 0) {
1981                    return false;
1982                }
1983                db.delete(CE_TABLE_AUTHTOKENS,
1984                        AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1985                        new String[]{type});
1986                ContentValues values = new ContentValues();
1987                values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1988                values.put(AUTHTOKENS_TYPE, type);
1989                values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1990                if (db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1991                    db.setTransactionSuccessful();
1992                    writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
1993                    return true;
1994                }
1995                return false;
1996            } finally {
1997                db.endTransaction();
1998            }
1999        }
2000    }
2001
2002    @Override
2003    public String peekAuthToken(Account account, String authTokenType) {
2004        final int callingUid = Binder.getCallingUid();
2005        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2006            Log.v(TAG, "peekAuthToken: " + account
2007                    + ", authTokenType " + authTokenType
2008                    + ", caller's uid " + callingUid
2009                    + ", pid " + Binder.getCallingPid());
2010        }
2011        if (account == null) throw new IllegalArgumentException("account is null");
2012        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2013        int userId = UserHandle.getCallingUserId();
2014        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2015            String msg = String.format(
2016                    "uid %s cannot peek the authtokens associated with accounts of type: %s",
2017                    callingUid,
2018                    account.type);
2019            throw new SecurityException(msg);
2020        }
2021        if (!isLocalUnlockedUser(userId)) {
2022            Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2023                    + callingUid);
2024            return null;
2025        }
2026        long identityToken = clearCallingIdentity();
2027        try {
2028            UserAccounts accounts = getUserAccounts(userId);
2029            return readAuthTokenInternal(accounts, account, authTokenType);
2030        } finally {
2031            restoreCallingIdentity(identityToken);
2032        }
2033    }
2034
2035    @Override
2036    public void setAuthToken(Account account, String authTokenType, String authToken) {
2037        final int callingUid = Binder.getCallingUid();
2038        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2039            Log.v(TAG, "setAuthToken: " + account
2040                    + ", authTokenType " + authTokenType
2041                    + ", caller's uid " + callingUid
2042                    + ", pid " + Binder.getCallingPid());
2043        }
2044        if (account == null) throw new IllegalArgumentException("account is null");
2045        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2046        int userId = UserHandle.getCallingUserId();
2047        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2048            String msg = String.format(
2049                    "uid %s cannot set auth tokens associated with accounts of type: %s",
2050                    callingUid,
2051                    account.type);
2052            throw new SecurityException(msg);
2053        }
2054        long identityToken = clearCallingIdentity();
2055        try {
2056            UserAccounts accounts = getUserAccounts(userId);
2057            saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
2058        } finally {
2059            restoreCallingIdentity(identityToken);
2060        }
2061    }
2062
2063    @Override
2064    public void setPassword(Account account, String password) {
2065        final int callingUid = Binder.getCallingUid();
2066        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2067            Log.v(TAG, "setAuthToken: " + account
2068                    + ", caller's uid " + callingUid
2069                    + ", pid " + Binder.getCallingPid());
2070        }
2071        if (account == null) throw new IllegalArgumentException("account is null");
2072        int userId = UserHandle.getCallingUserId();
2073        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2074            String msg = String.format(
2075                    "uid %s cannot set secrets for accounts of type: %s",
2076                    callingUid,
2077                    account.type);
2078            throw new SecurityException(msg);
2079        }
2080        long identityToken = clearCallingIdentity();
2081        try {
2082            UserAccounts accounts = getUserAccounts(userId);
2083            setPasswordInternal(accounts, account, password, callingUid);
2084        } finally {
2085            restoreCallingIdentity(identityToken);
2086        }
2087    }
2088
2089    private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2090            int callingUid) {
2091        if (account == null) {
2092            return;
2093        }
2094        boolean isChanged = false;
2095        synchronized (accounts.cacheLock) {
2096            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
2097            db.beginTransaction();
2098            try {
2099                final ContentValues values = new ContentValues();
2100                values.put(ACCOUNTS_PASSWORD, password);
2101                final long accountId = getAccountIdLocked(db, account);
2102                if (accountId >= 0) {
2103                    final String[] argsAccountId = {String.valueOf(accountId)};
2104                    db.update(
2105                            CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2106                    db.delete(
2107                            CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
2108                    accounts.authTokenCache.remove(account);
2109                    accounts.accountTokenCaches.remove(account);
2110                    db.setTransactionSuccessful();
2111                    // If there is an account whose password will be updated and the database
2112                    // transactions succeed, then we say that a change has occured. Even if the
2113                    // new password is the same as the old and there were no authtokens to delete.
2114                    isChanged = true;
2115                    String action = (password == null || password.length() == 0) ?
2116                            DebugDbHelper.ACTION_CLEAR_PASSWORD
2117                            : DebugDbHelper.ACTION_SET_PASSWORD;
2118                    logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
2119                }
2120            } finally {
2121                db.endTransaction();
2122                if (isChanged) {
2123                    // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2124                    sendAccountsChangedBroadcast(accounts.userId);
2125                }
2126            }
2127        }
2128    }
2129
2130    private void sendAccountsChangedBroadcast(int userId) {
2131        Log.i(TAG, "the accounts changed, sending broadcast of "
2132                + ACCOUNTS_CHANGED_INTENT.getAction());
2133        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
2134    }
2135
2136    @Override
2137    public void clearPassword(Account account) {
2138        final int callingUid = Binder.getCallingUid();
2139        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2140            Log.v(TAG, "clearPassword: " + account
2141                    + ", caller's uid " + callingUid
2142                    + ", pid " + Binder.getCallingPid());
2143        }
2144        if (account == null) throw new IllegalArgumentException("account is null");
2145        int userId = UserHandle.getCallingUserId();
2146        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2147            String msg = String.format(
2148                    "uid %s cannot clear passwords for accounts of type: %s",
2149                    callingUid,
2150                    account.type);
2151            throw new SecurityException(msg);
2152        }
2153        long identityToken = clearCallingIdentity();
2154        try {
2155            UserAccounts accounts = getUserAccounts(userId);
2156            setPasswordInternal(accounts, account, null, callingUid);
2157        } finally {
2158            restoreCallingIdentity(identityToken);
2159        }
2160    }
2161
2162    @Override
2163    public void setUserData(Account account, String key, String value) {
2164        final int callingUid = Binder.getCallingUid();
2165        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2166            Log.v(TAG, "setUserData: " + account
2167                    + ", key " + key
2168                    + ", caller's uid " + callingUid
2169                    + ", pid " + Binder.getCallingPid());
2170        }
2171        if (key == null) throw new IllegalArgumentException("key is null");
2172        if (account == null) throw new IllegalArgumentException("account is null");
2173        int userId = UserHandle.getCallingUserId();
2174        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2175            String msg = String.format(
2176                    "uid %s cannot set user data for accounts of type: %s",
2177                    callingUid,
2178                    account.type);
2179            throw new SecurityException(msg);
2180        }
2181        long identityToken = clearCallingIdentity();
2182        try {
2183            UserAccounts accounts = getUserAccounts(userId);
2184            synchronized (accounts.cacheLock) {
2185                if (!accountExistsCacheLocked(accounts, account)) {
2186                    return;
2187                }
2188                setUserdataInternalLocked(accounts, account, key, value);
2189            }
2190        } finally {
2191            restoreCallingIdentity(identityToken);
2192        }
2193    }
2194
2195    private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2196        if (accounts.accountCache.containsKey(account.type)) {
2197            for (Account acc : accounts.accountCache.get(account.type)) {
2198                if (acc.name.equals(account.name)) {
2199                    return true;
2200                }
2201            }
2202        }
2203        return false;
2204    }
2205
2206    private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
2207            String value) {
2208        if (account == null || key == null) {
2209            return;
2210        }
2211        final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2212        db.beginTransaction();
2213        try {
2214            long accountId = getAccountIdLocked(db, account);
2215            if (accountId < 0) {
2216                return;
2217            }
2218            long extrasId = getExtrasIdLocked(db, accountId, key);
2219            if (extrasId < 0) {
2220                extrasId = insertExtraLocked(db, accountId, key, value);
2221                if (extrasId < 0) {
2222                    return;
2223                }
2224            } else {
2225                ContentValues values = new ContentValues();
2226                values.put(EXTRAS_VALUE, value);
2227                if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
2228                    return;
2229                }
2230            }
2231            writeUserDataIntoCacheLocked(accounts, db, account, key, value);
2232            db.setTransactionSuccessful();
2233        } finally {
2234            db.endTransaction();
2235        }
2236    }
2237
2238    private void onResult(IAccountManagerResponse response, Bundle result) {
2239        if (result == null) {
2240            Log.e(TAG, "the result is unexpectedly null", new Exception());
2241        }
2242        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2243            Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2244                    + response);
2245        }
2246        try {
2247            response.onResult(result);
2248        } catch (RemoteException e) {
2249            // if the caller is dead then there is no one to care about remote
2250            // exceptions
2251            if (Log.isLoggable(TAG, Log.VERBOSE)) {
2252                Log.v(TAG, "failure while notifying response", e);
2253            }
2254        }
2255    }
2256
2257    @Override
2258    public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2259                                  final String authTokenType)
2260            throws RemoteException {
2261        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2262        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2263
2264        final int callingUid = getCallingUid();
2265        clearCallingIdentity();
2266        if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
2267            throw new SecurityException("can only call from system");
2268        }
2269        int userId = UserHandle.getUserId(callingUid);
2270        long identityToken = clearCallingIdentity();
2271        try {
2272            UserAccounts accounts = getUserAccounts(userId);
2273            new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2274                    false /* stripAuthTokenFromResult */,  null /* accountName */,
2275                    false /* authDetailsRequired */) {
2276                @Override
2277                protected String toDebugString(long now) {
2278                    return super.toDebugString(now) + ", getAuthTokenLabel"
2279                            + ", " + accountType
2280                            + ", authTokenType " + authTokenType;
2281                }
2282
2283                @Override
2284                public void run() throws RemoteException {
2285                    mAuthenticator.getAuthTokenLabel(this, authTokenType);
2286                }
2287
2288                @Override
2289                public void onResult(Bundle result) {
2290                    Bundle.setDefusable(result, true);
2291                    if (result != null) {
2292                        String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2293                        Bundle bundle = new Bundle();
2294                        bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2295                        super.onResult(bundle);
2296                        return;
2297                    } else {
2298                        super.onResult(result);
2299                    }
2300                }
2301            }.bind();
2302        } finally {
2303            restoreCallingIdentity(identityToken);
2304        }
2305    }
2306
2307    @Override
2308    public void getAuthToken(
2309            IAccountManagerResponse response,
2310            final Account account,
2311            final String authTokenType,
2312            final boolean notifyOnAuthFailure,
2313            final boolean expectActivityLaunch,
2314            final Bundle loginOptions) {
2315        Bundle.setDefusable(loginOptions, true);
2316        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2317            Log.v(TAG, "getAuthToken: " + account
2318                    + ", response " + response
2319                    + ", authTokenType " + authTokenType
2320                    + ", notifyOnAuthFailure " + notifyOnAuthFailure
2321                    + ", expectActivityLaunch " + expectActivityLaunch
2322                    + ", caller's uid " + Binder.getCallingUid()
2323                    + ", pid " + Binder.getCallingPid());
2324        }
2325        if (response == null) throw new IllegalArgumentException("response is null");
2326        try {
2327            if (account == null) {
2328                Slog.w(TAG, "getAuthToken called with null account");
2329                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2330                return;
2331            }
2332            if (authTokenType == null) {
2333                Slog.w(TAG, "getAuthToken called with null authTokenType");
2334                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2335                return;
2336            }
2337        } catch (RemoteException e) {
2338            Slog.w(TAG, "Failed to report error back to the client." + e);
2339            return;
2340        }
2341        int userId = UserHandle.getCallingUserId();
2342        long ident = Binder.clearCallingIdentity();
2343        final UserAccounts accounts;
2344        final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
2345        try {
2346            accounts = getUserAccounts(userId);
2347            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2348                    AuthenticatorDescription.newKey(account.type), accounts.userId);
2349        } finally {
2350            Binder.restoreCallingIdentity(ident);
2351        }
2352
2353        final boolean customTokens =
2354                authenticatorInfo != null && authenticatorInfo.type.customTokens;
2355
2356        // skip the check if customTokens
2357        final int callerUid = Binder.getCallingUid();
2358        final boolean permissionGranted =
2359                customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
2360
2361        // Get the calling package. We will use it for the purpose of caching.
2362        final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
2363        List<String> callerOwnedPackageNames;
2364        ident = Binder.clearCallingIdentity();
2365        try {
2366            callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2367        } finally {
2368            Binder.restoreCallingIdentity(ident);
2369        }
2370        if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2371            String msg = String.format(
2372                    "Uid %s is attempting to illegally masquerade as package %s!",
2373                    callerUid,
2374                    callerPkg);
2375            throw new SecurityException(msg);
2376        }
2377
2378        // let authenticator know the identity of the caller
2379        loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2380        loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
2381
2382        if (notifyOnAuthFailure) {
2383            loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
2384        }
2385
2386        long identityToken = clearCallingIdentity();
2387        try {
2388            // Distill the caller's package signatures into a single digest.
2389            final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2390
2391            // if the caller has permission, do the peek. otherwise go the more expensive
2392            // route of starting a Session
2393            if (!customTokens && permissionGranted) {
2394                String authToken = readAuthTokenInternal(accounts, account, authTokenType);
2395                if (authToken != null) {
2396                    Bundle result = new Bundle();
2397                    result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2398                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2399                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2400                    onResult(response, result);
2401                    return;
2402                }
2403            }
2404
2405            if (customTokens) {
2406                /*
2407                 * Look up tokens in the new cache only if the loginOptions don't have parameters
2408                 * outside of those expected to be injected by the AccountManager, e.g.
2409                 * ANDORID_PACKAGE_NAME.
2410                 */
2411                String token = readCachedTokenInternal(
2412                        accounts,
2413                        account,
2414                        authTokenType,
2415                        callerPkg,
2416                        callerPkgSigDigest);
2417                if (token != null) {
2418                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
2419                        Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2420                    }
2421                    Bundle result = new Bundle();
2422                    result.putString(AccountManager.KEY_AUTHTOKEN, token);
2423                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2424                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2425                    onResult(response, result);
2426                    return;
2427                }
2428            }
2429
2430            new Session(
2431                    accounts,
2432                    response,
2433                    account.type,
2434                    expectActivityLaunch,
2435                    false /* stripAuthTokenFromResult */,
2436                    account.name,
2437                    false /* authDetailsRequired */) {
2438                @Override
2439                protected String toDebugString(long now) {
2440                    if (loginOptions != null) loginOptions.keySet();
2441                    return super.toDebugString(now) + ", getAuthToken"
2442                            + ", " + account
2443                            + ", authTokenType " + authTokenType
2444                            + ", loginOptions " + loginOptions
2445                            + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2446                }
2447
2448                @Override
2449                public void run() throws RemoteException {
2450                    // If the caller doesn't have permission then create and return the
2451                    // "grant permission" intent instead of the "getAuthToken" intent.
2452                    if (!permissionGranted) {
2453                        mAuthenticator.getAuthTokenLabel(this, authTokenType);
2454                    } else {
2455                        mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2456                    }
2457                }
2458
2459                @Override
2460                public void onResult(Bundle result) {
2461                    Bundle.setDefusable(result, true);
2462                    if (result != null) {
2463                        if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
2464                            Intent intent = newGrantCredentialsPermissionIntent(
2465                                    account,
2466                                    null,
2467                                    callerUid,
2468                                    new AccountAuthenticatorResponse(this),
2469                                    authTokenType,
2470                                    true);
2471                            Bundle bundle = new Bundle();
2472                            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
2473                            onResult(bundle);
2474                            return;
2475                        }
2476                        String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
2477                        if (authToken != null) {
2478                            String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2479                            String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
2480                            if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
2481                                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
2482                                        "the type and name should not be empty");
2483                                return;
2484                            }
2485                            Account resultAccount = new Account(name, type);
2486                            if (!customTokens) {
2487                                saveAuthTokenToDatabase(
2488                                        mAccounts,
2489                                        resultAccount,
2490                                        authTokenType,
2491                                        authToken);
2492                            }
2493                            long expiryMillis = result.getLong(
2494                                    AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2495                            if (customTokens
2496                                    && expiryMillis > System.currentTimeMillis()) {
2497                                saveCachedToken(
2498                                        mAccounts,
2499                                        account,
2500                                        callerPkg,
2501                                        callerPkgSigDigest,
2502                                        authTokenType,
2503                                        authToken,
2504                                        expiryMillis);
2505                            }
2506                        }
2507
2508                        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
2509                        if (intent != null && notifyOnAuthFailure && !customTokens) {
2510                            /*
2511                             * Make sure that the supplied intent is owned by the authenticator
2512                             * giving it to the system. Otherwise a malicious authenticator could
2513                             * have users launching arbitrary activities by tricking users to
2514                             * interact with malicious notifications.
2515                             */
2516                            checkKeyIntent(
2517                                    Binder.getCallingUid(),
2518                                    intent);
2519                            doNotification(mAccounts,
2520                                    account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
2521                                    intent, "android", accounts.userId);
2522                        }
2523                    }
2524                    super.onResult(result);
2525                }
2526            }.bind();
2527        } finally {
2528            restoreCallingIdentity(identityToken);
2529        }
2530    }
2531
2532    private byte[] calculatePackageSignatureDigest(String callerPkg) {
2533        MessageDigest digester;
2534        try {
2535            digester = MessageDigest.getInstance("SHA-256");
2536            PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2537                    callerPkg, PackageManager.GET_SIGNATURES);
2538            for (Signature sig : pkgInfo.signatures) {
2539                digester.update(sig.toByteArray());
2540            }
2541        } catch (NoSuchAlgorithmException x) {
2542            Log.wtf(TAG, "SHA-256 should be available", x);
2543            digester = null;
2544        } catch (NameNotFoundException e) {
2545            Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2546            digester = null;
2547        }
2548        return (digester == null) ? null : digester.digest();
2549    }
2550
2551    private void createNoCredentialsPermissionNotification(Account account, Intent intent,
2552            String packageName, int userId) {
2553        int uid = intent.getIntExtra(
2554                GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2555        String authTokenType = intent.getStringExtra(
2556                GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
2557        final String titleAndSubtitle =
2558                mContext.getString(R.string.permission_request_notification_with_subtitle,
2559                account.name);
2560        final int index = titleAndSubtitle.indexOf('\n');
2561        String title = titleAndSubtitle;
2562        String subtitle = "";
2563        if (index > 0) {
2564            title = titleAndSubtitle.substring(0, index);
2565            subtitle = titleAndSubtitle.substring(index + 1);
2566        }
2567        UserHandle user = new UserHandle(userId);
2568        Context contextForUser = getContextForUser(user);
2569        Notification n = new Notification.Builder(contextForUser)
2570                .setSmallIcon(android.R.drawable.stat_sys_warning)
2571                .setWhen(0)
2572                .setColor(contextForUser.getColor(
2573                        com.android.internal.R.color.system_notification_accent_color))
2574                .setContentTitle(title)
2575                .setContentText(subtitle)
2576                .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2577                        PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2578                .build();
2579        installNotification(getCredentialPermissionNotificationId(
2580                account, authTokenType, uid), n, packageName, user.getIdentifier());
2581    }
2582
2583    private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
2584            int uid, AccountAuthenticatorResponse response, String authTokenType,
2585            boolean startInNewTask) {
2586
2587        Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
2588
2589        if (startInNewTask) {
2590            // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
2591            // Since it was set in Eclair+ we can't change it without breaking apps using
2592            // the intent from a non-Activity context. This is the default behavior.
2593            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2594        }
2595        intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
2596                authTokenType, uid) + (packageName != null ? packageName : "")));
2597        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
2598        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2599        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
2600        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
2601
2602        return intent;
2603    }
2604
2605    private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2606            int uid) {
2607        Integer id;
2608        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
2609        synchronized (accounts.credentialsPermissionNotificationIds) {
2610            final Pair<Pair<Account, String>, Integer> key =
2611                    new Pair<Pair<Account, String>, Integer>(
2612                            new Pair<Account, String>(account, authTokenType), uid);
2613            id = accounts.credentialsPermissionNotificationIds.get(key);
2614            if (id == null) {
2615                id = mNotificationIds.incrementAndGet();
2616                accounts.credentialsPermissionNotificationIds.put(key, id);
2617            }
2618        }
2619        return id;
2620    }
2621
2622    private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
2623        Integer id;
2624        synchronized (accounts.signinRequiredNotificationIds) {
2625            id = accounts.signinRequiredNotificationIds.get(account);
2626            if (id == null) {
2627                id = mNotificationIds.incrementAndGet();
2628                accounts.signinRequiredNotificationIds.put(account, id);
2629            }
2630        }
2631        return id;
2632    }
2633
2634    @Override
2635    public void addAccount(final IAccountManagerResponse response, final String accountType,
2636            final String authTokenType, final String[] requiredFeatures,
2637            final boolean expectActivityLaunch, final Bundle optionsIn) {
2638        Bundle.setDefusable(optionsIn, true);
2639        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2640            Log.v(TAG, "addAccount: accountType " + accountType
2641                    + ", response " + response
2642                    + ", authTokenType " + authTokenType
2643                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2644                    + ", expectActivityLaunch " + expectActivityLaunch
2645                    + ", caller's uid " + Binder.getCallingUid()
2646                    + ", pid " + Binder.getCallingPid());
2647        }
2648        if (response == null) throw new IllegalArgumentException("response is null");
2649        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2650
2651        // Is user disallowed from modifying accounts?
2652        final int uid = Binder.getCallingUid();
2653        final int userId = UserHandle.getUserId(uid);
2654        if (!canUserModifyAccounts(userId, uid)) {
2655            try {
2656                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2657                        "User is not allowed to add an account!");
2658            } catch (RemoteException re) {
2659            }
2660            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2661            return;
2662        }
2663        if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2664            try {
2665                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2666                        "User cannot modify accounts of this type (policy).");
2667            } catch (RemoteException re) {
2668            }
2669            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2670                    userId);
2671            return;
2672        }
2673
2674        final int pid = Binder.getCallingPid();
2675        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2676        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2677        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2678
2679        int usrId = UserHandle.getCallingUserId();
2680        long identityToken = clearCallingIdentity();
2681        try {
2682            UserAccounts accounts = getUserAccounts(usrId);
2683            logRecordWithUid(
2684                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
2685            new Session(accounts, response, accountType, expectActivityLaunch,
2686                    true /* stripAuthTokenFromResult */, null /* accountName */,
2687                    false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2688                @Override
2689                public void run() throws RemoteException {
2690                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2691                            options);
2692                }
2693
2694                @Override
2695                protected String toDebugString(long now) {
2696                    return super.toDebugString(now) + ", addAccount"
2697                            + ", accountType " + accountType
2698                            + ", requiredFeatures "
2699                            + (requiredFeatures != null
2700                              ? TextUtils.join(",", requiredFeatures)
2701                              : null);
2702                }
2703            }.bind();
2704        } finally {
2705            restoreCallingIdentity(identityToken);
2706        }
2707    }
2708
2709    @Override
2710    public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2711            final String authTokenType, final String[] requiredFeatures,
2712            final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
2713        Bundle.setDefusable(optionsIn, true);
2714        int callingUid = Binder.getCallingUid();
2715        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2716            Log.v(TAG, "addAccount: accountType " + accountType
2717                    + ", response " + response
2718                    + ", authTokenType " + authTokenType
2719                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2720                    + ", expectActivityLaunch " + expectActivityLaunch
2721                    + ", caller's uid " + Binder.getCallingUid()
2722                    + ", pid " + Binder.getCallingPid()
2723                    + ", for user id " + userId);
2724        }
2725        if (response == null) throw new IllegalArgumentException("response is null");
2726        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2727        // Only allow the system process to add accounts of other users
2728        if (isCrossUser(callingUid, userId)) {
2729            throw new SecurityException(
2730                    String.format(
2731                            "User %s trying to add account for %s" ,
2732                            UserHandle.getCallingUserId(),
2733                            userId));
2734        }
2735
2736        // Is user disallowed from modifying accounts?
2737        if (!canUserModifyAccounts(userId, callingUid)) {
2738            try {
2739                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2740                        "User is not allowed to add an account!");
2741            } catch (RemoteException re) {
2742            }
2743            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2744            return;
2745        }
2746        if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
2747            try {
2748                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2749                        "User cannot modify accounts of this type (policy).");
2750            } catch (RemoteException re) {
2751            }
2752            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2753                    userId);
2754            return;
2755        }
2756
2757        final int pid = Binder.getCallingPid();
2758        final int uid = Binder.getCallingUid();
2759        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2760        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2761        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2762
2763        long identityToken = clearCallingIdentity();
2764        try {
2765            UserAccounts accounts = getUserAccounts(userId);
2766            logRecordWithUid(
2767                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
2768            new Session(accounts, response, accountType, expectActivityLaunch,
2769                    true /* stripAuthTokenFromResult */, null /* accountName */,
2770                    false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2771                @Override
2772                public void run() throws RemoteException {
2773                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2774                            options);
2775                }
2776
2777                @Override
2778                protected String toDebugString(long now) {
2779                    return super.toDebugString(now) + ", addAccount"
2780                            + ", accountType " + accountType
2781                            + ", requiredFeatures "
2782                            + (requiredFeatures != null
2783                              ? TextUtils.join(",", requiredFeatures)
2784                              : null);
2785                }
2786            }.bind();
2787        } finally {
2788            restoreCallingIdentity(identityToken);
2789        }
2790    }
2791
2792    @Override
2793    public void startAddAccountSession(
2794            final IAccountManagerResponse response,
2795            final String accountType,
2796            final String authTokenType,
2797            final String[] requiredFeatures,
2798            final boolean expectActivityLaunch,
2799            final Bundle optionsIn) {
2800        Bundle.setDefusable(optionsIn, true);
2801        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2802            Log.v(TAG,
2803                    "startAddAccountSession: accountType " + accountType
2804                    + ", response " + response
2805                    + ", authTokenType " + authTokenType
2806                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2807                    + ", expectActivityLaunch " + expectActivityLaunch
2808                    + ", caller's uid " + Binder.getCallingUid()
2809                    + ", pid " + Binder.getCallingPid());
2810        }
2811        if (response == null) {
2812            throw new IllegalArgumentException("response is null");
2813        }
2814        if (accountType == null) {
2815            throw new IllegalArgumentException("accountType is null");
2816        }
2817
2818        final int uid = Binder.getCallingUid();
2819        // Only allow system to start session
2820        if (!isSystemUid(uid)) {
2821            String msg = String.format(
2822                    "uid %s cannot stat add account session.",
2823                    uid);
2824            throw new SecurityException(msg);
2825        }
2826
2827        final int userId = UserHandle.getUserId(uid);
2828        if (!canUserModifyAccounts(userId, uid)) {
2829            try {
2830                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2831                        "User is not allowed to add an account!");
2832            } catch (RemoteException re) {
2833            }
2834            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2835            return;
2836        }
2837        if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2838            try {
2839                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2840                        "User cannot modify accounts of this type (policy).");
2841            } catch (RemoteException re) {
2842            }
2843            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2844                    userId);
2845            return;
2846        }
2847        final int pid = Binder.getCallingPid();
2848        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2849        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2850        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2851
2852        // Check to see if the Password should be included to the caller.
2853        String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
2854        boolean isPasswordForwardingAllowed = isPermitted(
2855                callerPkg, uid, Manifest.permission.GET_PASSWORD);
2856
2857        long identityToken = clearCallingIdentity();
2858        try {
2859            UserAccounts accounts = getUserAccounts(userId);
2860            logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2861                    TABLE_ACCOUNTS, uid);
2862            new StartAccountSession(
2863                    accounts,
2864                    response,
2865                    accountType,
2866                    expectActivityLaunch,
2867                    null /* accountName */,
2868                    false /* authDetailsRequired */,
2869                    true /* updateLastAuthenticationTime */,
2870                    isPasswordForwardingAllowed) {
2871                @Override
2872                public void run() throws RemoteException {
2873                    mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2874                            requiredFeatures, options);
2875                }
2876
2877                @Override
2878                protected String toDebugString(long now) {
2879                    String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2880                    return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2881                            + accountType + ", requiredFeatures "
2882                            + (requiredFeatures != null ? requiredFeaturesStr : null);
2883                }
2884            }.bind();
2885        } finally {
2886            restoreCallingIdentity(identityToken);
2887        }
2888    }
2889
2890    /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2891    private abstract class StartAccountSession extends Session {
2892
2893        private final boolean mIsPasswordForwardingAllowed;
2894
2895        public StartAccountSession(
2896                UserAccounts accounts,
2897                IAccountManagerResponse response,
2898                String accountType,
2899                boolean expectActivityLaunch,
2900                String accountName,
2901                boolean authDetailsRequired,
2902                boolean updateLastAuthenticationTime,
2903                boolean isPasswordForwardingAllowed) {
2904            super(accounts, response, accountType, expectActivityLaunch,
2905                    true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2906                    updateLastAuthenticationTime);
2907            mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
2908        }
2909
2910        @Override
2911        public void onResult(Bundle result) {
2912            Bundle.setDefusable(result, true);
2913            mNumResults++;
2914            Intent intent = null;
2915            if (result != null
2916                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2917                checkKeyIntent(
2918                        Binder.getCallingUid(),
2919                        intent);
2920            }
2921            IAccountManagerResponse response;
2922            if (mExpectActivityLaunch && result != null
2923                    && result.containsKey(AccountManager.KEY_INTENT)) {
2924                response = mResponse;
2925            } else {
2926                response = getResponseAndClose();
2927            }
2928            if (response == null) {
2929                return;
2930            }
2931            if (result == null) {
2932                if (Log.isLoggable(TAG, Log.VERBOSE)) {
2933                    Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2934                            + response);
2935                }
2936                sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2937                        "null bundle returned");
2938                return;
2939            }
2940
2941            if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2942                // All AccountManager error codes are greater
2943                // than 0
2944                sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2945                        result.getString(AccountManager.KEY_ERROR_MESSAGE));
2946                return;
2947            }
2948
2949            // Omit passwords if the caller isn't permitted to see them.
2950            if (!mIsPasswordForwardingAllowed) {
2951                result.remove(AccountManager.KEY_PASSWORD);
2952            }
2953
2954            // Strip auth token from result.
2955            result.remove(AccountManager.KEY_AUTHTOKEN);
2956
2957            if (Log.isLoggable(TAG, Log.VERBOSE)) {
2958                Log.v(TAG,
2959                        getClass().getSimpleName() + " calling onResult() on response " + response);
2960            }
2961
2962            // Get the session bundle created by authenticator. The
2963            // bundle contains data necessary for finishing the session
2964            // later. The session bundle will be encrypted here and
2965            // decrypted later when trying to finish the session.
2966            Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
2967            if (sessionBundle != null) {
2968                String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2969                if (TextUtils.isEmpty(accountType)
2970                        || !mAccountType.equalsIgnoreCase(accountType)) {
2971                    Log.w(TAG, "Account type in session bundle doesn't match request.");
2972                }
2973                // Add accountType info to session bundle. This will
2974                // override any value set by authenticator.
2975                sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
2976
2977                // Encrypt session bundle before returning to caller.
2978                try {
2979                    CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2980                    Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
2981                    result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
2982                } catch (GeneralSecurityException e) {
2983                    if (Log.isLoggable(TAG, Log.DEBUG)) {
2984                        Log.v(TAG, "Failed to encrypt session bundle!", e);
2985                    }
2986                    sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2987                            "failed to encrypt session bundle");
2988                    return;
2989                }
2990            }
2991
2992            sendResponse(response, result);
2993        }
2994    }
2995
2996    @Override
2997    public void finishSessionAsUser(IAccountManagerResponse response,
2998            @NonNull Bundle sessionBundle,
2999            boolean expectActivityLaunch,
3000            Bundle appInfo,
3001            int userId) {
3002        Bundle.setDefusable(sessionBundle, true);
3003        int callingUid = Binder.getCallingUid();
3004        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3005            Log.v(TAG,
3006                    "finishSession: response "+ response
3007                            + ", expectActivityLaunch " + expectActivityLaunch
3008                            + ", caller's uid " + callingUid
3009                            + ", caller's user id " + UserHandle.getCallingUserId()
3010                            + ", pid " + Binder.getCallingPid()
3011                            + ", for user id " + userId);
3012        }
3013        if (response == null) {
3014            throw new IllegalArgumentException("response is null");
3015        }
3016
3017        // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3018        // Account type is added to it before encryption.
3019        if (sessionBundle == null || sessionBundle.size() == 0) {
3020            throw new IllegalArgumentException("sessionBundle is empty");
3021        }
3022
3023        // Only allow the system process to finish session for other users
3024        if (isCrossUser(callingUid, userId)) {
3025            throw new SecurityException(
3026                    String.format(
3027                            "User %s trying to finish session for %s without cross user permission",
3028                            UserHandle.getCallingUserId(),
3029                            userId));
3030        }
3031
3032        // Only allow system to finish session
3033        if (!isSystemUid(callingUid)) {
3034            String msg = String.format(
3035                    "uid %s cannot finish session because it's not system uid.",
3036                    callingUid);
3037            throw new SecurityException(msg);
3038        }
3039
3040        if (!canUserModifyAccounts(userId, callingUid)) {
3041            sendErrorResponse(response,
3042                    AccountManager.ERROR_CODE_USER_RESTRICTED,
3043                    "User is not allowed to add an account!");
3044            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3045            return;
3046        }
3047
3048        final int pid = Binder.getCallingPid();
3049        final Bundle decryptedBundle;
3050        final String accountType;
3051        // First decrypt session bundle to get account type for checking permission.
3052        try {
3053            CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3054            decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3055            if (decryptedBundle == null) {
3056                sendErrorResponse(
3057                        response,
3058                        AccountManager.ERROR_CODE_BAD_REQUEST,
3059                        "failed to decrypt session bundle");
3060                return;
3061            }
3062            accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3063            // Account type cannot be null. This should not happen if session bundle was created
3064            // properly by #StartAccountSession.
3065            if (TextUtils.isEmpty(accountType)) {
3066                sendErrorResponse(
3067                        response,
3068                        AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3069                        "accountType is empty");
3070                return;
3071            }
3072
3073            // If by any chances, decryptedBundle contains colliding keys with
3074            // system info
3075            // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3076            // update credentials flow, we should replace with the new values of the current call.
3077            if (appInfo != null) {
3078                decryptedBundle.putAll(appInfo);
3079            }
3080
3081            // Add info that may be used by add account or update credentials flow.
3082            decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
3083            decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3084        } catch (GeneralSecurityException e) {
3085            if (Log.isLoggable(TAG, Log.DEBUG)) {
3086                Log.v(TAG, "Failed to decrypt session bundle!", e);
3087            }
3088            sendErrorResponse(
3089                    response,
3090                    AccountManager.ERROR_CODE_BAD_REQUEST,
3091                    "failed to decrypt session bundle");
3092            return;
3093        }
3094
3095        if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
3096            sendErrorResponse(
3097                    response,
3098                    AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3099                    "User cannot modify accounts of this type (policy).");
3100            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3101                    userId);
3102            return;
3103        }
3104
3105        long identityToken = clearCallingIdentity();
3106        try {
3107            UserAccounts accounts = getUserAccounts(userId);
3108            logRecordWithUid(
3109                    accounts,
3110                    DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3111                    TABLE_ACCOUNTS,
3112                    callingUid);
3113            new Session(
3114                    accounts,
3115                    response,
3116                    accountType,
3117                    expectActivityLaunch,
3118                    true /* stripAuthTokenFromResult */,
3119                    null /* accountName */,
3120                    false /* authDetailsRequired */,
3121                    true /* updateLastAuthenticationTime */) {
3122                @Override
3123                public void run() throws RemoteException {
3124                    mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3125                }
3126
3127                @Override
3128                protected String toDebugString(long now) {
3129                    return super.toDebugString(now)
3130                            + ", finishSession"
3131                            + ", accountType " + accountType;
3132                }
3133            }.bind();
3134        } finally {
3135            restoreCallingIdentity(identityToken);
3136        }
3137    }
3138
3139    private void showCantAddAccount(int errorCode, int userId) {
3140        Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3141        cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3142        cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3143        long identityToken = clearCallingIdentity();
3144        try {
3145            mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
3146        } finally {
3147            restoreCallingIdentity(identityToken);
3148        }
3149    }
3150
3151    @Override
3152    public void confirmCredentialsAsUser(
3153            IAccountManagerResponse response,
3154            final Account account,
3155            final Bundle options,
3156            final boolean expectActivityLaunch,
3157            int userId) {
3158        Bundle.setDefusable(options, true);
3159        int callingUid = Binder.getCallingUid();
3160        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3161            Log.v(TAG, "confirmCredentials: " + account
3162                    + ", response " + response
3163                    + ", expectActivityLaunch " + expectActivityLaunch
3164                    + ", caller's uid " + callingUid
3165                    + ", pid " + Binder.getCallingPid());
3166        }
3167        // Only allow the system process to read accounts of other users
3168        if (isCrossUser(callingUid, userId)) {
3169            throw new SecurityException(
3170                    String.format(
3171                            "User %s trying to confirm account credentials for %s" ,
3172                            UserHandle.getCallingUserId(),
3173                            userId));
3174        }
3175        if (response == null) throw new IllegalArgumentException("response is null");
3176        if (account == null) throw new IllegalArgumentException("account is null");
3177        long identityToken = clearCallingIdentity();
3178        try {
3179            UserAccounts accounts = getUserAccounts(userId);
3180            new Session(accounts, response, account.type, expectActivityLaunch,
3181                    true /* stripAuthTokenFromResult */, account.name,
3182                    true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
3183                @Override
3184                public void run() throws RemoteException {
3185                    mAuthenticator.confirmCredentials(this, account, options);
3186                }
3187                @Override
3188                protected String toDebugString(long now) {
3189                    return super.toDebugString(now) + ", confirmCredentials"
3190                            + ", " + account;
3191                }
3192            }.bind();
3193        } finally {
3194            restoreCallingIdentity(identityToken);
3195        }
3196    }
3197
3198    @Override
3199    public void updateCredentials(IAccountManagerResponse response, final Account account,
3200            final String authTokenType, final boolean expectActivityLaunch,
3201            final Bundle loginOptions) {
3202        Bundle.setDefusable(loginOptions, true);
3203        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3204            Log.v(TAG, "updateCredentials: " + account
3205                    + ", response " + response
3206                    + ", authTokenType " + authTokenType
3207                    + ", expectActivityLaunch " + expectActivityLaunch
3208                    + ", caller's uid " + Binder.getCallingUid()
3209                    + ", pid " + Binder.getCallingPid());
3210        }
3211        if (response == null) throw new IllegalArgumentException("response is null");
3212        if (account == null) throw new IllegalArgumentException("account is null");
3213        int userId = UserHandle.getCallingUserId();
3214        long identityToken = clearCallingIdentity();
3215        try {
3216            UserAccounts accounts = getUserAccounts(userId);
3217            new Session(accounts, response, account.type, expectActivityLaunch,
3218                    true /* stripAuthTokenFromResult */, account.name,
3219                    false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
3220                @Override
3221                public void run() throws RemoteException {
3222                    mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3223                }
3224                @Override
3225                protected String toDebugString(long now) {
3226                    if (loginOptions != null) loginOptions.keySet();
3227                    return super.toDebugString(now) + ", updateCredentials"
3228                            + ", " + account
3229                            + ", authTokenType " + authTokenType
3230                            + ", loginOptions " + loginOptions;
3231                }
3232            }.bind();
3233        } finally {
3234            restoreCallingIdentity(identityToken);
3235        }
3236    }
3237
3238    @Override
3239    public void startUpdateCredentialsSession(
3240            IAccountManagerResponse response,
3241            final Account account,
3242            final String authTokenType,
3243            final boolean expectActivityLaunch,
3244            final Bundle loginOptions) {
3245        Bundle.setDefusable(loginOptions, true);
3246        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3247            Log.v(TAG,
3248                    "startUpdateCredentialsSession: " + account + ", response " + response
3249                            + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3250                            + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3251                            + ", pid " + Binder.getCallingPid());
3252        }
3253        if (response == null) {
3254            throw new IllegalArgumentException("response is null");
3255        }
3256        if (account == null) {
3257            throw new IllegalArgumentException("account is null");
3258        }
3259
3260        final int uid = Binder.getCallingUid();
3261        // Only allow system to start session
3262        if (!isSystemUid(uid)) {
3263            String msg = String.format(
3264                    "uid %s cannot start update credentials session.",
3265                    uid);
3266            throw new SecurityException(msg);
3267        }
3268
3269        int userId = UserHandle.getCallingUserId();
3270
3271        // Check to see if the Password should be included to the caller.
3272        String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3273        boolean isPasswordForwardingAllowed = isPermitted(
3274                callerPkg, uid, Manifest.permission.GET_PASSWORD);
3275
3276        long identityToken = clearCallingIdentity();
3277        try {
3278            UserAccounts accounts = getUserAccounts(userId);
3279            new StartAccountSession(
3280                    accounts,
3281                    response,
3282                    account.type,
3283                    expectActivityLaunch,
3284                    account.name,
3285                    false /* authDetailsRequired */,
3286                    true /* updateLastCredentialTime */,
3287                    isPasswordForwardingAllowed) {
3288                @Override
3289                public void run() throws RemoteException {
3290                    mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3291                            loginOptions);
3292                }
3293
3294                @Override
3295                protected String toDebugString(long now) {
3296                    if (loginOptions != null)
3297                        loginOptions.keySet();
3298                    return super.toDebugString(now)
3299                            + ", startUpdateCredentialsSession"
3300                            + ", " + account
3301                            + ", authTokenType " + authTokenType
3302                            + ", loginOptions " + loginOptions;
3303                }
3304            }.bind();
3305        } finally {
3306            restoreCallingIdentity(identityToken);
3307        }
3308    }
3309
3310    @Override
3311    public void isCredentialsUpdateSuggested(
3312            IAccountManagerResponse response,
3313            final Account account,
3314            final String statusToken) {
3315        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3316            Log.v(TAG,
3317                    "isCredentialsUpdateSuggested: " + account + ", response " + response
3318                            + ", caller's uid " + Binder.getCallingUid()
3319                            + ", pid " + Binder.getCallingPid());
3320        }
3321        if (response == null) {
3322            throw new IllegalArgumentException("response is null");
3323        }
3324        if (account == null) {
3325            throw new IllegalArgumentException("account is null");
3326        }
3327        if (TextUtils.isEmpty(statusToken)) {
3328            throw new IllegalArgumentException("status token is empty");
3329        }
3330
3331        int uid = Binder.getCallingUid();
3332        // Only allow system to start session
3333        if (!isSystemUid(uid)) {
3334            String msg = String.format(
3335                    "uid %s cannot stat add account session.",
3336                    uid);
3337            throw new SecurityException(msg);
3338        }
3339
3340        int usrId = UserHandle.getCallingUserId();
3341        long identityToken = clearCallingIdentity();
3342        try {
3343            UserAccounts accounts = getUserAccounts(usrId);
3344            new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3345                    false /* stripAuthTokenFromResult */, account.name,
3346                    false /* authDetailsRequired */) {
3347                @Override
3348                protected String toDebugString(long now) {
3349                    return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3350                            + ", " + account;
3351                }
3352
3353                @Override
3354                public void run() throws RemoteException {
3355                    mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3356                }
3357
3358                @Override
3359                public void onResult(Bundle result) {
3360                    Bundle.setDefusable(result, true);
3361                    IAccountManagerResponse response = getResponseAndClose();
3362                    if (response == null) {
3363                        return;
3364                    }
3365
3366                    if (result == null) {
3367                        sendErrorResponse(
3368                                response,
3369                                AccountManager.ERROR_CODE_INVALID_RESPONSE,
3370                                "null bundle");
3371                        return;
3372                    }
3373
3374                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3375                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3376                                + response);
3377                    }
3378                    // Check to see if an error occurred. We know if an error occurred because all
3379                    // error codes are greater than 0.
3380                    if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3381                        sendErrorResponse(response,
3382                                result.getInt(AccountManager.KEY_ERROR_CODE),
3383                                result.getString(AccountManager.KEY_ERROR_MESSAGE));
3384                        return;
3385                    }
3386                    if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3387                        sendErrorResponse(
3388                                response,
3389                                AccountManager.ERROR_CODE_INVALID_RESPONSE,
3390                                "no result in response");
3391                        return;
3392                    }
3393                    final Bundle newResult = new Bundle();
3394                    newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3395                            result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3396                    sendResponse(response, newResult);
3397                }
3398            }.bind();
3399        } finally {
3400            restoreCallingIdentity(identityToken);
3401        }
3402    }
3403
3404    @Override
3405    public void editProperties(IAccountManagerResponse response, final String accountType,
3406            final boolean expectActivityLaunch) {
3407        final int callingUid = Binder.getCallingUid();
3408        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3409            Log.v(TAG, "editProperties: accountType " + accountType
3410                    + ", response " + response
3411                    + ", expectActivityLaunch " + expectActivityLaunch
3412                    + ", caller's uid " + callingUid
3413                    + ", pid " + Binder.getCallingPid());
3414        }
3415        if (response == null) throw new IllegalArgumentException("response is null");
3416        if (accountType == null) throw new IllegalArgumentException("accountType is null");
3417        int userId = UserHandle.getCallingUserId();
3418        if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
3419            String msg = String.format(
3420                    "uid %s cannot edit authenticator properites for account type: %s",
3421                    callingUid,
3422                    accountType);
3423            throw new SecurityException(msg);
3424        }
3425        long identityToken = clearCallingIdentity();
3426        try {
3427            UserAccounts accounts = getUserAccounts(userId);
3428            new Session(accounts, response, accountType, expectActivityLaunch,
3429                    true /* stripAuthTokenFromResult */, null /* accountName */,
3430                    false /* authDetailsRequired */) {
3431                @Override
3432                public void run() throws RemoteException {
3433                    mAuthenticator.editProperties(this, mAccountType);
3434                }
3435                @Override
3436                protected String toDebugString(long now) {
3437                    return super.toDebugString(now) + ", editProperties"
3438                            + ", accountType " + accountType;
3439                }
3440            }.bind();
3441        } finally {
3442            restoreCallingIdentity(identityToken);
3443        }
3444    }
3445
3446    @Override
3447    public boolean hasAccountAccess(@NonNull Account account,  @NonNull String packageName,
3448            @NonNull UserHandle userHandle) {
3449        if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
3450            throw new SecurityException("Can be called only by system UID");
3451        }
3452        Preconditions.checkNotNull(account, "account cannot be null");
3453        Preconditions.checkNotNull(packageName, "packageName cannot be null");
3454        Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3455
3456        final int userId = userHandle.getIdentifier();
3457
3458        Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3459
3460        try {
3461            final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3462            return hasAccountAccess(account, packageName, uid);
3463        } catch (NameNotFoundException e) {
3464            return false;
3465        }
3466    }
3467
3468    private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3469            int uid) {
3470        if (packageName == null) {
3471            String[] packageNames = mPackageManager.getPackagesForUid(uid);
3472            if (ArrayUtils.isEmpty(packageNames)) {
3473                return false;
3474            }
3475            // For app op checks related to permissions all packages in the UID
3476            // have the same app op state, so doesn't matter which one we pick.
3477            packageName = packageNames[0];
3478        }
3479
3480        // Use null token which means any token. Having a token means the package
3481        // is trusted by the authenticator, hence it is fine to access the account.
3482        if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
3483            return true;
3484        }
3485        // In addition to the permissions required to get an auth token we also allow
3486        // the account to be accessed by holders of the get accounts permissions.
3487        return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3488                || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3489    }
3490
3491    private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3492        final long identity = Binder.clearCallingIdentity();
3493        try {
3494            IPackageManager pm = ActivityThread.getPackageManager();
3495            if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3496                return false;
3497            }
3498            final int opCode = AppOpsManager.permissionToOpCode(permission);
3499            return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3500                    opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3501        } catch (RemoteException e) {
3502            /* ignore - local call */
3503        } finally {
3504            Binder.restoreCallingIdentity(identity);
3505        }
3506        return false;
3507    }
3508
3509    @Override
3510    public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3511            @NonNull String packageName, @NonNull UserHandle userHandle) {
3512        if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
3513            throw new SecurityException("Can be called only by system UID");
3514        }
3515
3516        Preconditions.checkNotNull(account, "account cannot be null");
3517        Preconditions.checkNotNull(packageName, "packageName cannot be null");
3518        Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3519
3520        final int userId = userHandle.getIdentifier();
3521
3522        Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3523
3524        final int uid;
3525        try {
3526            uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3527        } catch (NameNotFoundException e) {
3528            Slog.e(TAG, "Unknown package " + packageName);
3529            return null;
3530        }
3531
3532        Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3533
3534        final long identity = Binder.clearCallingIdentity();
3535        try {
3536            return PendingIntent.getActivityAsUser(
3537                    mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3538                            | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3539                    null, new UserHandle(userId)).getIntentSender();
3540        } finally {
3541            Binder.restoreCallingIdentity(identity);
3542        }
3543    }
3544
3545    private Intent newRequestAccountAccessIntent(Account account, String packageName,
3546            int uid, RemoteCallback callback) {
3547        return newGrantCredentialsPermissionIntent(account, packageName, uid,
3548                new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3549            @Override
3550            public void onResult(Bundle value) throws RemoteException {
3551                handleAuthenticatorResponse(true);
3552            }
3553
3554            @Override
3555            public void onRequestContinued() {
3556                /* ignore */
3557            }
3558
3559            @Override
3560            public void onError(int errorCode, String errorMessage) throws RemoteException {
3561                handleAuthenticatorResponse(false);
3562            }
3563
3564            private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3565                cancelNotification(getCredentialPermissionNotificationId(account,
3566                        AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
3567                        UserHandle.getUserHandleForUid(uid));
3568                if (callback != null) {
3569                    Bundle result = new Bundle();
3570                    result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3571                    callback.sendResult(result);
3572                }
3573            }
3574        }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
3575    }
3576
3577    @Override
3578    public boolean someUserHasAccount(@NonNull final Account account) {
3579        if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3580            throw new SecurityException("Only system can check for accounts across users");
3581        }
3582        final long token = Binder.clearCallingIdentity();
3583        try {
3584            AccountAndUser[] allAccounts = getAllAccounts();
3585            for (int i = allAccounts.length - 1; i >= 0; i--) {
3586                if (allAccounts[i].account.equals(account)) {
3587                    return true;
3588                }
3589            }
3590            return false;
3591        } finally {
3592            Binder.restoreCallingIdentity(token);
3593        }
3594    }
3595
3596    private class GetAccountsByTypeAndFeatureSession extends Session {
3597        private final String[] mFeatures;
3598        private volatile Account[] mAccountsOfType = null;
3599        private volatile ArrayList<Account> mAccountsWithFeatures = null;
3600        private volatile int mCurrentAccount = 0;
3601        private final int mCallingUid;
3602
3603        public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
3604                IAccountManagerResponse response, String type, String[] features, int callingUid) {
3605            super(accounts, response, type, false /* expectActivityLaunch */,
3606                    true /* stripAuthTokenFromResult */, null /* accountName */,
3607                    false /* authDetailsRequired */);
3608            mCallingUid = callingUid;
3609            mFeatures = features;
3610        }
3611
3612        @Override
3613        public void run() throws RemoteException {
3614            synchronized (mAccounts.cacheLock) {
3615                mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3616                        null);
3617            }
3618            // check whether each account matches the requested features
3619            mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
3620            mCurrentAccount = 0;
3621
3622            checkAccount();
3623        }
3624
3625        public void checkAccount() {
3626            if (mCurrentAccount >= mAccountsOfType.length) {
3627                sendResult();
3628                return;
3629            }
3630
3631            final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3632            if (accountAuthenticator == null) {
3633                // It is possible that the authenticator has died, which is indicated by
3634                // mAuthenticator being set to null. If this happens then just abort.
3635                // There is no need to send back a result or error in this case since
3636                // that already happened when mAuthenticator was cleared.
3637                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3638                    Log.v(TAG, "checkAccount: aborting session since we are no longer"
3639                            + " connected to the authenticator, " + toDebugString());
3640                }
3641                return;
3642            }
3643            try {
3644                accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
3645            } catch (RemoteException e) {
3646                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
3647            }
3648        }
3649
3650        @Override
3651        public void onResult(Bundle result) {
3652            Bundle.setDefusable(result, true);
3653            mNumResults++;
3654            if (result == null) {
3655                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
3656                return;
3657            }
3658            if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
3659                mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3660            }
3661            mCurrentAccount++;
3662            checkAccount();
3663        }
3664
3665        public void sendResult() {
3666            IAccountManagerResponse response = getResponseAndClose();
3667            if (response != null) {
3668                try {
3669                    Account[] accounts = new Account[mAccountsWithFeatures.size()];
3670                    for (int i = 0; i < accounts.length; i++) {
3671                        accounts[i] = mAccountsWithFeatures.get(i);
3672                    }
3673                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3674                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3675                                + response);
3676                    }
3677                    Bundle result = new Bundle();
3678                    result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3679                    response.onResult(result);
3680                } catch (RemoteException e) {
3681                    // if the caller is dead then there is no one to care about remote exceptions
3682                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3683                        Log.v(TAG, "failure while notifying response", e);
3684                    }
3685                }
3686            }
3687        }
3688
3689
3690        @Override
3691        protected String toDebugString(long now) {
3692            return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3693                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3694        }
3695    }
3696
3697    /**
3698     * Returns the accounts visible to the client within the context of a specific user
3699     * @hide
3700     */
3701    @NonNull
3702    public Account[] getAccounts(int userId, String opPackageName) {
3703        int callingUid = Binder.getCallingUid();
3704        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3705                opPackageName);
3706        if (visibleAccountTypes.isEmpty()) {
3707            return new Account[0];
3708        }
3709        long identityToken = clearCallingIdentity();
3710        try {
3711            UserAccounts accounts = getUserAccounts(userId);
3712            return getAccountsInternal(
3713                    accounts,
3714                    callingUid,
3715                    null,  // packageName
3716                    visibleAccountTypes);
3717        } finally {
3718            restoreCallingIdentity(identityToken);
3719        }
3720    }
3721
3722    /**
3723     * Returns accounts for all running users.
3724     *
3725     * @hide
3726     */
3727    @NonNull
3728    public AccountAndUser[] getRunningAccounts() {
3729        final int[] runningUserIds;
3730        try {
3731            runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3732        } catch (RemoteException e) {
3733            // Running in system_server; should never happen
3734            throw new RuntimeException(e);
3735        }
3736        return getAccounts(runningUserIds);
3737    }
3738
3739    /** {@hide} */
3740    @NonNull
3741    public AccountAndUser[] getAllAccounts() {
3742        final List<UserInfo> users = getUserManager().getUsers(true);
3743        final int[] userIds = new int[users.size()];
3744        for (int i = 0; i < userIds.length; i++) {
3745            userIds[i] = users.get(i).id;
3746        }
3747        return getAccounts(userIds);
3748    }
3749
3750    @NonNull
3751    private AccountAndUser[] getAccounts(int[] userIds) {
3752        final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
3753        for (int userId : userIds) {
3754            UserAccounts userAccounts = getUserAccounts(userId);
3755            if (userAccounts == null) continue;
3756            synchronized (userAccounts.cacheLock) {
3757                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3758                        Binder.getCallingUid(), null);
3759                for (int a = 0; a < accounts.length; a++) {
3760                    runningAccounts.add(new AccountAndUser(accounts[a], userId));
3761                }
3762            }
3763        }
3764
3765        AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3766        return runningAccounts.toArray(accountsArray);
3767    }
3768
3769    @Override
3770    @NonNull
3771    public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3772        return getAccountsAsUser(type, userId, null, -1, opPackageName);
3773    }
3774
3775    @NonNull
3776    private Account[] getAccountsAsUser(
3777            String type,
3778            int userId,
3779            String callingPackage,
3780            int packageUid,
3781            String opPackageName) {
3782        int callingUid = Binder.getCallingUid();
3783        // Only allow the system process to read accounts of other users
3784        if (userId != UserHandle.getCallingUserId()
3785                && callingUid != Process.myUid()
3786                && mContext.checkCallingOrSelfPermission(
3787                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3788                    != PackageManager.PERMISSION_GRANTED) {
3789            throw new SecurityException("User " + UserHandle.getCallingUserId()
3790                    + " trying to get account for " + userId);
3791        }
3792
3793        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3794            Log.v(TAG, "getAccounts: accountType " + type
3795                    + ", caller's uid " + Binder.getCallingUid()
3796                    + ", pid " + Binder.getCallingPid());
3797        }
3798        // If the original calling app was using the framework account chooser activity, we'll
3799        // be passed in the original caller's uid here, which is what should be used for filtering.
3800        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3801            callingUid = packageUid;
3802            opPackageName = callingPackage;
3803        }
3804
3805        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3806                opPackageName);
3807        if (visibleAccountTypes.isEmpty()
3808                || (type != null && !visibleAccountTypes.contains(type))) {
3809            return new Account[0];
3810        } else if (visibleAccountTypes.contains(type)) {
3811            // Prune the list down to just the requested type.
3812            visibleAccountTypes = new ArrayList<>();
3813            visibleAccountTypes.add(type);
3814        } // else aggregate all the visible accounts (it won't matter if the
3815          // list is empty).
3816
3817        long identityToken = clearCallingIdentity();
3818        try {
3819            UserAccounts accounts = getUserAccounts(userId);
3820            return getAccountsInternal(
3821                    accounts,
3822                    callingUid,
3823                    callingPackage,
3824                    visibleAccountTypes);
3825        } finally {
3826            restoreCallingIdentity(identityToken);
3827        }
3828    }
3829
3830    @NonNull
3831    private Account[] getAccountsInternal(
3832            UserAccounts userAccounts,
3833            int callingUid,
3834            String callingPackage,
3835            List<String> visibleAccountTypes) {
3836        synchronized (userAccounts.cacheLock) {
3837            ArrayList<Account> visibleAccounts = new ArrayList<>();
3838            for (String visibleType : visibleAccountTypes) {
3839                Account[] accountsForType = getAccountsFromCacheLocked(
3840                        userAccounts, visibleType, callingUid, callingPackage);
3841                if (accountsForType != null) {
3842                    visibleAccounts.addAll(Arrays.asList(accountsForType));
3843                }
3844            }
3845            Account[] result = new Account[visibleAccounts.size()];
3846            for (int i = 0; i < visibleAccounts.size(); i++) {
3847                result[i] = visibleAccounts.get(i);
3848            }
3849            return result;
3850        }
3851    }
3852
3853    @Override
3854    public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3855        checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
3856        Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3857        for (Account account : accounts) {
3858            addSharedAccountAsUser(account, userId);
3859        }
3860    }
3861
3862    private boolean addSharedAccountAsUser(Account account, int userId) {
3863        userId = handleIncomingUser(userId);
3864        UserAccounts accounts = getUserAccounts(userId);
3865        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3866        ContentValues values = new ContentValues();
3867        values.put(ACCOUNTS_NAME, account.name);
3868        values.put(ACCOUNTS_TYPE, account.type);
3869        db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3870                new String[] {account.name, account.type});
3871        long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3872        if (accountId < 0) {
3873            Log.w(TAG, "insertAccountIntoDatabase: " + account
3874                    + ", skipping the DB insert failed");
3875            return false;
3876        }
3877        logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
3878        return true;
3879    }
3880
3881    @Override
3882    public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3883        userId = handleIncomingUser(userId);
3884        UserAccounts accounts = getUserAccounts(userId);
3885        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3886        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3887        final ContentValues values = new ContentValues();
3888        values.put(ACCOUNTS_NAME, newName);
3889        int r = db.update(
3890                TABLE_SHARED_ACCOUNTS,
3891                values,
3892                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3893                new String[] { account.name, account.type });
3894        if (r > 0) {
3895            int callingUid = getCallingUid();
3896            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3897                    sharedTableAccountId, accounts, callingUid);
3898            // Recursively rename the account.
3899            renameAccountInternal(accounts, account, newName);
3900        }
3901        return r > 0;
3902    }
3903
3904    @Override
3905    public boolean removeSharedAccountAsUser(Account account, int userId) {
3906        return removeSharedAccountAsUser(account, userId, getCallingUid());
3907    }
3908
3909    private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
3910        userId = handleIncomingUser(userId);
3911        UserAccounts accounts = getUserAccounts(userId);
3912        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3913        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3914        int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3915                new String[] {account.name, account.type});
3916        if (r > 0) {
3917            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3918                    sharedTableAccountId, accounts, callingUid);
3919            removeAccountInternal(accounts, account, callingUid);
3920        }
3921        return r > 0;
3922    }
3923
3924    @Override
3925    public Account[] getSharedAccountsAsUser(int userId) {
3926        userId = handleIncomingUser(userId);
3927        UserAccounts accounts = getUserAccounts(userId);
3928        ArrayList<Account> accountList = new ArrayList<>();
3929        Cursor cursor = null;
3930        try {
3931            cursor = accounts.openHelper.getReadableDatabase()
3932                    .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3933                    null, null, null, null, null);
3934            if (cursor != null && cursor.moveToFirst()) {
3935                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3936                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3937                do {
3938                    accountList.add(new Account(cursor.getString(nameIndex),
3939                            cursor.getString(typeIndex)));
3940                } while (cursor.moveToNext());
3941            }
3942        } finally {
3943            if (cursor != null) {
3944                cursor.close();
3945            }
3946        }
3947        Account[] accountArray = new Account[accountList.size()];
3948        accountList.toArray(accountArray);
3949        return accountArray;
3950    }
3951
3952    @Override
3953    @NonNull
3954    public Account[] getAccounts(String type, String opPackageName) {
3955        return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
3956    }
3957
3958    @Override
3959    @NonNull
3960    public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
3961        int callingUid = Binder.getCallingUid();
3962        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3963            throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3964                    + callingUid + " with uid=" + uid);
3965        }
3966        return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3967                opPackageName);
3968    }
3969
3970    @Override
3971    @NonNull
3972    public Account[] getAccountsByTypeForPackage(String type, String packageName,
3973            String opPackageName) {
3974        int packageUid = -1;
3975        try {
3976            packageUid = AppGlobals.getPackageManager().getPackageUid(
3977                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3978                    UserHandle.getCallingUserId());
3979        } catch (RemoteException re) {
3980            Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3981            return new Account[0];
3982        }
3983        return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3984                packageUid, opPackageName);
3985    }
3986
3987    @Override
3988    public void getAccountsByFeatures(
3989            IAccountManagerResponse response,
3990            String type,
3991            String[] features,
3992            String opPackageName) {
3993        int callingUid = Binder.getCallingUid();
3994        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3995            Log.v(TAG, "getAccounts: accountType " + type
3996                    + ", response " + response
3997                    + ", features " + stringArrayToString(features)
3998                    + ", caller's uid " + callingUid
3999                    + ", pid " + Binder.getCallingPid());
4000        }
4001        if (response == null) throw new IllegalArgumentException("response is null");
4002        if (type == null) throw new IllegalArgumentException("accountType is null");
4003        int userId = UserHandle.getCallingUserId();
4004
4005        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4006                opPackageName);
4007        if (!visibleAccountTypes.contains(type)) {
4008            Bundle result = new Bundle();
4009            // Need to return just the accounts that are from matching signatures.
4010            result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4011            try {
4012                response.onResult(result);
4013            } catch (RemoteException e) {
4014                Log.e(TAG, "Cannot respond to caller do to exception." , e);
4015            }
4016            return;
4017        }
4018        long identityToken = clearCallingIdentity();
4019        try {
4020            UserAccounts userAccounts = getUserAccounts(userId);
4021            if (features == null || features.length == 0) {
4022                Account[] accounts;
4023                synchronized (userAccounts.cacheLock) {
4024                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
4025                }
4026                Bundle result = new Bundle();
4027                result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4028                onResult(response, result);
4029                return;
4030            }
4031            new GetAccountsByTypeAndFeatureSession(
4032                    userAccounts,
4033                    response,
4034                    type,
4035                    features,
4036                    callingUid).bind();
4037        } finally {
4038            restoreCallingIdentity(identityToken);
4039        }
4040    }
4041
4042    private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
4043        Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
4044                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
4045        try {
4046            if (cursor.moveToNext()) {
4047                return cursor.getLong(0);
4048            }
4049            return -1;
4050        } finally {
4051            cursor.close();
4052        }
4053    }
4054
4055    private long getAccountIdLocked(SQLiteDatabase db, Account account) {
4056        Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
4057                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
4058        try {
4059            if (cursor.moveToNext()) {
4060                return cursor.getLong(0);
4061            }
4062            return -1;
4063        } finally {
4064            cursor.close();
4065        }
4066    }
4067
4068    private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
4069        Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
4070                EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
4071                new String[]{key}, null, null, null);
4072        try {
4073            if (cursor.moveToNext()) {
4074                return cursor.getLong(0);
4075            }
4076            return -1;
4077        } finally {
4078            cursor.close();
4079        }
4080    }
4081
4082    @Override
4083    public void onAccountAccessed(String token) throws RemoteException {
4084        final int uid = Binder.getCallingUid();
4085        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
4086            return;
4087        }
4088        final int userId = UserHandle.getCallingUserId();
4089        final long identity = Binder.clearCallingIdentity();
4090        try {
4091            for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
4092                if (Objects.equals(account.getAccessId(), token)) {
4093                    // An app just accessed the account. At this point it knows about
4094                    // it and there is not need to hide this account from the app.
4095                    if (!hasAccountAccess(account, null, uid)) {
4096                        updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
4097                                uid, true);
4098                    }
4099                }
4100            }
4101        } finally {
4102            Binder.restoreCallingIdentity(identity);
4103        }
4104    }
4105
4106    private abstract class Session extends IAccountAuthenticatorResponse.Stub
4107            implements IBinder.DeathRecipient, ServiceConnection {
4108        IAccountManagerResponse mResponse;
4109        final String mAccountType;
4110        final boolean mExpectActivityLaunch;
4111        final long mCreationTime;
4112        final String mAccountName;
4113        // Indicates if we need to add auth details(like last credential time)
4114        final boolean mAuthDetailsRequired;
4115        // If set, we need to update the last authenticated time. This is
4116        // currently
4117        // used on
4118        // successful confirming credentials.
4119        final boolean mUpdateLastAuthenticatedTime;
4120
4121        public int mNumResults = 0;
4122        private int mNumRequestContinued = 0;
4123        private int mNumErrors = 0;
4124
4125        IAccountAuthenticator mAuthenticator = null;
4126
4127        private final boolean mStripAuthTokenFromResult;
4128        protected final UserAccounts mAccounts;
4129
4130        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4131                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4132                boolean authDetailsRequired) {
4133            this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4134                    accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4135        }
4136
4137        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4138                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4139                boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
4140            super();
4141            //if (response == null) throw new IllegalArgumentException("response is null");
4142            if (accountType == null) throw new IllegalArgumentException("accountType is null");
4143            mAccounts = accounts;
4144            mStripAuthTokenFromResult = stripAuthTokenFromResult;
4145            mResponse = response;
4146            mAccountType = accountType;
4147            mExpectActivityLaunch = expectActivityLaunch;
4148            mCreationTime = SystemClock.elapsedRealtime();
4149            mAccountName = accountName;
4150            mAuthDetailsRequired = authDetailsRequired;
4151            mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
4152
4153            synchronized (mSessions) {
4154                mSessions.put(toString(), this);
4155            }
4156            if (response != null) {
4157                try {
4158                    response.asBinder().linkToDeath(this, 0 /* flags */);
4159                } catch (RemoteException e) {
4160                    mResponse = null;
4161                    binderDied();
4162                }
4163            }
4164        }
4165
4166        IAccountManagerResponse getResponseAndClose() {
4167            if (mResponse == null) {
4168                // this session has already been closed
4169                return null;
4170            }
4171            IAccountManagerResponse response = mResponse;
4172            close(); // this clears mResponse so we need to save the response before this call
4173            return response;
4174        }
4175
4176        /**
4177         * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4178         * security policy.
4179         *
4180         * In particular we want to make sure that the Authenticator doesn't try to trick users
4181         * into launching aribtrary intents on the device via by tricking to click authenticator
4182         * supplied entries in the system Settings app.
4183         */
4184        protected void checkKeyIntent(
4185                int authUid,
4186                Intent intent) throws SecurityException {
4187            intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
4188                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
4189                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
4190                    | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
4191            long bid = Binder.clearCallingIdentity();
4192            try {
4193                PackageManager pm = mContext.getPackageManager();
4194                ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4195                ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4196                int targetUid = targetActivityInfo.applicationInfo.uid;
4197                if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) {
4198                    String pkgName = targetActivityInfo.packageName;
4199                    String activityName = targetActivityInfo.name;
4200                    String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4201                            + "does not share a signature with the supplying authenticator (%s).";
4202                    throw new SecurityException(
4203                            String.format(tmpl, activityName, pkgName, mAccountType));
4204                }
4205            } finally {
4206                Binder.restoreCallingIdentity(bid);
4207            }
4208        }
4209
4210        private void close() {
4211            synchronized (mSessions) {
4212                if (mSessions.remove(toString()) == null) {
4213                    // the session was already closed, so bail out now
4214                    return;
4215                }
4216            }
4217            if (mResponse != null) {
4218                // stop listening for response deaths
4219                mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4220
4221                // clear this so that we don't accidentally send any further results
4222                mResponse = null;
4223            }
4224            cancelTimeout();
4225            unbind();
4226        }
4227
4228        @Override
4229        public void binderDied() {
4230            mResponse = null;
4231            close();
4232        }
4233
4234        protected String toDebugString() {
4235            return toDebugString(SystemClock.elapsedRealtime());
4236        }
4237
4238        protected String toDebugString(long now) {
4239            return "Session: expectLaunch " + mExpectActivityLaunch
4240                    + ", connected " + (mAuthenticator != null)
4241                    + ", stats (" + mNumResults + "/" + mNumRequestContinued
4242                    + "/" + mNumErrors + ")"
4243                    + ", lifetime " + ((now - mCreationTime) / 1000.0);
4244        }
4245
4246        void bind() {
4247            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4248                Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4249            }
4250            if (!bindToAuthenticator(mAccountType)) {
4251                Log.d(TAG, "bind attempt failed for " + toDebugString());
4252                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
4253            }
4254        }
4255
4256        private void unbind() {
4257            if (mAuthenticator != null) {
4258                mAuthenticator = null;
4259                mContext.unbindService(this);
4260            }
4261        }
4262
4263        public void cancelTimeout() {
4264            mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
4265        }
4266
4267        @Override
4268        public void onServiceConnected(ComponentName name, IBinder service) {
4269            mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
4270            try {
4271                run();
4272            } catch (RemoteException e) {
4273                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4274                        "remote exception");
4275            }
4276        }
4277
4278        @Override
4279        public void onServiceDisconnected(ComponentName name) {
4280            mAuthenticator = null;
4281            IAccountManagerResponse response = getResponseAndClose();
4282            if (response != null) {
4283                try {
4284                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4285                            "disconnected");
4286                } catch (RemoteException e) {
4287                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4288                        Log.v(TAG, "Session.onServiceDisconnected: "
4289                                + "caught RemoteException while responding", e);
4290                    }
4291                }
4292            }
4293        }
4294
4295        public abstract void run() throws RemoteException;
4296
4297        public void onTimedOut() {
4298            IAccountManagerResponse response = getResponseAndClose();
4299            if (response != null) {
4300                try {
4301                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4302                            "timeout");
4303                } catch (RemoteException e) {
4304                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4305                        Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4306                                e);
4307                    }
4308                }
4309            }
4310        }
4311
4312        @Override
4313        public void onResult(Bundle result) {
4314            Bundle.setDefusable(result, true);
4315            mNumResults++;
4316            Intent intent = null;
4317            if (result != null) {
4318                boolean isSuccessfulConfirmCreds = result.getBoolean(
4319                        AccountManager.KEY_BOOLEAN_RESULT, false);
4320                boolean isSuccessfulUpdateCredsOrAddAccount =
4321                        result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4322                        && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
4323                // We should only update lastAuthenticated time, if
4324                // mUpdateLastAuthenticatedTime is true and the confirmRequest
4325                // or updateRequest was successful
4326                boolean needUpdate = mUpdateLastAuthenticatedTime
4327                        && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
4328                if (needUpdate || mAuthDetailsRequired) {
4329                    boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4330                    if (needUpdate && accountPresent) {
4331                        updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4332                    }
4333                    if (mAuthDetailsRequired) {
4334                        long lastAuthenticatedTime = -1;
4335                        if (accountPresent) {
4336                            lastAuthenticatedTime = DatabaseUtils.longForQuery(
4337                                    mAccounts.openHelper.getReadableDatabase(),
4338                                    "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4339                                            + " FROM " +
4340                                            TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4341                                            + ACCOUNTS_TYPE + "=?",
4342                                    new String[] {
4343                                            mAccountName, mAccountType
4344                                    });
4345                        }
4346                        result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
4347                                lastAuthenticatedTime);
4348                    }
4349                }
4350            }
4351            if (result != null
4352                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
4353                checkKeyIntent(
4354                        Binder.getCallingUid(),
4355                        intent);
4356            }
4357            if (result != null
4358                    && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
4359                String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4360                String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
4361                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4362                    Account account = new Account(accountName, accountType);
4363                    cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4364                            new UserHandle(mAccounts.userId));
4365                }
4366            }
4367            IAccountManagerResponse response;
4368            if (mExpectActivityLaunch && result != null
4369                    && result.containsKey(AccountManager.KEY_INTENT)) {
4370                response = mResponse;
4371            } else {
4372                response = getResponseAndClose();
4373            }
4374            if (response != null) {
4375                try {
4376                    if (result == null) {
4377                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
4378                            Log.v(TAG, getClass().getSimpleName()
4379                                    + " calling onError() on response " + response);
4380                        }
4381                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
4382                                "null bundle returned");
4383                    } else {
4384                        if (mStripAuthTokenFromResult) {
4385                            result.remove(AccountManager.KEY_AUTHTOKEN);
4386                        }
4387                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
4388                            Log.v(TAG, getClass().getSimpleName()
4389                                    + " calling onResult() on response " + response);
4390                        }
4391                        if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4392                                (intent == null)) {
4393                            // All AccountManager error codes are greater than 0
4394                            response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4395                                    result.getString(AccountManager.KEY_ERROR_MESSAGE));
4396                        } else {
4397                            response.onResult(result);
4398                        }
4399                    }
4400                } catch (RemoteException e) {
4401                    // if the caller is dead then there is no one to care about remote exceptions
4402                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4403                        Log.v(TAG, "failure while notifying response", e);
4404                    }
4405                }
4406            }
4407        }
4408
4409        @Override
4410        public void onRequestContinued() {
4411            mNumRequestContinued++;
4412        }
4413
4414        @Override
4415        public void onError(int errorCode, String errorMessage) {
4416            mNumErrors++;
4417            IAccountManagerResponse response = getResponseAndClose();
4418            if (response != null) {
4419                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4420                    Log.v(TAG, getClass().getSimpleName()
4421                            + " calling onError() on response " + response);
4422                }
4423                try {
4424                    response.onError(errorCode, errorMessage);
4425                } catch (RemoteException e) {
4426                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4427                        Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4428                    }
4429                }
4430            } else {
4431                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4432                    Log.v(TAG, "Session.onError: already closed");
4433                }
4434            }
4435        }
4436
4437        /**
4438         * find the component name for the authenticator and initiate a bind
4439         * if no authenticator or the bind fails then return false, otherwise return true
4440         */
4441        private boolean bindToAuthenticator(String authenticatorType) {
4442            final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4443            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4444                    AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
4445            if (authenticatorInfo == null) {
4446                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4447                    Log.v(TAG, "there is no authenticator for " + authenticatorType
4448                            + ", bailing out");
4449                }
4450                return false;
4451            }
4452
4453            if (!isLocalUnlockedUser(mAccounts.userId)
4454                    && !authenticatorInfo.componentInfo.directBootAware) {
4455                Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4456                        + " which isn't encryption aware");
4457                return false;
4458            }
4459
4460            Intent intent = new Intent();
4461            intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4462            intent.setComponent(authenticatorInfo.componentName);
4463            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4464                Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4465            }
4466            if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
4467                    UserHandle.of(mAccounts.userId))) {
4468                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4469                    Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4470                }
4471                return false;
4472            }
4473
4474            return true;
4475        }
4476    }
4477
4478    class MessageHandler extends Handler {
4479        MessageHandler(Looper looper) {
4480            super(looper);
4481        }
4482
4483        @Override
4484        public void handleMessage(Message msg) {
4485            switch (msg.what) {
4486                case MESSAGE_TIMED_OUT:
4487                    Session session = (Session)msg.obj;
4488                    session.onTimedOut();
4489                    break;
4490
4491                case MESSAGE_COPY_SHARED_ACCOUNT:
4492                    copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
4493                    break;
4494
4495                default:
4496                    throw new IllegalStateException("unhandled message: " + msg.what);
4497            }
4498        }
4499    }
4500
4501    @VisibleForTesting
4502    String getPreNDatabaseName(int userId) {
4503        File systemDir = Environment.getDataSystemDirectory();
4504        File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4505                PRE_N_DATABASE_NAME);
4506        if (userId == 0) {
4507            // Migrate old file, if it exists, to the new location.
4508            // Make sure the new file doesn't already exist. A dummy file could have been
4509            // accidentally created in the old location, causing the new one to become corrupted
4510            // as well.
4511            File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
4512            if (oldFile.exists() && !databaseFile.exists()) {
4513                // Check for use directory; create if it doesn't exist, else renameTo will fail
4514                File userDir = Environment.getUserSystemDirectory(userId);
4515                if (!userDir.exists()) {
4516                    if (!userDir.mkdirs()) {
4517                        throw new IllegalStateException("User dir cannot be created: " + userDir);
4518                    }
4519                }
4520                if (!oldFile.renameTo(databaseFile)) {
4521                    throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4522                }
4523            }
4524        }
4525        return databaseFile.getPath();
4526    }
4527
4528    @VisibleForTesting
4529    String getDeDatabaseName(int userId) {
4530        File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4531                DE_DATABASE_NAME);
4532        return databaseFile.getPath();
4533    }
4534
4535    @VisibleForTesting
4536    String getCeDatabaseName(int userId) {
4537        File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4538                CE_DATABASE_NAME);
4539        return databaseFile.getPath();
4540    }
4541
4542    private static class DebugDbHelper{
4543        private DebugDbHelper() {
4544        }
4545
4546        private static String TABLE_DEBUG = "debug_table";
4547
4548        // Columns for the table
4549        private static String ACTION_TYPE = "action_type";
4550        private static String TIMESTAMP = "time";
4551        private static String CALLER_UID = "caller_uid";
4552        private static String TABLE_NAME = "table_name";
4553        private static String KEY = "primary_key";
4554
4555        // These actions correspond to the occurrence of real actions. Since
4556        // these are called by the authenticators, the uid associated will be
4557        // of the authenticator.
4558        private static String ACTION_SET_PASSWORD = "action_set_password";
4559        private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4560        private static String ACTION_ACCOUNT_ADD = "action_account_add";
4561        private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
4562        private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
4563        private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4564        private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4565
4566        // These actions don't necessarily correspond to any action on
4567        // accountDb taking place. As an example, there might be a request for
4568        // addingAccount, which might not lead to addition of account on grounds
4569        // of bad authentication. We will still be logging it to keep track of
4570        // who called.
4571        private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4572        private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
4573        private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
4574
4575        //This action doesn't add account to accountdb. Account is only
4576        // added in finishSession which may be in a different user profile.
4577        private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
4578        private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4579                "action_called_account_session_finish";
4580
4581        private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4582
4583        private static void createDebugTable(SQLiteDatabase db) {
4584            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4585                    + ACCOUNTS_ID + " INTEGER,"
4586                    + ACTION_TYPE + " TEXT NOT NULL, "
4587                    + TIMESTAMP + " DATETIME,"
4588                    + CALLER_UID + " INTEGER NOT NULL,"
4589                    + TABLE_NAME + " TEXT NOT NULL,"
4590                    + KEY + " INTEGER PRIMARY KEY)");
4591            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4592        }
4593    }
4594
4595    private void logRecord(UserAccounts accounts, String action, String tableName) {
4596        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4597        logRecord(db, action, tableName, -1, accounts);
4598    }
4599
4600    private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4601        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4602        logRecord(db, action, tableName, -1, accounts, uid);
4603    }
4604
4605    /*
4606     * This function receives an opened writable database.
4607     */
4608    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4609            UserAccounts userAccount) {
4610        logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4611    }
4612
4613    /*
4614     * This function receives an opened writable database.
4615     */
4616    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4617            UserAccounts userAccount, int callingUid) {
4618        SQLiteStatement logStatement = userAccount.statementForLogging;
4619        logStatement.bindLong(1, accountId);
4620        logStatement.bindString(2, action);
4621        logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
4622        logStatement.bindLong(4, callingUid);
4623        logStatement.bindString(5, tableName);
4624        logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
4625        logStatement.execute();
4626        logStatement.clearBindings();
4627        userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
4628                % MAX_DEBUG_DB_SIZE;
4629    }
4630
4631    /*
4632     * This should only be called once to compile the sql statement for logging
4633     * and to find the insertion point.
4634     */
4635    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
4636            UserAccounts userAccount) {
4637        // Initialize the count if not done earlier.
4638        int size = (int) getDebugTableRowCount(db);
4639        if (size >= MAX_DEBUG_DB_SIZE) {
4640            // Table is full, and we need to find the point where to insert.
4641            userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
4642        } else {
4643            userAccount.debugDbInsertionPoint = size;
4644        }
4645        compileSqlStatementForLogging(db, userAccount);
4646    }
4647
4648    private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
4649        String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
4650                + " VALUES (?,?,?,?,?,?)";
4651        userAccount.statementForLogging = db.compileStatement(sql);
4652    }
4653
4654    private long getDebugTableRowCount(SQLiteDatabase db) {
4655        String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
4656        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4657    }
4658
4659    /*
4660     * Finds the row key where the next insertion should take place. This should
4661     * be invoked only if the table has reached its full capacity.
4662     */
4663    private long getDebugTableInsertionPoint(SQLiteDatabase db) {
4664        // This query finds the smallest timestamp value (and if 2 records have
4665        // same timestamp, the choose the lower id).
4666        String queryCountDebugDbRows = new StringBuilder()
4667                .append("SELECT ").append(DebugDbHelper.KEY)
4668                .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
4669                .append(" ORDER BY ")
4670                .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
4671                .append(" LIMIT 1")
4672                .toString();
4673        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4674    }
4675
4676    static class PreNDatabaseHelper extends SQLiteOpenHelper {
4677        private final Context mContext;
4678        private final int mUserId;
4679
4680        public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
4681            super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
4682            mContext = context;
4683            mUserId = userId;
4684        }
4685
4686        @Override
4687        public void onCreate(SQLiteDatabase db) {
4688            // We use PreNDatabaseHelper only if pre-N db exists
4689            throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
4690        }
4691
4692        private void createSharedAccountsTable(SQLiteDatabase db) {
4693            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4694                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4695                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4696                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4697                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4698        }
4699
4700        private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4701            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4702                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4703        }
4704
4705        private void addOldAccountNameColumn(SQLiteDatabase db) {
4706            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4707        }
4708
4709        private void addDebugTable(SQLiteDatabase db) {
4710            DebugDbHelper.createDebugTable(db);
4711        }
4712
4713        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4714            db.execSQL(""
4715                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4716                    + " BEGIN"
4717                    + "   DELETE FROM " + TABLE_AUTHTOKENS
4718                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4719                    + "   DELETE FROM " + TABLE_EXTRAS
4720                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4721                    + "   DELETE FROM " + TABLE_GRANTS
4722                    + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4723                    + " END");
4724        }
4725
4726        private void createGrantsTable(SQLiteDatabase db) {
4727            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4728                    + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4729                    + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4730                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4731                    + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4732                    +   "," + GRANTS_GRANTEE_UID + "))");
4733        }
4734
4735        private void populateMetaTableWithAuthTypeAndUID(
4736                SQLiteDatabase db,
4737                Map<String, Integer> authTypeAndUIDMap) {
4738            Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator();
4739            while (iterator.hasNext()) {
4740                Entry<String, Integer> entry = iterator.next();
4741                ContentValues values = new ContentValues();
4742                values.put(META_KEY,
4743                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
4744                values.put(META_VALUE, entry.getValue());
4745                db.insert(TABLE_META, null, values);
4746            }
4747        }
4748
4749        /**
4750         * Pre-N database may need an upgrade before splitting
4751         */
4752        @Override
4753        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4754            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4755
4756            if (oldVersion == 1) {
4757                // no longer need to do anything since the work is done
4758                // when upgrading from version 2
4759                oldVersion++;
4760            }
4761
4762            if (oldVersion == 2) {
4763                createGrantsTable(db);
4764                db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4765                createAccountsDeletionTrigger(db);
4766                oldVersion++;
4767            }
4768
4769            if (oldVersion == 3) {
4770                db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4771                        " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4772                oldVersion++;
4773            }
4774
4775            if (oldVersion == 4) {
4776                createSharedAccountsTable(db);
4777                oldVersion++;
4778            }
4779
4780            if (oldVersion == 5) {
4781                addOldAccountNameColumn(db);
4782                oldVersion++;
4783            }
4784
4785            if (oldVersion == 6) {
4786                addLastSuccessfullAuthenticatedTimeColumn(db);
4787                oldVersion++;
4788            }
4789
4790            if (oldVersion == 7) {
4791                addDebugTable(db);
4792                oldVersion++;
4793            }
4794
4795            if (oldVersion == 8) {
4796                populateMetaTableWithAuthTypeAndUID(
4797                        db,
4798                        AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
4799                oldVersion++;
4800            }
4801
4802            if (oldVersion != newVersion) {
4803                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4804            }
4805        }
4806
4807        @Override
4808        public void onOpen(SQLiteDatabase db) {
4809            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4810        }
4811    }
4812
4813    static class DeDatabaseHelper extends SQLiteOpenHelper {
4814
4815        private final int mUserId;
4816        private volatile boolean mCeAttached;
4817
4818        private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
4819            super(context, deDatabaseName, null, DE_DATABASE_VERSION);
4820            mUserId = userId;
4821        }
4822
4823        /**
4824         * This call needs to be made while the mCacheLock is held. The way to
4825         * ensure this is to get the lock any time a method is called ont the DatabaseHelper
4826         * @param db The database.
4827         */
4828        @Override
4829        public void onCreate(SQLiteDatabase db) {
4830            Log.i(TAG, "Creating DE database for user " + mUserId);
4831            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4832                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
4833                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4834                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4835                    + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
4836                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
4837                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4838
4839            db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4840                    + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4841                    + META_VALUE + " TEXT)");
4842
4843            createGrantsTable(db);
4844            createSharedAccountsTable(db);
4845            createAccountsDeletionTrigger(db);
4846            DebugDbHelper.createDebugTable(db);
4847        }
4848
4849        private void createSharedAccountsTable(SQLiteDatabase db) {
4850            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4851                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4852                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4853                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4854                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4855        }
4856
4857        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4858            db.execSQL(""
4859                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4860                    + " BEGIN"
4861                    + "   DELETE FROM " + TABLE_GRANTS
4862                    + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4863                    + " END");
4864        }
4865
4866        private void createGrantsTable(SQLiteDatabase db) {
4867            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4868                    + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4869                    + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4870                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4871                    + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4872                    +   "," + GRANTS_GRANTEE_UID + "))");
4873        }
4874
4875        @Override
4876        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4877            Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4878
4879            if (oldVersion != newVersion) {
4880                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4881            }
4882        }
4883
4884        public void attachCeDatabase(File ceDbFile) {
4885            SQLiteDatabase db = getWritableDatabase();
4886            db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
4887            mCeAttached = true;
4888        }
4889
4890        public boolean isCeDatabaseAttached() {
4891            return mCeAttached;
4892        }
4893
4894
4895        public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
4896            if(!mCeAttached) {
4897                Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
4898                        + " is still locked. CE database is not yet available.", new Throwable());
4899            }
4900            return super.getReadableDatabase();
4901        }
4902
4903        public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
4904            if(!mCeAttached) {
4905                Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
4906                        + " is still locked. CE database is not yet available.", new Throwable());
4907            }
4908            return super.getWritableDatabase();
4909        }
4910
4911        @Override
4912        public void onOpen(SQLiteDatabase db) {
4913            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
4914        }
4915
4916        private void migratePreNDbToDe(File preNDbFile) {
4917            Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
4918            SQLiteDatabase db = getWritableDatabase();
4919            db.execSQL("ATTACH DATABASE '" +  preNDbFile.getPath() + "' AS preNDb");
4920            db.beginTransaction();
4921            // Copy accounts fields
4922            db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
4923                    + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
4924                    + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4925                    + ") "
4926                    + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
4927                    + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4928                    + " FROM preNDb." + TABLE_ACCOUNTS);
4929            // Copy SHARED_ACCOUNTS
4930            db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
4931                    + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
4932                    "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
4933                    + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
4934            // Copy DEBUG_TABLE
4935            db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
4936                    + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
4937                    + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
4938                    + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
4939                    "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
4940                    + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
4941                    + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
4942                    + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
4943            // Copy GRANTS
4944            db.execSQL("INSERT INTO " + TABLE_GRANTS
4945                    + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
4946                    + GRANTS_GRANTEE_UID + ") " +
4947                    "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
4948                    + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
4949            // Copy META
4950            db.execSQL("INSERT INTO " + TABLE_META
4951                    + "(" + META_KEY + "," + META_VALUE + ") "
4952                    + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
4953            db.setTransactionSuccessful();
4954            db.endTransaction();
4955
4956            db.execSQL("DETACH DATABASE preNDb");
4957        }
4958
4959        static DeDatabaseHelper create(
4960                Context context,
4961                int userId,
4962                File preNDatabaseFile,
4963                File deDatabaseFile) {
4964            boolean newDbExists = deDatabaseFile.exists();
4965            DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
4966                    deDatabaseFile.getPath());
4967            // If the db just created, and there is a legacy db, migrate it
4968            if (!newDbExists && preNDatabaseFile.exists()) {
4969                // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
4970                PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
4971                        preNDatabaseFile.getPath());
4972                // Open the database to force upgrade if required
4973                preNDatabaseHelper.getWritableDatabase();
4974                preNDatabaseHelper.close();
4975                // Move data without SPII to DE
4976                deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
4977            }
4978            return deDatabaseHelper;
4979        }
4980    }
4981
4982    static class CeDatabaseHelper extends SQLiteOpenHelper {
4983
4984        public CeDatabaseHelper(Context context, String ceDatabaseName) {
4985            super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
4986        }
4987
4988        /**
4989         * This call needs to be made while the mCacheLock is held.
4990         * @param db The database.
4991         */
4992        @Override
4993        public void onCreate(SQLiteDatabase db) {
4994            Log.i(TAG, "Creating CE database " + getDatabaseName());
4995            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4996                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4997                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4998                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4999                    + ACCOUNTS_PASSWORD + " TEXT, "
5000                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5001
5002            db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
5003                    + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
5004                    + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5005                    + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
5006                    + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
5007                    + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
5008
5009            db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
5010                    + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5011                    + EXTRAS_ACCOUNTS_ID + " INTEGER, "
5012                    + EXTRAS_KEY + " TEXT NOT NULL, "
5013                    + EXTRAS_VALUE + " TEXT, "
5014                    + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
5015
5016            createAccountsDeletionTrigger(db);
5017        }
5018
5019        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5020            db.execSQL(""
5021                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5022                    + " BEGIN"
5023                    + "   DELETE FROM " + TABLE_AUTHTOKENS
5024                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5025                    + "   DELETE FROM " + TABLE_EXTRAS
5026                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5027                    + " END");
5028        }
5029
5030        @Override
5031        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5032            Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
5033
5034            if (oldVersion == 9) {
5035                if (Log.isLoggable(TAG, Log.VERBOSE)) {
5036                    Log.v(TAG, "onUpgrade upgrading to v10");
5037                }
5038                db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
5039                db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
5040                // Recreate the trigger, since the old one references the table to be removed
5041                db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
5042                createAccountsDeletionTrigger(db);
5043                db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
5044                db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
5045                oldVersion ++;
5046            }
5047
5048            if (oldVersion != newVersion) {
5049                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5050            }
5051        }
5052
5053        @Override
5054        public void onOpen(SQLiteDatabase db) {
5055            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
5056        }
5057
5058        static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
5059                String type) {
5060            Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
5061                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
5062                    new String[]{name, type}, null, null, null);
5063            try {
5064                if (cursor.moveToNext()) {
5065                    return cursor.getString(0);
5066                }
5067                return null;
5068            } finally {
5069                cursor.close();
5070            }
5071        }
5072
5073        static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
5074            // Select accounts from CE that do not exist in DE
5075            Cursor cursor = db.rawQuery(
5076                    "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
5077                            + " FROM " + CE_TABLE_ACCOUNTS
5078                            + " WHERE NOT EXISTS "
5079                            + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
5080                            + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
5081                            + " )", null);
5082            try {
5083                List<Account> accounts = new ArrayList<>(cursor.getCount());
5084                while (cursor.moveToNext()) {
5085                    String accountName = cursor.getString(0);
5086                    String accountType = cursor.getString(1);
5087                    accounts.add(new Account(accountName, accountType));
5088                }
5089                return accounts;
5090            } finally {
5091                cursor.close();
5092            }
5093        }
5094
5095        /**
5096         * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
5097         * it also performs migration to the new CE database.
5098         * @param context
5099         * @param userId id of the user where the database is located
5100         */
5101        static CeDatabaseHelper create(
5102                Context context,
5103                int userId,
5104                File preNDatabaseFile,
5105                File ceDatabaseFile) {
5106            boolean newDbExists = ceDatabaseFile.exists();
5107            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5108                Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
5109                        + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
5110            }
5111            boolean removeOldDb = false;
5112            if (!newDbExists && preNDatabaseFile.exists()) {
5113                removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
5114            }
5115            // Try to open and upgrade if necessary
5116            CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
5117            ceHelper.getWritableDatabase();
5118            ceHelper.close();
5119            if (removeOldDb) {
5120                Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
5121                if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
5122                    Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
5123                }
5124            }
5125            return ceHelper;
5126        }
5127
5128        private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
5129            Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
5130            try {
5131                FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
5132            } catch (IOException e) {
5133                Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
5134                // Try to remove potentially damaged file if I/O error occurred
5135                deleteDbFileWarnIfFailed(ceDbFile);
5136                return false;
5137            }
5138            return true;
5139        }
5140    }
5141
5142    public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
5143        return asBinder();
5144    }
5145
5146    /**
5147     * Searches array of arguments for the specified string
5148     * @param args array of argument strings
5149     * @param value value to search for
5150     * @return true if the value is contained in the array
5151     */
5152    private static boolean scanArgs(String[] args, String value) {
5153        if (args != null) {
5154            for (String arg : args) {
5155                if (value.equals(arg)) {
5156                    return true;
5157                }
5158            }
5159        }
5160        return false;
5161    }
5162
5163    @Override
5164    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
5165        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
5166                != PackageManager.PERMISSION_GRANTED) {
5167            fout.println("Permission Denial: can't dump AccountsManager from from pid="
5168                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
5169                    + " without permission " + android.Manifest.permission.DUMP);
5170            return;
5171        }
5172        final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
5173        final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
5174
5175        final List<UserInfo> users = getUserManager().getUsers();
5176        for (UserInfo user : users) {
5177            ipw.println("User " + user + ":");
5178            ipw.increaseIndent();
5179            dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5180            ipw.println();
5181            ipw.decreaseIndent();
5182        }
5183    }
5184
5185    private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5186            String[] args, boolean isCheckinRequest) {
5187        synchronized (userAccounts.cacheLock) {
5188            final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
5189
5190            if (isCheckinRequest) {
5191                // This is a checkin request. *Only* upload the account types and the count of each.
5192                Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
5193                        null, null, ACCOUNTS_TYPE, null, null);
5194                try {
5195                    while (cursor.moveToNext()) {
5196                        // print type,count
5197                        fout.println(cursor.getString(0) + "," + cursor.getString(1));
5198                    }
5199                } finally {
5200                    if (cursor != null) {
5201                        cursor.close();
5202                    }
5203                }
5204            } else {
5205                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
5206                        Process.myUid(), null);
5207                fout.println("Accounts: " + accounts.length);
5208                for (Account account : accounts) {
5209                    fout.println("  " + account);
5210                }
5211
5212                // Add debug information.
5213                fout.println();
5214                Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
5215                        null, null, null, null, DebugDbHelper.TIMESTAMP);
5216                fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
5217                fout.println("Accounts History");
5218                try {
5219                    while (cursor.moveToNext()) {
5220                        // print type,count
5221                        fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
5222                                cursor.getString(2) + "," + cursor.getString(3) + ","
5223                                + cursor.getString(4) + "," + cursor.getString(5));
5224                    }
5225                } finally {
5226                    cursor.close();
5227                }
5228
5229                fout.println();
5230                synchronized (mSessions) {
5231                    final long now = SystemClock.elapsedRealtime();
5232                    fout.println("Active Sessions: " + mSessions.size());
5233                    for (Session session : mSessions.values()) {
5234                        fout.println("  " + session.toDebugString(now));
5235                    }
5236                }
5237
5238                fout.println();
5239                mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
5240            }
5241        }
5242    }
5243
5244    private void doNotification(UserAccounts accounts, Account account, CharSequence message,
5245            Intent intent, String packageName, final int userId) {
5246        long identityToken = clearCallingIdentity();
5247        try {
5248            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5249                Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5250            }
5251
5252            if (intent.getComponent() != null &&
5253                    GrantCredentialsPermissionActivity.class.getName().equals(
5254                            intent.getComponent().getClassName())) {
5255                createNoCredentialsPermissionNotification(account, intent, packageName, userId);
5256            } else {
5257                Context contextForUser = getContextForUser(new UserHandle(userId));
5258                final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
5259                intent.addCategory(String.valueOf(notificationId));
5260
5261                final String notificationTitleFormat =
5262                        contextForUser.getText(R.string.notification_title).toString();
5263                Notification n = new Notification.Builder(contextForUser)
5264                        .setWhen(0)
5265                        .setSmallIcon(android.R.drawable.stat_sys_warning)
5266                        .setColor(contextForUser.getColor(
5267                                com.android.internal.R.color.system_notification_accent_color))
5268                        .setContentTitle(String.format(notificationTitleFormat, account.name))
5269                        .setContentText(message)
5270                        .setContentIntent(PendingIntent.getActivityAsUser(
5271                                mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
5272                                null, new UserHandle(userId)))
5273                        .build();
5274                installNotification(notificationId, n, packageName, userId);
5275            }
5276        } finally {
5277            restoreCallingIdentity(identityToken);
5278        }
5279    }
5280
5281    @VisibleForTesting
5282    protected void installNotification(int notificationId, final Notification notification,
5283            UserHandle user) {
5284        installNotification(notificationId, notification, "android", user.getIdentifier());
5285    }
5286
5287    private void installNotification(int notificationId, final Notification notification,
5288            String packageName, int userId) {
5289        final long token = clearCallingIdentity();
5290        try {
5291            INotificationManager notificationManager = NotificationManager.getService();
5292            try {
5293                notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
5294                        notificationId, notification, new int[1], userId);
5295            } catch (RemoteException e) {
5296                /* ignore - local call */
5297            }
5298        } finally {
5299            Binder.restoreCallingIdentity(token);
5300        }
5301    }
5302
5303    @VisibleForTesting
5304    protected void cancelNotification(int id, UserHandle user) {
5305        cancelNotification(id, mContext.getPackageName(), user);
5306    }
5307
5308    protected void cancelNotification(int id, String packageName, UserHandle user) {
5309        long identityToken = clearCallingIdentity();
5310        try {
5311            INotificationManager service = INotificationManager.Stub.asInterface(
5312                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
5313            service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
5314        } catch (RemoteException e) {
5315            /* ignore - local call */
5316        } finally {
5317            restoreCallingIdentity(identityToken);
5318        }
5319    }
5320
5321    private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
5322        for (String perm : permissions) {
5323            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5324                if (Log.isLoggable(TAG, Log.VERBOSE)) {
5325                    Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
5326                }
5327                final int opCode = AppOpsManager.permissionToOpCode(perm);
5328                if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
5329                        opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5330                    return true;
5331                }
5332            }
5333        }
5334        return false;
5335    }
5336
5337    private int handleIncomingUser(int userId) {
5338        try {
5339            return ActivityManagerNative.getDefault().handleIncomingUser(
5340                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5341        } catch (RemoteException re) {
5342            // Shouldn't happen, local.
5343        }
5344        return userId;
5345    }
5346
5347    private boolean isPrivileged(int callingUid) {
5348        final int callingUserId = UserHandle.getUserId(callingUid);
5349
5350        final PackageManager userPackageManager;
5351        try {
5352            userPackageManager = mContext.createPackageContextAsUser(
5353                    "android", 0, new UserHandle(callingUserId)).getPackageManager();
5354        } catch (NameNotFoundException e) {
5355            return false;
5356        }
5357
5358        String[] packages = userPackageManager.getPackagesForUid(callingUid);
5359        for (String name : packages) {
5360            try {
5361                PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
5362                if (packageInfo != null
5363                        && (packageInfo.applicationInfo.privateFlags
5364                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
5365                    return true;
5366                }
5367            } catch (PackageManager.NameNotFoundException e) {
5368                return false;
5369            }
5370        }
5371        return false;
5372    }
5373
5374    private boolean permissionIsGranted(
5375            Account account, String authTokenType, int callerUid, int userId) {
5376        if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5377            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5378                Log.v(TAG, "Access to " + account + " granted calling uid is system");
5379            }
5380            return true;
5381        }
5382
5383        if (isPrivileged(callerUid)) {
5384            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5385                Log.v(TAG, "Access to " + account + " granted calling uid "
5386                        + callerUid + " privileged");
5387            }
5388            return true;
5389        }
5390        if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5391            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5392                Log.v(TAG, "Access to " + account + " granted calling uid "
5393                        + callerUid + " manages the account");
5394            }
5395            return true;
5396        }
5397        if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5398            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5399                Log.v(TAG, "Access to " + account + " granted calling uid "
5400                        + callerUid + " user granted access");
5401            }
5402            return true;
5403        }
5404
5405        if (Log.isLoggable(TAG, Log.VERBOSE)) {
5406            Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5407        }
5408
5409        return false;
5410    }
5411
5412    private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5413            String opPackageName) {
5414        if (accountType == null) {
5415            return false;
5416        } else {
5417            return getTypesVisibleToCaller(callingUid, userId,
5418                    opPackageName).contains(accountType);
5419        }
5420    }
5421
5422    private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5423        if (accountType == null) {
5424            return false;
5425        } else {
5426            return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5427        }
5428    }
5429
5430    private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5431            String opPackageName) {
5432        boolean isPermitted =
5433                isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5434                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
5435        return getTypesForCaller(callingUid, userId, isPermitted);
5436    }
5437
5438    private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5439        return getTypesForCaller(callingUid, userId, false);
5440    }
5441
5442    private List<String> getTypesForCaller(
5443            int callingUid, int userId, boolean isOtherwisePermitted) {
5444        List<String> managedAccountTypes = new ArrayList<>();
5445        long identityToken = Binder.clearCallingIdentity();
5446        Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5447        try {
5448            serviceInfos = mAuthenticatorCache.getAllServices(userId);
5449        } finally {
5450            Binder.restoreCallingIdentity(identityToken);
5451        }
5452        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
5453                serviceInfos) {
5454            final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5455            if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5456                managedAccountTypes.add(serviceInfo.type.type);
5457            }
5458        }
5459        return managedAccountTypes;
5460    }
5461
5462    private boolean isAccountPresentForCaller(String accountName, String accountType) {
5463        if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5464            for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5465                if (account.name.equals(accountName)) {
5466                    return true;
5467                }
5468            }
5469        }
5470        return false;
5471    }
5472
5473    private static void checkManageUsersPermission(String message) {
5474        if (ActivityManager.checkComponentPermission(
5475                android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5476                != PackageManager.PERMISSION_GRANTED) {
5477            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5478        }
5479    }
5480
5481    private static void checkManageOrCreateUsersPermission(String message) {
5482        if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5483                Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5484                ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5485                        Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5486            throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5487                    + message);
5488        }
5489    }
5490
5491    private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5492            int callerUid) {
5493        if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5494            return true;
5495        }
5496        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
5497        synchronized (accounts.cacheLock) {
5498            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
5499
5500            final String query;
5501            final String[] args;
5502
5503            if (authTokenType != null) {
5504                query = COUNT_OF_MATCHING_GRANTS;
5505                args = new String[] {String.valueOf(callerUid), authTokenType,
5506                        account.name, account.type};
5507            } else {
5508                query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
5509                args = new String[] {String.valueOf(callerUid), account.name,
5510                        account.type};
5511            }
5512            final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
5513            if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5514                // TODO: Skip this check when running automated tests. Replace this
5515                // with a more general solution.
5516                Log.d(TAG, "no credentials permission for usage of " + account + ", "
5517                        + authTokenType + " by uid " + callerUid
5518                        + " but ignoring since device is in test harness.");
5519                return true;
5520            }
5521            return permissionGranted;
5522        }
5523    }
5524
5525    private boolean isSystemUid(int callingUid) {
5526        String[] packages = null;
5527        long ident = Binder.clearCallingIdentity();
5528        try {
5529            packages = mPackageManager.getPackagesForUid(callingUid);
5530        } finally {
5531            Binder.restoreCallingIdentity(ident);
5532        }
5533        if (packages != null) {
5534            for (String name : packages) {
5535                try {
5536                    PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5537                    if (packageInfo != null
5538                            && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5539                                    != 0) {
5540                        return true;
5541                    }
5542                } catch (PackageManager.NameNotFoundException e) {
5543                    Log.w(TAG, String.format("Could not find package [%s]", name), e);
5544                }
5545            }
5546        } else {
5547            Log.w(TAG, "No known packages with uid " + callingUid);
5548        }
5549        return false;
5550    }
5551
5552    /** Succeeds if any of the specified permissions are granted. */
5553    private void checkReadAccountsPermitted(
5554            int callingUid,
5555            String accountType,
5556            int userId,
5557            String opPackageName) {
5558        if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
5559            String msg = String.format(
5560                    "caller uid %s cannot access %s accounts",
5561                    callingUid,
5562                    accountType);
5563            Log.w(TAG, "  " + msg);
5564            throw new SecurityException(msg);
5565        }
5566    }
5567
5568    private boolean canUserModifyAccounts(int userId, int callingUid) {
5569        // the managing app can always modify accounts
5570        if (isProfileOwner(callingUid)) {
5571            return true;
5572        }
5573        if (getUserManager().getUserRestrictions(new UserHandle(userId))
5574                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5575            return false;
5576        }
5577        return true;
5578    }
5579
5580    private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5581        // the managing app can always modify accounts
5582        if (isProfileOwner(callingUid)) {
5583            return true;
5584        }
5585        DevicePolicyManager dpm = (DevicePolicyManager) mContext
5586                .getSystemService(Context.DEVICE_POLICY_SERVICE);
5587        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
5588        if (typesArray == null) {
5589            return true;
5590        }
5591        for (String forbiddenType : typesArray) {
5592            if (forbiddenType.equals(accountType)) {
5593                return false;
5594            }
5595        }
5596        return true;
5597    }
5598
5599    private boolean isProfileOwner(int uid) {
5600        final DevicePolicyManagerInternal dpmi =
5601                LocalServices.getService(DevicePolicyManagerInternal.class);
5602        return (dpmi != null)
5603                && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5604    }
5605
5606    @Override
5607    public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5608            throws RemoteException {
5609        final int callingUid = getCallingUid();
5610
5611        if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
5612            throw new SecurityException();
5613        }
5614
5615        if (value) {
5616            grantAppPermission(account, authTokenType, uid);
5617        } else {
5618            revokeAppPermission(account, authTokenType, uid);
5619        }
5620    }
5621
5622    /**
5623     * Allow callers with the given uid permission to get credentials for account/authTokenType.
5624     * <p>
5625     * Although this is public it can only be accessed via the AccountManagerService object
5626     * which is in the system. This means we don't need to protect it with permissions.
5627     * @hide
5628     */
5629    void grantAppPermission(Account account, String authTokenType, int uid) {
5630        if (account == null || authTokenType == null) {
5631            Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
5632            return;
5633        }
5634        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5635        synchronized (accounts.cacheLock) {
5636            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
5637            db.beginTransaction();
5638            try {
5639                long accountId = getAccountIdLocked(db, account);
5640                if (accountId >= 0) {
5641                    ContentValues values = new ContentValues();
5642                    values.put(GRANTS_ACCOUNTS_ID, accountId);
5643                    values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
5644                    values.put(GRANTS_GRANTEE_UID, uid);
5645                    db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
5646                    db.setTransactionSuccessful();
5647                }
5648            } finally {
5649                db.endTransaction();
5650            }
5651            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5652                    UserHandle.of(accounts.userId));
5653
5654            cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
5655        }
5656
5657        // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5658        for (AccountManagerInternal.OnAppPermissionChangeListener listener
5659                : mAppPermissionChangeListeners) {
5660            mMessageHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5661        }
5662    }
5663
5664    /**
5665     * Don't allow callers with the given uid permission to get credentials for
5666     * account/authTokenType.
5667     * <p>
5668     * Although this is public it can only be accessed via the AccountManagerService object
5669     * which is in the system. This means we don't need to protect it with permissions.
5670     * @hide
5671     */
5672    private void revokeAppPermission(Account account, String authTokenType, int uid) {
5673        if (account == null || authTokenType == null) {
5674            Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
5675            return;
5676        }
5677        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5678        synchronized (accounts.cacheLock) {
5679            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
5680            db.beginTransaction();
5681            try {
5682                long accountId = getAccountIdLocked(db, account);
5683                if (accountId >= 0) {
5684                    db.delete(TABLE_GRANTS,
5685                            GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
5686                                    + GRANTS_GRANTEE_UID + "=?",
5687                            new String[]{String.valueOf(accountId), authTokenType,
5688                                    String.valueOf(uid)});
5689                    db.setTransactionSuccessful();
5690                }
5691            } finally {
5692                db.endTransaction();
5693            }
5694
5695            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5696                    new UserHandle(accounts.userId));
5697        }
5698
5699        // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5700        for (AccountManagerInternal.OnAppPermissionChangeListener listener
5701                : mAppPermissionChangeListeners) {
5702            mMessageHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5703        }
5704    }
5705
5706    static final private String stringArrayToString(String[] value) {
5707        return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
5708    }
5709
5710    private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5711        final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
5712        if (oldAccountsForType != null) {
5713            ArrayList<Account> newAccountsList = new ArrayList<Account>();
5714            for (Account curAccount : oldAccountsForType) {
5715                if (!curAccount.equals(account)) {
5716                    newAccountsList.add(curAccount);
5717                }
5718            }
5719            if (newAccountsList.isEmpty()) {
5720                accounts.accountCache.remove(account.type);
5721            } else {
5722                Account[] newAccountsForType = new Account[newAccountsList.size()];
5723                newAccountsForType = newAccountsList.toArray(newAccountsForType);
5724                accounts.accountCache.put(account.type, newAccountsForType);
5725            }
5726        }
5727        accounts.userDataCache.remove(account);
5728        accounts.authTokenCache.remove(account);
5729        accounts.previousNameCache.remove(account);
5730    }
5731
5732    /**
5733     * This assumes that the caller has already checked that the account is not already present.
5734     * IMPORTANT: The account being inserted will begin to be tracked for access in remote
5735     * processes and if you will return this account to apps you should return the result.
5736     * @return The inserted account which is a new instance that is being tracked.
5737     */
5738    private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
5739        Account[] accountsForType = accounts.accountCache.get(account.type);
5740        int oldLength = (accountsForType != null) ? accountsForType.length : 0;
5741        Account[] newAccountsForType = new Account[oldLength + 1];
5742        if (accountsForType != null) {
5743            System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
5744        }
5745        String token = account.getAccessId() != null ? account.getAccessId()
5746                : UUID.randomUUID().toString();
5747        newAccountsForType[oldLength] = new Account(account, token);
5748        accounts.accountCache.put(account.type, newAccountsForType);
5749        return newAccountsForType[oldLength];
5750    }
5751
5752    private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
5753            int callingUid, String callingPackage) {
5754        if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
5755                || callingUid == Process.myUid()) {
5756            return unfiltered;
5757        }
5758        UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
5759        if (user != null && user.isRestricted()) {
5760            String[] packages = mPackageManager.getPackagesForUid(callingUid);
5761            // If any of the packages is a white listed package, return the full set,
5762            // otherwise return non-shared accounts only.
5763            // This might be a temporary way to specify a whitelist
5764            String whiteList = mContext.getResources().getString(
5765                    com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
5766            for (String packageName : packages) {
5767                if (whiteList.contains(";" + packageName + ";")) {
5768                    return unfiltered;
5769                }
5770            }
5771            ArrayList<Account> allowed = new ArrayList<Account>();
5772            Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
5773            if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
5774            String requiredAccountType = "";
5775            try {
5776                // If there's an explicit callingPackage specified, check if that package
5777                // opted in to see restricted accounts.
5778                if (callingPackage != null) {
5779                    PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
5780                    if (pi != null && pi.restrictedAccountType != null) {
5781                        requiredAccountType = pi.restrictedAccountType;
5782                    }
5783                } else {
5784                    // Otherwise check if the callingUid has a package that has opted in
5785                    for (String packageName : packages) {
5786                        PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
5787                        if (pi != null && pi.restrictedAccountType != null) {
5788                            requiredAccountType = pi.restrictedAccountType;
5789                            break;
5790                        }
5791                    }
5792                }
5793            } catch (NameNotFoundException nnfe) {
5794            }
5795            for (Account account : unfiltered) {
5796                if (account.type.equals(requiredAccountType)) {
5797                    allowed.add(account);
5798                } else {
5799                    boolean found = false;
5800                    for (Account shared : sharedAccounts) {
5801                        if (shared.equals(account)) {
5802                            found = true;
5803                            break;
5804                        }
5805                    }
5806                    if (!found) {
5807                        allowed.add(account);
5808                    }
5809                }
5810            }
5811            Account[] filtered = new Account[allowed.size()];
5812            allowed.toArray(filtered);
5813            return filtered;
5814        } else {
5815            return unfiltered;
5816        }
5817    }
5818
5819    /*
5820     * packageName can be null. If not null, it should be used to filter out restricted accounts
5821     * that the package is not allowed to access.
5822     */
5823    protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
5824            int callingUid, String callingPackage) {
5825        if (accountType != null) {
5826            final Account[] accounts = userAccounts.accountCache.get(accountType);
5827            if (accounts == null) {
5828                return EMPTY_ACCOUNT_ARRAY;
5829            } else {
5830                return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
5831                        callingUid, callingPackage);
5832            }
5833        } else {
5834            int totalLength = 0;
5835            for (Account[] accounts : userAccounts.accountCache.values()) {
5836                totalLength += accounts.length;
5837            }
5838            if (totalLength == 0) {
5839                return EMPTY_ACCOUNT_ARRAY;
5840            }
5841            Account[] accounts = new Account[totalLength];
5842            totalLength = 0;
5843            for (Account[] accountsOfType : userAccounts.accountCache.values()) {
5844                System.arraycopy(accountsOfType, 0, accounts, totalLength,
5845                        accountsOfType.length);
5846                totalLength += accountsOfType.length;
5847            }
5848            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
5849        }
5850    }
5851
5852    protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5853            Account account, String key, String value) {
5854        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
5855        if (userDataForAccount == null) {
5856            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
5857            accounts.userDataCache.put(account, userDataForAccount);
5858        }
5859        if (value == null) {
5860            userDataForAccount.remove(key);
5861        } else {
5862            userDataForAccount.put(key, value);
5863        }
5864    }
5865
5866    protected String readCachedTokenInternal(
5867            UserAccounts accounts,
5868            Account account,
5869            String tokenType,
5870            String callingPackage,
5871            byte[] pkgSigDigest) {
5872        synchronized (accounts.cacheLock) {
5873            return accounts.accountTokenCaches.get(
5874                    account, tokenType, callingPackage, pkgSigDigest);
5875        }
5876    }
5877
5878    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5879            Account account, String key, String value) {
5880        HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
5881        if (authTokensForAccount == null) {
5882            authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
5883            accounts.authTokenCache.put(account, authTokensForAccount);
5884        }
5885        if (value == null) {
5886            authTokensForAccount.remove(key);
5887        } else {
5888            authTokensForAccount.put(key, value);
5889        }
5890    }
5891
5892    protected String readAuthTokenInternal(UserAccounts accounts, Account account,
5893            String authTokenType) {
5894        synchronized (accounts.cacheLock) {
5895            HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
5896            if (authTokensForAccount == null) {
5897                // need to populate the cache for this account
5898                final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
5899                authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
5900                accounts.authTokenCache.put(account, authTokensForAccount);
5901            }
5902            return authTokensForAccount.get(authTokenType);
5903        }
5904    }
5905
5906    protected String readUserDataInternalLocked(
5907            UserAccounts accounts, Account account, String key) {
5908        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
5909        if (userDataForAccount == null) {
5910            // need to populate the cache for this account
5911            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
5912            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
5913            accounts.userDataCache.put(account, userDataForAccount);
5914        }
5915        return userDataForAccount.get(key);
5916    }
5917
5918    protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
5919            final SQLiteDatabase db, Account account) {
5920        HashMap<String, String> userDataForAccount = new HashMap<>();
5921        Cursor cursor = db.query(CE_TABLE_EXTRAS,
5922                COLUMNS_EXTRAS_KEY_AND_VALUE,
5923                SELECTION_USERDATA_BY_ACCOUNT,
5924                new String[]{account.name, account.type},
5925                null, null, null);
5926        try {
5927            while (cursor.moveToNext()) {
5928                final String tmpkey = cursor.getString(0);
5929                final String value = cursor.getString(1);
5930                userDataForAccount.put(tmpkey, value);
5931            }
5932        } finally {
5933            cursor.close();
5934        }
5935        return userDataForAccount;
5936    }
5937
5938    protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
5939            final SQLiteDatabase db, Account account) {
5940        HashMap<String, String> authTokensForAccount = new HashMap<>();
5941        Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
5942                COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
5943                SELECTION_AUTHTOKENS_BY_ACCOUNT,
5944                new String[]{account.name, account.type},
5945                null, null, null);
5946        try {
5947            while (cursor.moveToNext()) {
5948                final String type = cursor.getString(0);
5949                final String authToken = cursor.getString(1);
5950                authTokensForAccount.put(type, authToken);
5951            }
5952        } finally {
5953            cursor.close();
5954        }
5955        return authTokensForAccount;
5956    }
5957
5958    private Context getContextForUser(UserHandle user) {
5959        try {
5960            return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
5961        } catch (NameNotFoundException e) {
5962            // Default to mContext, not finding the package system is running as is unlikely.
5963            return mContext;
5964        }
5965    }
5966
5967    private void sendResponse(IAccountManagerResponse response, Bundle result) {
5968        try {
5969            response.onResult(result);
5970        } catch (RemoteException e) {
5971            // if the caller is dead then there is no one to care about remote
5972            // exceptions
5973            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5974                Log.v(TAG, "failure while notifying response", e);
5975            }
5976        }
5977    }
5978
5979    private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
5980            String errorMessage) {
5981        try {
5982            response.onError(errorCode, errorMessage);
5983        } catch (RemoteException e) {
5984            // if the caller is dead then there is no one to care about remote
5985            // exceptions
5986            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5987                Log.v(TAG, "failure while notifying response", e);
5988            }
5989        }
5990    }
5991
5992    private final class AccountManagerInternalImpl extends AccountManagerInternal {
5993        private final Object mLock = new Object();
5994
5995        @GuardedBy("mLock")
5996        private AccountManagerBackupHelper mBackupHelper;
5997
5998        @Override
5999        public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
6000                @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
6001            if (account == null) {
6002                Slog.w(TAG, "account cannot be null");
6003                return;
6004            }
6005            if (packageName == null) {
6006                Slog.w(TAG, "packageName cannot be null");
6007                return;
6008            }
6009            if (userId < UserHandle.USER_SYSTEM) {
6010                Slog.w(TAG, "user id must be concrete");
6011                return;
6012            }
6013            if (callback == null) {
6014                Slog.w(TAG, "callback cannot be null");
6015                return;
6016            }
6017
6018            if (AccountManagerService.this.hasAccountAccess(account, packageName,
6019                    new UserHandle(userId))) {
6020                Bundle result = new Bundle();
6021                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
6022                callback.sendResult(result);
6023                return;
6024            }
6025
6026            final int uid;
6027            try {
6028                uid = mPackageManager.getPackageUidAsUser(packageName, userId);
6029            } catch (NameNotFoundException e) {
6030                Slog.e(TAG, "Unknown package " + packageName);
6031                return;
6032            }
6033
6034            Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
6035            final UserAccounts userAccounts;
6036            synchronized (mUsers) {
6037                userAccounts = mUsers.get(userId);
6038            }
6039            doNotification(userAccounts, account, null, intent, packageName, userId);
6040        }
6041
6042        @Override
6043        public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
6044            // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
6045            mAppPermissionChangeListeners.add(listener);
6046        }
6047
6048        @Override
6049        public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
6050            return AccountManagerService.this.hasAccountAccess(account, null, uid);
6051        }
6052
6053        @Override
6054        public byte[] backupAccountAccessPermissions(int userId) {
6055            synchronized (mLock) {
6056                if (mBackupHelper == null) {
6057                    mBackupHelper = new AccountManagerBackupHelper(
6058                            AccountManagerService.this, this);
6059                }
6060                return mBackupHelper.backupAccountAccessPermissions(userId);
6061            }
6062        }
6063
6064        @Override
6065        public void restoreAccountAccessPermissions(byte[] data, int userId) {
6066            synchronized (mLock) {
6067                if (mBackupHelper == null) {
6068                    mBackupHelper = new AccountManagerBackupHelper(
6069                            AccountManagerService.this, this);
6070                }
6071                mBackupHelper.restoreAccountAccessPermissions(data, userId);
6072            }
6073        }
6074    }
6075}
6076