AccountManagerService.java revision 5cb2973495084f8ce3433b579e4b4962ed9d7efc
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.app.ActivityManager;
36import android.app.ActivityManagerNative;
37import android.app.ActivityThread;
38import android.app.AppGlobals;
39import android.app.AppOpsManager;
40import android.app.INotificationManager;
41import android.app.Notification;
42import android.app.NotificationManager;
43import android.app.PendingIntent;
44import android.app.admin.DeviceAdminInfo;
45import android.app.admin.DevicePolicyManager;
46import android.app.admin.DevicePolicyManagerInternal;
47import android.content.BroadcastReceiver;
48import android.content.ComponentName;
49import android.content.ContentValues;
50import android.content.Context;
51import android.content.Intent;
52import android.content.IntentFilter;
53import android.content.IntentSender;
54import android.content.ServiceConnection;
55import android.content.pm.ActivityInfo;
56import android.content.pm.ApplicationInfo;
57import android.content.pm.IPackageManager;
58import android.content.pm.PackageInfo;
59import android.content.pm.PackageManager;
60import android.content.pm.PackageManager.NameNotFoundException;
61import android.content.pm.RegisteredServicesCache;
62import android.content.pm.RegisteredServicesCacheListener;
63import android.content.pm.ResolveInfo;
64import android.content.pm.Signature;
65import android.content.pm.UserInfo;
66import android.database.Cursor;
67import android.database.DatabaseUtils;
68import android.database.sqlite.SQLiteDatabase;
69import android.database.sqlite.SQLiteOpenHelper;
70import android.database.sqlite.SQLiteStatement;
71import android.os.Binder;
72import android.os.Bundle;
73import android.os.Environment;
74import android.os.FileUtils;
75import android.os.Handler;
76import android.os.IBinder;
77import android.os.Looper;
78import android.os.Message;
79import android.os.Parcel;
80import android.os.Process;
81import android.os.RemoteCallback;
82import android.os.RemoteException;
83import android.os.ServiceManager;
84import android.os.SystemClock;
85import android.os.UserHandle;
86import android.os.UserManager;
87import android.os.storage.StorageManager;
88import android.service.notification.StatusBarNotification;
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.VisibleForTesting;
98import com.android.internal.content.PackageMonitor;
99import com.android.internal.util.ArrayUtils;
100import com.android.internal.util.IndentingPrintWriter;
101import com.android.internal.util.Preconditions;
102import com.android.server.FgThread;
103import com.android.server.LocalServices;
104import com.android.server.SystemService;
105
106import com.google.android.collect.Lists;
107import com.google.android.collect.Sets;
108
109import java.io.File;
110import java.io.FileDescriptor;
111import java.io.IOException;
112import java.io.PrintWriter;
113import java.security.GeneralSecurityException;
114import java.security.MessageDigest;
115import java.security.NoSuchAlgorithmException;
116import java.text.SimpleDateFormat;
117import java.util.ArrayList;
118import java.util.Arrays;
119import java.util.Collection;
120import java.util.Date;
121import java.util.HashMap;
122import java.util.HashSet;
123import java.util.Iterator;
124import java.util.LinkedHashMap;
125import java.util.List;
126import java.util.Map;
127import java.util.Map.Entry;
128import java.util.concurrent.atomic.AtomicInteger;
129import java.util.concurrent.atomic.AtomicReference;
130
131/**
132 * A system service that provides  account, password, and authtoken management for all
133 * accounts on the device. Some of these calls are implemented with the help of the corresponding
134 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
135 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
136 *    AccountManager accountManager = AccountManager.get(context);
137 * @hide
138 */
139public class AccountManagerService
140        extends IAccountManager.Stub
141        implements RegisteredServicesCacheListener<AuthenticatorDescription> {
142    private static final String TAG = "AccountManagerService";
143
144    public static class Lifecycle extends SystemService {
145        private AccountManagerService mService;
146
147        public Lifecycle(Context context) {
148            super(context);
149        }
150
151        @Override
152        public void onStart() {
153            mService = new AccountManagerService(getContext());
154            publishBinderService(Context.ACCOUNT_SERVICE, mService);
155        }
156
157        @Override
158        public void onBootPhase(int phase) {
159            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
160                mService.systemReady();
161            }
162        }
163
164        @Override
165        public void onUnlockUser(int userHandle) {
166            mService.onUnlockUser(userHandle);
167        }
168    }
169
170    private static final String DATABASE_NAME = "accounts.db";
171    private static final int PRE_N_DATABASE_VERSION = 9;
172    private static final int CE_DATABASE_VERSION = 10;
173    private static final int DE_DATABASE_VERSION = 1;
174
175    private static final int MAX_DEBUG_DB_SIZE = 64;
176
177    private final Context mContext;
178
179    private final PackageManager mPackageManager;
180    private final AppOpsManager mAppOpsManager;
181    private UserManager mUserManager;
182
183    private final MessageHandler mMessageHandler;
184
185    // Messages that can be sent on mHandler
186    private static final int MESSAGE_TIMED_OUT = 3;
187    private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
188
189    private final IAccountAuthenticatorCache mAuthenticatorCache;
190
191    private static final String TABLE_ACCOUNTS = "accounts";
192    private static final String ACCOUNTS_ID = "_id";
193    private static final String ACCOUNTS_NAME = "name";
194    private static final String ACCOUNTS_TYPE = "type";
195    private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
196    private static final String ACCOUNTS_PASSWORD = "password";
197    private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
198    private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
199            "last_password_entry_time_millis_epoch";
200
201    private static final String TABLE_AUTHTOKENS = "authtokens";
202    private static final String AUTHTOKENS_ID = "_id";
203    private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
204    private static final String AUTHTOKENS_TYPE = "type";
205    private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
206
207    private static final String TABLE_GRANTS = "grants";
208    private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
209    private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
210    private static final String GRANTS_GRANTEE_UID = "uid";
211
212    private static final String TABLE_EXTRAS = "extras";
213    private static final String EXTRAS_ID = "_id";
214    private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
215    private static final String EXTRAS_KEY = "key";
216    private static final String EXTRAS_VALUE = "value";
217
218    private static final String TABLE_META = "meta";
219    private static final String META_KEY = "key";
220    private static final String META_VALUE = "value";
221
222    private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
223    private static final String SHARED_ACCOUNTS_ID = "_id";
224
225    private static final String PRE_N_DATABASE_NAME = "accounts.db";
226    private static final String CE_DATABASE_NAME = "accounts_ce.db";
227    private static final String DE_DATABASE_NAME = "accounts_de.db";
228    private static final String CE_DB_PREFIX = "ceDb.";
229    private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
230    private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
231    private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
232
233    private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
234            new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
235    private static final Intent ACCOUNTS_CHANGED_INTENT;
236
237    static {
238        ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
239        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
240    }
241
242    private static final String COUNT_OF_MATCHING_GRANTS = ""
243            + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
244            + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
245            + " AND " + GRANTS_GRANTEE_UID + "=?"
246            + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
247            + " AND " + ACCOUNTS_NAME + "=?"
248            + " AND " + ACCOUNTS_TYPE + "=?";
249
250    private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = ""
251            + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
252            + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
253            + " AND " + GRANTS_GRANTEE_UID + "=?"
254            + " AND " + ACCOUNTS_NAME + "=?"
255            + " AND " + ACCOUNTS_TYPE + "=?";
256
257    private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
258            AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
259
260    private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
261            AUTHTOKENS_AUTHTOKEN};
262
263    private static final String SELECTION_USERDATA_BY_ACCOUNT =
264            EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
265    private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
266
267    private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
268            "auth_uid_for_type:";
269    private static final String META_KEY_DELIMITER = ":";
270    private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
271
272    private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
273    private final AtomicInteger mNotificationIds = new AtomicInteger(1);
274
275    static class UserAccounts {
276        private final int userId;
277        private final DeDatabaseHelper openHelper;
278        private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
279                credentialsPermissionNotificationIds =
280                new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
281        private final HashMap<Account, Integer> signinRequiredNotificationIds =
282                new HashMap<Account, Integer>();
283        private final Object cacheLock = new Object();
284        /** protected by the {@link #cacheLock} */
285        private final HashMap<String, Account[]> accountCache =
286                new LinkedHashMap<String, Account[]>();
287        /** protected by the {@link #cacheLock} */
288        private final HashMap<Account, HashMap<String, String>> userDataCache =
289                new HashMap<Account, HashMap<String, String>>();
290        /** protected by the {@link #cacheLock} */
291        private final HashMap<Account, HashMap<String, String>> authTokenCache =
292                new HashMap<Account, HashMap<String, String>>();
293
294        /** protected by the {@link #cacheLock} */
295        private final TokenCache accountTokenCaches = new TokenCache();
296
297        /**
298         * protected by the {@link #cacheLock}
299         *
300         * Caches the previous names associated with an account. Previous names
301         * should be cached because we expect that when an Account is renamed,
302         * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
303         * want to know if the accounts they care about have been renamed.
304         *
305         * The previous names are wrapped in an {@link AtomicReference} so that
306         * we can distinguish between those accounts with no previous names and
307         * those whose previous names haven't been cached (yet).
308         */
309        private final HashMap<Account, AtomicReference<String>> previousNameCache =
310                new HashMap<Account, AtomicReference<String>>();
311
312        private int debugDbInsertionPoint = -1;
313        private SQLiteStatement statementForLogging;
314
315        UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
316            this.userId = userId;
317            synchronized (cacheLock) {
318                openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
319            }
320        }
321    }
322
323    private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
324    private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
325
326    private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
327    private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
328
329    /**
330     * This should only be called by system code. One should only call this after the service
331     * has started.
332     * @return a reference to the AccountManagerService instance
333     * @hide
334     */
335    public static AccountManagerService getSingleton() {
336        return sThis.get();
337    }
338
339    public AccountManagerService(Context context) {
340        this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
341    }
342
343    public AccountManagerService(Context context, PackageManager packageManager,
344            IAccountAuthenticatorCache authenticatorCache) {
345        mContext = context;
346        mPackageManager = packageManager;
347        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
348
349        mMessageHandler = new MessageHandler(FgThread.get().getLooper());
350
351        mAuthenticatorCache = authenticatorCache;
352        mAuthenticatorCache.setListener(this, null /* Handler */);
353
354        sThis.set(this);
355
356        IntentFilter intentFilter = new IntentFilter();
357        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
358        intentFilter.addDataScheme("package");
359        mContext.registerReceiver(new BroadcastReceiver() {
360            @Override
361            public void onReceive(Context context1, Intent intent) {
362                // Don't delete accounts when updating a authenticator's
363                // package.
364                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
365                    /* Purging data requires file io, don't block the main thread. This is probably
366                     * less than ideal because we are introducing a race condition where old grants
367                     * could be exercised until they are purged. But that race condition existed
368                     * anyway with the broadcast receiver.
369                     *
370                     * Ideally, we would completely clear the cache, purge data from the database,
371                     * and then rebuild the cache. All under the cache lock. But that change is too
372                     * large at this point.
373                     */
374                    Runnable r = new Runnable() {
375                        @Override
376                        public void run() {
377                            purgeOldGrantsAll();
378                        }
379                    };
380                    new Thread(r).start();
381                }
382            }
383        }, intentFilter);
384
385        IntentFilter userFilter = new IntentFilter();
386        userFilter.addAction(Intent.ACTION_USER_REMOVED);
387        mContext.registerReceiverAsUser(new BroadcastReceiver() {
388            @Override
389            public void onReceive(Context context, Intent intent) {
390                String action = intent.getAction();
391                if (Intent.ACTION_USER_REMOVED.equals(action)) {
392                    onUserRemoved(intent);
393                }
394            }
395        }, UserHandle.ALL, userFilter, null, null);
396
397        LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl());
398
399        // Need to cancel account request notifications if the update/install can access the account
400        new PackageMonitor() {
401            @Override
402            public void onPackageAdded(String packageName, int uid) {
403                // Called on a handler, and running as the system
404                cancelAccountAccessRequestNotificationIfNeeded(uid, true);
405            }
406
407            @Override
408            public void onPackageUpdateFinished(String packageName, int uid) {
409                // Called on a handler, and running as the system
410                cancelAccountAccessRequestNotificationIfNeeded(uid, true);
411            }
412        }.register(mContext, mMessageHandler.getLooper(), UserHandle.ALL, true);
413
414        // Cancel account request notification if an app op was preventing the account access
415        mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
416                new AppOpsManager.OnOpChangedInternalListener() {
417            @Override
418            public void onOpChanged(int op, String packageName) {
419                try {
420                    final int userId = ActivityManager.getCurrentUser();
421                    final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
422                    final int mode = mAppOpsManager.checkOpNoThrow(
423                            AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
424                    if (mode == AppOpsManager.MODE_ALLOWED) {
425                        final long identity = Binder.clearCallingIdentity();
426                        try {
427                            cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
428                        } finally {
429                            Binder.restoreCallingIdentity(identity);
430                        }
431                    }
432                } catch (NameNotFoundException e) {
433                    /* ignore */
434                }
435            }
436        });
437
438        // Cancel account request notification if a permission was preventing the account access
439        mPackageManager.addOnPermissionsChangeListener(
440                (int uid) -> {
441            Account[] accounts = null;
442            String[] packageNames = mPackageManager.getPackagesForUid(uid);
443            if (packageNames != null) {
444                final int userId = UserHandle.getUserId(uid);
445                final long identity = Binder.clearCallingIdentity();
446                try {
447                    for (String packageName : packageNames) {
448                        if (mContext.getPackageManager().checkPermission(
449                                Manifest.permission.GET_ACCOUNTS, packageName)
450                                        != PackageManager.PERMISSION_GRANTED) {
451                            continue;
452                        }
453
454                        if (accounts == null) {
455                            accounts = getAccountsAsUser(null, userId, "android");
456                            if (ArrayUtils.isEmpty(accounts)) {
457                                return;
458                            }
459                        }
460
461                        for (Account account : accounts) {
462                            cancelAccountAccessRequestNotificationIfNeeded(
463                                    account, uid, packageName, true);
464                        }
465                    }
466                } finally {
467                    Binder.restoreCallingIdentity(identity);
468                }
469            }
470        });
471    }
472
473    private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
474            boolean checkAccess) {
475        Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
476        for (Account account : accounts) {
477            cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
478        }
479    }
480
481    private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
482            boolean checkAccess) {
483        Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
484        for (Account account : accounts) {
485            cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
486        }
487    }
488
489    private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
490            boolean checkAccess) {
491        String[] packageNames = mPackageManager.getPackagesForUid(uid);
492        if (packageNames != null) {
493            for (String packageName : packageNames) {
494                cancelAccountAccessRequestNotificationIfNeeded(account, uid,
495                        packageName, checkAccess);
496            }
497        }
498    }
499
500    private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
501            int uid, String packageName, boolean checkAccess) {
502        if (!checkAccess || hasAccountAccess(account, packageName,
503                UserHandle.getUserHandleForUid(uid))) {
504            cancelNotification(getCredentialPermissionNotificationId(account,
505                    AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
506                    UserHandle.getUserHandleForUid(uid));
507        }
508    }
509
510    @Override
511    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
512            throws RemoteException {
513        try {
514            return super.onTransact(code, data, reply, flags);
515        } catch (RuntimeException e) {
516            // The account manager only throws security exceptions, so let's
517            // log all others.
518            if (!(e instanceof SecurityException)) {
519                Slog.wtf(TAG, "Account Manager Crash", e);
520            }
521            throw e;
522        }
523    }
524
525    public void systemReady() {
526    }
527
528    private UserManager getUserManager() {
529        if (mUserManager == null) {
530            mUserManager = UserManager.get(mContext);
531        }
532        return mUserManager;
533    }
534
535    /**
536     * Validate internal set of accounts against installed authenticators for
537     * given user. Clears cached authenticators before validating.
538     */
539    public void validateAccounts(int userId) {
540        final UserAccounts accounts = getUserAccounts(userId);
541        // Invalidate user-specific cache to make sure we catch any
542        // removed authenticators.
543        validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
544    }
545
546    /**
547     * Validate internal set of accounts against installed authenticators for
548     * given user. Clear cached authenticators before validating when requested.
549     */
550    private void validateAccountsInternal(
551            UserAccounts accounts, boolean invalidateAuthenticatorCache) {
552        if (Log.isLoggable(TAG, Log.DEBUG)) {
553            Log.d(TAG, "validateAccountsInternal " + accounts.userId
554                    + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
555                    + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
556        }
557
558        if (invalidateAuthenticatorCache) {
559            mAuthenticatorCache.invalidateCache(accounts.userId);
560        }
561
562        final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
563                mAuthenticatorCache, accounts.userId);
564        boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
565
566        synchronized (accounts.cacheLock) {
567            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
568            boolean accountDeleted = false;
569
570            // Get a list of stored authenticator type and UID
571            Cursor metaCursor = db.query(
572                    TABLE_META,
573                    new String[] {META_KEY, META_VALUE},
574                    SELECTION_META_BY_AUTHENTICATOR_TYPE,
575                    new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
576                    null /* groupBy */,
577                    null /* having */,
578                    META_KEY);
579            // Create a list of authenticator type whose previous uid no longer exists
580            HashSet<String> obsoleteAuthType = Sets.newHashSet();
581            try {
582                SparseBooleanArray knownUids = null;
583                while (metaCursor.moveToNext()) {
584                    String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
585                    String uid = metaCursor.getString(1);
586                    if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uid)) {
587                        // Should never happen.
588                        Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
589                                + ", uid empty: " + TextUtils.isEmpty(uid));
590                        continue;
591                    }
592                    Integer knownUid = knownAuth.get(type);
593                    if (knownUid != null && uid.equals(knownUid.toString())) {
594                        // Remove it from the knownAuth list if it's unchanged.
595                        knownAuth.remove(type);
596                    } else {
597                        /*
598                         * The authenticator is presently not cached and should only be triggered
599                         * when we think an authenticator has been removed (or is being updated).
600                         * But we still want to check if any data with the associated uid is
601                         * around. This is an (imperfect) signal that the package may be updating.
602                         *
603                         * A side effect of this is that an authenticator sharing a uid with
604                         * multiple apps won't get its credentials wiped as long as some app with
605                         * that uid is still on the device. But I suspect that this is a rare case.
606                         * And it isn't clear to me how an attacker could really exploit that
607                         * feature.
608                         *
609                         * The upshot is that we don't have to worry about accounts getting
610                         * uninstalled while the authenticator's package is being updated.
611                         *
612                         */
613                        if (knownUids == null) {
614                            knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
615                        }
616                        if (!knownUids.get(Integer.parseInt(uid))) {
617                            // The authenticator is not presently available to the cache. And the
618                            // package no longer has a data directory (so we surmise it isn't updating).
619                            // So purge its data from the account databases.
620                            obsoleteAuthType.add(type);
621                            // And delete it from the TABLE_META
622                            db.delete(
623                                    TABLE_META,
624                                    META_KEY + "=? AND " + META_VALUE + "=?",
625                                    new String[] {
626                                            META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
627                                            uid}
628                                    );
629                        }
630                    }
631                }
632            } finally {
633                metaCursor.close();
634            }
635
636            // Add the newly registered authenticator to TABLE_META. If old authenticators have
637            // been renabled (after being updated for example), then we just overwrite the old
638            // values.
639            Iterator<Entry<String, Integer>> iterator = knownAuth.entrySet().iterator();
640            while (iterator.hasNext()) {
641                Entry<String, Integer> entry = iterator.next();
642                ContentValues values = new ContentValues();
643                values.put(META_KEY,
644                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
645                values.put(META_VALUE, entry.getValue());
646                db.insertWithOnConflict(TABLE_META, null, values, SQLiteDatabase.CONFLICT_REPLACE);
647            }
648
649            Cursor cursor = db.query(TABLE_ACCOUNTS,
650                    new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
651                    null, null, null, null, ACCOUNTS_ID);
652            try {
653                accounts.accountCache.clear();
654                final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
655                while (cursor.moveToNext()) {
656                    final long accountId = cursor.getLong(0);
657                    final String accountType = cursor.getString(1);
658                    final String accountName = cursor.getString(2);
659
660                    if (obsoleteAuthType.contains(accountType)) {
661                        Slog.w(TAG, "deleting account " + accountName + " because type "
662                                + accountType + "'s registered authenticator no longer exist.");
663                        db.beginTransaction();
664                        try {
665                            db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
666                            // Also delete from CE table if user is unlocked; if user is currently
667                            // locked the account will be removed later by syncDeCeAccountsLocked
668                            if (userUnlocked) {
669                                db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
670                            }
671                            db.setTransactionSuccessful();
672                        } finally {
673                            db.endTransaction();
674                        }
675                        accountDeleted = true;
676
677                        logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
678                                accountId, accounts);
679
680                        final Account account = new Account(accountName, accountType);
681                        accounts.userDataCache.remove(account);
682                        accounts.authTokenCache.remove(account);
683                        accounts.accountTokenCaches.remove(account);
684                    } else {
685                        ArrayList<String> accountNames = accountNamesByType.get(accountType);
686                        if (accountNames == null) {
687                            accountNames = new ArrayList<String>();
688                            accountNamesByType.put(accountType, accountNames);
689                        }
690                        accountNames.add(accountName);
691                    }
692                }
693                for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
694                    final String accountType = cur.getKey();
695                    final ArrayList<String> accountNames = cur.getValue();
696                    final Account[] accountsForType = new Account[accountNames.size()];
697                    for (int i = 0; i < accountsForType.length; i++) {
698                        accountsForType[i] = new Account(accountNames.get(i), accountType);
699                    }
700                    accounts.accountCache.put(accountType, accountsForType);
701                }
702            } finally {
703                cursor.close();
704                if (accountDeleted) {
705                    sendAccountsChangedBroadcast(accounts.userId);
706                }
707            }
708        }
709    }
710
711    private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
712        // Get the UIDs of all apps that might have data on the device. We want
713        // to preserve user data if the app might otherwise be storing data.
714        List<PackageInfo> pkgsWithData =
715                mPackageManager.getInstalledPackagesAsUser(
716                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
717        SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
718        for (PackageInfo pkgInfo : pkgsWithData) {
719            if (pkgInfo.applicationInfo != null
720                    && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
721                knownUids.put(pkgInfo.applicationInfo.uid, true);
722            }
723        }
724        return knownUids;
725    }
726
727    private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
728            Context context,
729            int userId) {
730        AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
731        return getAuthenticatorTypeAndUIDForUser(authCache, userId);
732    }
733
734    private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
735            IAccountAuthenticatorCache authCache,
736            int userId) {
737        HashMap<String, Integer> knownAuth = new HashMap<>();
738        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
739                .getAllServices(userId)) {
740            knownAuth.put(service.type.type, service.uid);
741        }
742        return knownAuth;
743    }
744
745    private UserAccounts getUserAccountsForCaller() {
746        return getUserAccounts(UserHandle.getCallingUserId());
747    }
748
749    protected UserAccounts getUserAccounts(int userId) {
750        synchronized (mUsers) {
751            UserAccounts accounts = mUsers.get(userId);
752            boolean validateAccounts = false;
753            if (accounts == null) {
754                File preNDbFile = new File(getPreNDatabaseName(userId));
755                File deDbFile = new File(getDeDatabaseName(userId));
756                accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
757                initializeDebugDbSizeAndCompileSqlStatementForLogging(
758                        accounts.openHelper.getWritableDatabase(), accounts);
759                mUsers.append(userId, accounts);
760                purgeOldGrants(accounts);
761                validateAccounts = true;
762            }
763            // open CE database if necessary
764            if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
765                Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
766                synchronized (accounts.cacheLock) {
767                    File preNDatabaseFile = new File(getPreNDatabaseName(userId));
768                    File ceDatabaseFile = new File(getCeDatabaseName(userId));
769                    CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
770                    accounts.openHelper.attachCeDatabase(ceDatabaseFile);
771                }
772                syncDeCeAccountsLocked(accounts);
773            }
774            if (validateAccounts) {
775                validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
776            }
777            return accounts;
778        }
779    }
780
781    private void syncDeCeAccountsLocked(UserAccounts accounts) {
782        Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
783        final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
784        List<Account> accountsToRemove = CeDatabaseHelper.findCeAccountsNotInDe(db);
785        if (!accountsToRemove.isEmpty()) {
786            Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
787                    + accounts.userId + " was locked. Removing accounts from CE tables");
788            logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
789
790            for (Account account : accountsToRemove) {
791                removeAccountInternal(accounts, account, Process.myUid());
792            }
793        }
794    }
795
796    private void purgeOldGrantsAll() {
797        synchronized (mUsers) {
798            for (int i = 0; i < mUsers.size(); i++) {
799                purgeOldGrants(mUsers.valueAt(i));
800            }
801        }
802    }
803
804    private void purgeOldGrants(UserAccounts accounts) {
805        synchronized (accounts.cacheLock) {
806            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
807            final Cursor cursor = db.query(TABLE_GRANTS,
808                    new String[]{GRANTS_GRANTEE_UID},
809                    null, null, GRANTS_GRANTEE_UID, null, null);
810            try {
811                while (cursor.moveToNext()) {
812                    final int uid = cursor.getInt(0);
813                    final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
814                    if (packageExists) {
815                        continue;
816                    }
817                    Log.d(TAG, "deleting grants for UID " + uid
818                            + " because its package is no longer installed");
819                    db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
820                            new String[]{Integer.toString(uid)});
821                }
822            } finally {
823                cursor.close();
824            }
825        }
826    }
827
828    private void onUserRemoved(Intent intent) {
829        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
830        if (userId < 1) return;
831
832        UserAccounts accounts;
833        boolean userUnlocked;
834        synchronized (mUsers) {
835            accounts = mUsers.get(userId);
836            mUsers.remove(userId);
837            userUnlocked = mLocalUnlockedUsers.get(userId);
838            mLocalUnlockedUsers.delete(userId);
839        }
840        if (accounts != null) {
841            synchronized (accounts.cacheLock) {
842                accounts.openHelper.close();
843            }
844        }
845        Log.i(TAG, "Removing database files for user " + userId);
846        File dbFile = new File(getDeDatabaseName(userId));
847
848        deleteDbFileWarnIfFailed(dbFile);
849        // Remove CE file if user is unlocked, or FBE is not enabled
850        boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
851        if (!fbeEnabled || userUnlocked) {
852            File ceDb = new File(getCeDatabaseName(userId));
853            if (ceDb.exists()) {
854                deleteDbFileWarnIfFailed(ceDb);
855            }
856        }
857    }
858
859    private static void deleteDbFileWarnIfFailed(File dbFile) {
860        if (!SQLiteDatabase.deleteDatabase(dbFile)) {
861            Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
862        }
863    }
864
865    @VisibleForTesting
866    void onUserUnlocked(Intent intent) {
867        onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
868    }
869
870    void onUnlockUser(int userId) {
871        if (Log.isLoggable(TAG, Log.VERBOSE)) {
872            Log.v(TAG, "onUserUnlocked " + userId);
873        }
874        synchronized (mUsers) {
875            mLocalUnlockedUsers.put(userId, true);
876        }
877        if (userId < 1) return;
878        syncSharedAccounts(userId);
879    }
880
881    private void syncSharedAccounts(int userId) {
882        // Check if there's a shared account that needs to be created as an account
883        Account[] sharedAccounts = getSharedAccountsAsUser(userId);
884        if (sharedAccounts == null || sharedAccounts.length == 0) return;
885        Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
886        int parentUserId = UserManager.isSplitSystemUser()
887                ? getUserManager().getUserInfo(userId).restrictedProfileParentId
888                : UserHandle.USER_SYSTEM;
889        if (parentUserId < 0) {
890            Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
891            return;
892        }
893        for (Account sa : sharedAccounts) {
894            if (ArrayUtils.contains(accounts, sa)) continue;
895            // Account doesn't exist. Copy it now.
896            copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
897        }
898    }
899
900    @Override
901    public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
902        validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
903    }
904
905    @Override
906    public String getPassword(Account account) {
907        int callingUid = Binder.getCallingUid();
908        if (Log.isLoggable(TAG, Log.VERBOSE)) {
909            Log.v(TAG, "getPassword: " + account
910                    + ", caller's uid " + Binder.getCallingUid()
911                    + ", pid " + Binder.getCallingPid());
912        }
913        if (account == null) throw new IllegalArgumentException("account is null");
914        int userId = UserHandle.getCallingUserId();
915        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
916            String msg = String.format(
917                    "uid %s cannot get secrets for accounts of type: %s",
918                    callingUid,
919                    account.type);
920            throw new SecurityException(msg);
921        }
922        long identityToken = clearCallingIdentity();
923        try {
924            UserAccounts accounts = getUserAccounts(userId);
925            return readPasswordInternal(accounts, account);
926        } finally {
927            restoreCallingIdentity(identityToken);
928        }
929    }
930
931    private String readPasswordInternal(UserAccounts accounts, Account account) {
932        if (account == null) {
933            return null;
934        }
935        if (!isLocalUnlockedUser(accounts.userId)) {
936            Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
937            return null;
938        }
939
940        synchronized (accounts.cacheLock) {
941            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
942            return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
943                    account.type);
944        }
945    }
946
947    @Override
948    public String getPreviousName(Account account) {
949        if (Log.isLoggable(TAG, Log.VERBOSE)) {
950            Log.v(TAG, "getPreviousName: " + account
951                    + ", caller's uid " + Binder.getCallingUid()
952                    + ", pid " + Binder.getCallingPid());
953        }
954        if (account == null) throw new IllegalArgumentException("account is null");
955        int userId = UserHandle.getCallingUserId();
956        long identityToken = clearCallingIdentity();
957        try {
958            UserAccounts accounts = getUserAccounts(userId);
959            return readPreviousNameInternal(accounts, account);
960        } finally {
961            restoreCallingIdentity(identityToken);
962        }
963    }
964
965    private String readPreviousNameInternal(UserAccounts accounts, Account account) {
966        if  (account == null) {
967            return null;
968        }
969        synchronized (accounts.cacheLock) {
970            AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
971            if (previousNameRef == null) {
972                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
973                Cursor cursor = db.query(
974                        TABLE_ACCOUNTS,
975                        new String[]{ ACCOUNTS_PREVIOUS_NAME },
976                        ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
977                        new String[] { account.name, account.type },
978                        null,
979                        null,
980                        null);
981                try {
982                    if (cursor.moveToNext()) {
983                        String previousName = cursor.getString(0);
984                        previousNameRef = new AtomicReference<>(previousName);
985                        accounts.previousNameCache.put(account, previousNameRef);
986                        return previousName;
987                    } else {
988                        return null;
989                    }
990                } finally {
991                    cursor.close();
992                }
993            } else {
994                return previousNameRef.get();
995            }
996        }
997    }
998
999    @Override
1000    public String getUserData(Account account, String key) {
1001        final int callingUid = Binder.getCallingUid();
1002        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1003            String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1004                    account, key, callingUid, Binder.getCallingPid());
1005            Log.v(TAG, msg);
1006        }
1007        if (account == null) throw new IllegalArgumentException("account is null");
1008        if (key == null) throw new IllegalArgumentException("key is null");
1009        int userId = UserHandle.getCallingUserId();
1010        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1011            String msg = String.format(
1012                    "uid %s cannot get user data for accounts of type: %s",
1013                    callingUid,
1014                    account.type);
1015            throw new SecurityException(msg);
1016        }
1017        if (!isLocalUnlockedUser(userId)) {
1018            Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1019            return null;
1020        }
1021        long identityToken = clearCallingIdentity();
1022        try {
1023            UserAccounts accounts = getUserAccounts(userId);
1024            synchronized (accounts.cacheLock) {
1025                if (!accountExistsCacheLocked(accounts, account)) {
1026                    return null;
1027                }
1028                return readUserDataInternalLocked(accounts, account, key);
1029            }
1030        } finally {
1031            restoreCallingIdentity(identityToken);
1032        }
1033    }
1034
1035    @Override
1036    public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
1037        int callingUid = Binder.getCallingUid();
1038        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1039            Log.v(TAG, "getAuthenticatorTypes: "
1040                    + "for user id " + userId
1041                    + " caller's uid " + callingUid
1042                    + ", pid " + Binder.getCallingPid());
1043        }
1044        // Only allow the system process to read accounts of other users
1045        if (isCrossUser(callingUid, userId)) {
1046            throw new SecurityException(
1047                    String.format(
1048                            "User %s tying to get authenticator types for %s" ,
1049                            UserHandle.getCallingUserId(),
1050                            userId));
1051        }
1052
1053        final long identityToken = clearCallingIdentity();
1054        try {
1055            return getAuthenticatorTypesInternal(userId);
1056
1057        } finally {
1058            restoreCallingIdentity(identityToken);
1059        }
1060    }
1061
1062    /**
1063     * Should only be called inside of a clearCallingIdentity block.
1064     */
1065    private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
1066        Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1067                authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1068        AuthenticatorDescription[] types =
1069                new AuthenticatorDescription[authenticatorCollection.size()];
1070        int i = 0;
1071        for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1072                : authenticatorCollection) {
1073            types[i] = authenticator.type;
1074            i++;
1075        }
1076        return types;
1077    }
1078
1079
1080
1081    private boolean isCrossUser(int callingUid, int userId) {
1082        return (userId != UserHandle.getCallingUserId()
1083                && callingUid != Process.myUid()
1084                && mContext.checkCallingOrSelfPermission(
1085                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1086                                != PackageManager.PERMISSION_GRANTED);
1087    }
1088
1089    @Override
1090    public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
1091        Bundle.setDefusable(extras, true);
1092        final int callingUid = Binder.getCallingUid();
1093        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1094            Log.v(TAG, "addAccountExplicitly: " + account
1095                    + ", caller's uid " + callingUid
1096                    + ", pid " + Binder.getCallingPid());
1097        }
1098        if (account == null) throw new IllegalArgumentException("account is null");
1099        int userId = UserHandle.getCallingUserId();
1100        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1101            String msg = String.format(
1102                    "uid %s cannot explicitly add accounts of type: %s",
1103                    callingUid,
1104                    account.type);
1105            throw new SecurityException(msg);
1106        }
1107        /*
1108         * Child users are not allowed to add accounts. Only the accounts that are
1109         * shared by the parent profile can be added to child profile.
1110         *
1111         * TODO: Only allow accounts that were shared to be added by
1112         *     a limited user.
1113         */
1114
1115        // fails if the account already exists
1116        long identityToken = clearCallingIdentity();
1117        try {
1118            UserAccounts accounts = getUserAccounts(userId);
1119            return addAccountInternal(accounts, account, password, extras, callingUid);
1120        } finally {
1121            restoreCallingIdentity(identityToken);
1122        }
1123    }
1124
1125    @Override
1126    public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
1127            final int userFrom, int userTo) {
1128        int callingUid = Binder.getCallingUid();
1129        if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1130            throw new SecurityException("Calling copyAccountToUser requires "
1131                    + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1132        }
1133        final UserAccounts fromAccounts = getUserAccounts(userFrom);
1134        final UserAccounts toAccounts = getUserAccounts(userTo);
1135        if (fromAccounts == null || toAccounts == null) {
1136            if (response != null) {
1137                Bundle result = new Bundle();
1138                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1139                try {
1140                    response.onResult(result);
1141                } catch (RemoteException e) {
1142                    Slog.w(TAG, "Failed to report error back to the client." + e);
1143                }
1144            }
1145            return;
1146        }
1147
1148        Slog.d(TAG, "Copying account " + account.name
1149                + " from user " + userFrom + " to user " + userTo);
1150        long identityToken = clearCallingIdentity();
1151        try {
1152            new Session(fromAccounts, response, account.type, false,
1153                    false /* stripAuthTokenFromResult */, account.name,
1154                    false /* authDetailsRequired */) {
1155                @Override
1156                protected String toDebugString(long now) {
1157                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
1158                            + ", " + account.type;
1159                }
1160
1161                @Override
1162                public void run() throws RemoteException {
1163                    mAuthenticator.getAccountCredentialsForCloning(this, account);
1164                }
1165
1166                @Override
1167                public void onResult(Bundle result) {
1168                    Bundle.setDefusable(result, true);
1169                    if (result != null
1170                            && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1171                        // Create a Session for the target user and pass in the bundle
1172                        completeCloningAccount(response, result, account, toAccounts, userFrom);
1173                    } else {
1174                        super.onResult(result);
1175                    }
1176                }
1177            }.bind();
1178        } finally {
1179            restoreCallingIdentity(identityToken);
1180        }
1181    }
1182
1183    @Override
1184    public boolean accountAuthenticated(final Account account) {
1185        final int callingUid = Binder.getCallingUid();
1186        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1187            String msg = String.format(
1188                    "accountAuthenticated( account: %s, callerUid: %s)",
1189                    account,
1190                    callingUid);
1191            Log.v(TAG, msg);
1192        }
1193        if (account == null) {
1194            throw new IllegalArgumentException("account is null");
1195        }
1196        int userId = UserHandle.getCallingUserId();
1197        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1198            String msg = String.format(
1199                    "uid %s cannot notify authentication for accounts of type: %s",
1200                    callingUid,
1201                    account.type);
1202            throw new SecurityException(msg);
1203        }
1204
1205        if (!canUserModifyAccounts(userId, callingUid) ||
1206                !canUserModifyAccountsForType(userId, account.type, callingUid)) {
1207            return false;
1208        }
1209
1210        long identityToken = clearCallingIdentity();
1211        try {
1212            UserAccounts accounts = getUserAccounts(userId);
1213            return updateLastAuthenticatedTime(account);
1214        } finally {
1215            restoreCallingIdentity(identityToken);
1216        }
1217    }
1218
1219    private boolean updateLastAuthenticatedTime(Account account) {
1220        final UserAccounts accounts = getUserAccountsForCaller();
1221        synchronized (accounts.cacheLock) {
1222            final ContentValues values = new ContentValues();
1223            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1224            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1225            int i = db.update(
1226                    TABLE_ACCOUNTS,
1227                    values,
1228                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1229                    new String[] {
1230                            account.name, account.type
1231                    });
1232            if (i > 0) {
1233                return true;
1234            }
1235        }
1236        return false;
1237    }
1238
1239    private void completeCloningAccount(IAccountManagerResponse response,
1240            final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1241            final int parentUserId){
1242        Bundle.setDefusable(accountCredentials, true);
1243        long id = clearCallingIdentity();
1244        try {
1245            new Session(targetUser, response, account.type, false,
1246                    false /* stripAuthTokenFromResult */, account.name,
1247                    false /* authDetailsRequired */) {
1248                @Override
1249                protected String toDebugString(long now) {
1250                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
1251                            + ", " + account.type;
1252                }
1253
1254                @Override
1255                public void run() throws RemoteException {
1256                    // Confirm that the owner's account still exists before this step.
1257                    UserAccounts owner = getUserAccounts(parentUserId);
1258                    synchronized (owner.cacheLock) {
1259                        for (Account acc : getAccounts(parentUserId,
1260                                mContext.getOpPackageName())) {
1261                            if (acc.equals(account)) {
1262                                mAuthenticator.addAccountFromCredentials(
1263                                        this, account, accountCredentials);
1264                                break;
1265                            }
1266                        }
1267                    }
1268                }
1269
1270                @Override
1271                public void onResult(Bundle result) {
1272                    Bundle.setDefusable(result, true);
1273                    // TODO: Anything to do if if succedded?
1274                    // TODO: If it failed: Show error notification? Should we remove the shadow
1275                    // account to avoid retries?
1276                    super.onResult(result);
1277                }
1278
1279                @Override
1280                public void onError(int errorCode, String errorMessage) {
1281                    super.onError(errorCode,  errorMessage);
1282                    // TODO: Show error notification to user
1283                    // TODO: Should we remove the shadow account so that it doesn't keep trying?
1284                }
1285
1286            }.bind();
1287        } finally {
1288            restoreCallingIdentity(id);
1289        }
1290    }
1291
1292    private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
1293            Bundle extras, int callingUid) {
1294        Bundle.setDefusable(extras, true);
1295        if (account == null) {
1296            return false;
1297        }
1298        if (!isLocalUnlockedUser(accounts.userId)) {
1299            Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1300                    + " is locked. callingUid=" + callingUid);
1301            return false;
1302        }
1303        synchronized (accounts.cacheLock) {
1304            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1305            db.beginTransaction();
1306            try {
1307                long numMatches = DatabaseUtils.longForQuery(db,
1308                        "select count(*) from " + CE_TABLE_ACCOUNTS
1309                                + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1310                        new String[]{account.name, account.type});
1311                if (numMatches > 0) {
1312                    Log.w(TAG, "insertAccountIntoDatabase: " + account
1313                            + ", skipping since the account already exists");
1314                    return false;
1315                }
1316                ContentValues values = new ContentValues();
1317                values.put(ACCOUNTS_NAME, account.name);
1318                values.put(ACCOUNTS_TYPE, account.type);
1319                values.put(ACCOUNTS_PASSWORD, password);
1320                long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
1321                if (accountId < 0) {
1322                    Log.w(TAG, "insertAccountIntoDatabase: " + account
1323                            + ", skipping the DB insert failed");
1324                    return false;
1325                }
1326                // Insert into DE table
1327                values = new ContentValues();
1328                values.put(ACCOUNTS_ID, accountId);
1329                values.put(ACCOUNTS_NAME, account.name);
1330                values.put(ACCOUNTS_TYPE, account.type);
1331                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
1332                        System.currentTimeMillis());
1333                if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
1334                    Log.w(TAG, "insertAccountIntoDatabase: " + account
1335                            + ", skipping the DB insert failed");
1336                    return false;
1337                }
1338                if (extras != null) {
1339                    for (String key : extras.keySet()) {
1340                        final String value = extras.getString(key);
1341                        if (insertExtraLocked(db, accountId, key, value) < 0) {
1342                            Log.w(TAG, "insertAccountIntoDatabase: " + account
1343                                    + ", skipping since insertExtra failed for key " + key);
1344                            return false;
1345                        }
1346                    }
1347                }
1348                db.setTransactionSuccessful();
1349
1350                logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1351                        accounts, callingUid);
1352
1353                insertAccountIntoCacheLocked(accounts, account);
1354            } finally {
1355                db.endTransaction();
1356            }
1357        }
1358        if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1359            addAccountToLinkedRestrictedUsers(account, accounts.userId);
1360        }
1361
1362        // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1363        sendAccountsChangedBroadcast(accounts.userId);
1364        return true;
1365    }
1366
1367    private boolean isLocalUnlockedUser(int userId) {
1368        synchronized (mUsers) {
1369            return mLocalUnlockedUsers.get(userId);
1370        }
1371    }
1372
1373    /**
1374     * Adds the account to all linked restricted users as shared accounts. If the user is currently
1375     * running, then clone the account too.
1376     * @param account the account to share with limited users
1377     *
1378     */
1379    private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
1380        List<UserInfo> users = getUserManager().getUsers();
1381        for (UserInfo user : users) {
1382            if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
1383                addSharedAccountAsUser(account, user.id);
1384                if (isLocalUnlockedUser(user.id)) {
1385                    mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
1386                            MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
1387                }
1388            }
1389        }
1390    }
1391
1392    private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
1393        ContentValues values = new ContentValues();
1394        values.put(EXTRAS_KEY, key);
1395        values.put(EXTRAS_ACCOUNTS_ID, accountId);
1396        values.put(EXTRAS_VALUE, value);
1397        return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
1398    }
1399
1400    @Override
1401    public void hasFeatures(IAccountManagerResponse response,
1402            Account account, String[] features, String opPackageName) {
1403        int callingUid = Binder.getCallingUid();
1404        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1405            Log.v(TAG, "hasFeatures: " + account
1406                    + ", response " + response
1407                    + ", features " + stringArrayToString(features)
1408                    + ", caller's uid " + callingUid
1409                    + ", pid " + Binder.getCallingPid());
1410        }
1411        if (response == null) throw new IllegalArgumentException("response is null");
1412        if (account == null) throw new IllegalArgumentException("account is null");
1413        if (features == null) throw new IllegalArgumentException("features is null");
1414        int userId = UserHandle.getCallingUserId();
1415        checkReadAccountsPermitted(callingUid, account.type, userId,
1416                opPackageName);
1417
1418        long identityToken = clearCallingIdentity();
1419        try {
1420            UserAccounts accounts = getUserAccounts(userId);
1421            new TestFeaturesSession(accounts, response, account, features).bind();
1422        } finally {
1423            restoreCallingIdentity(identityToken);
1424        }
1425    }
1426
1427    private class TestFeaturesSession extends Session {
1428        private final String[] mFeatures;
1429        private final Account mAccount;
1430
1431        public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
1432                Account account, String[] features) {
1433            super(accounts, response, account.type, false /* expectActivityLaunch */,
1434                    true /* stripAuthTokenFromResult */, account.name,
1435                    false /* authDetailsRequired */);
1436            mFeatures = features;
1437            mAccount = account;
1438        }
1439
1440        @Override
1441        public void run() throws RemoteException {
1442            try {
1443                mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1444            } catch (RemoteException e) {
1445                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1446            }
1447        }
1448
1449        @Override
1450        public void onResult(Bundle result) {
1451            Bundle.setDefusable(result, true);
1452            IAccountManagerResponse response = getResponseAndClose();
1453            if (response != null) {
1454                try {
1455                    if (result == null) {
1456                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
1457                        return;
1458                    }
1459                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1460                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1461                                + response);
1462                    }
1463                    final Bundle newResult = new Bundle();
1464                    newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1465                            result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1466                    response.onResult(newResult);
1467                } catch (RemoteException e) {
1468                    // if the caller is dead then there is no one to care about remote exceptions
1469                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1470                        Log.v(TAG, "failure while notifying response", e);
1471                    }
1472                }
1473            }
1474        }
1475
1476        @Override
1477        protected String toDebugString(long now) {
1478            return super.toDebugString(now) + ", hasFeatures"
1479                    + ", " + mAccount
1480                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1481        }
1482    }
1483
1484    @Override
1485    public void renameAccount(
1486            IAccountManagerResponse response, Account accountToRename, String newName) {
1487        final int callingUid = Binder.getCallingUid();
1488        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1489            Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
1490                + ", caller's uid " + callingUid
1491                + ", pid " + Binder.getCallingPid());
1492        }
1493        if (accountToRename == null) throw new IllegalArgumentException("account is null");
1494        int userId = UserHandle.getCallingUserId();
1495        if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
1496            String msg = String.format(
1497                    "uid %s cannot rename accounts of type: %s",
1498                    callingUid,
1499                    accountToRename.type);
1500            throw new SecurityException(msg);
1501        }
1502        long identityToken = clearCallingIdentity();
1503        try {
1504            UserAccounts accounts = getUserAccounts(userId);
1505            Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
1506            Bundle result = new Bundle();
1507            result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1508            result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1509            try {
1510                response.onResult(result);
1511            } catch (RemoteException e) {
1512                Log.w(TAG, e.getMessage());
1513            }
1514        } finally {
1515            restoreCallingIdentity(identityToken);
1516        }
1517    }
1518
1519    private Account renameAccountInternal(
1520            UserAccounts accounts, Account accountToRename, String newName) {
1521        Account resultAccount = null;
1522        /*
1523         * Cancel existing notifications. Let authenticators
1524         * re-post notifications as required. But we don't know if
1525         * the authenticators have bound their notifications to
1526         * now stale account name data.
1527         *
1528         * With a rename api, we might not need to do this anymore but it
1529         * shouldn't hurt.
1530         */
1531        cancelNotification(
1532                getSigninRequiredNotificationId(accounts, accountToRename),
1533                 new UserHandle(accounts.userId));
1534        synchronized(accounts.credentialsPermissionNotificationIds) {
1535            for (Pair<Pair<Account, String>, Integer> pair:
1536                    accounts.credentialsPermissionNotificationIds.keySet()) {
1537                if (accountToRename.equals(pair.first.first)) {
1538                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
1539                    cancelNotification(id, new UserHandle(accounts.userId));
1540                }
1541            }
1542        }
1543        synchronized (accounts.cacheLock) {
1544            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
1545            db.beginTransaction();
1546            Account renamedAccount = new Account(newName, accountToRename.type);
1547            try {
1548                final long accountId = getAccountIdLocked(db, accountToRename);
1549                if (accountId >= 0) {
1550                    final ContentValues values = new ContentValues();
1551                    values.put(ACCOUNTS_NAME, newName);
1552                    final String[] argsAccountId = { String.valueOf(accountId) };
1553                    db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1554                    // Update NAME/PREVIOUS_NAME in DE accounts table
1555                    values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1556                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1557                    db.setTransactionSuccessful();
1558                    logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1559                            accounts);
1560                }
1561            } finally {
1562                db.endTransaction();
1563            }
1564            /*
1565             * Database transaction was successful. Clean up cached
1566             * data associated with the account in the user profile.
1567             */
1568            insertAccountIntoCacheLocked(accounts, renamedAccount);
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.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 (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        int usrId = UserHandle.getCallingUserId();
2858        long identityToken = clearCallingIdentity();
2859        try {
2860            UserAccounts accounts = getUserAccounts(usrId);
2861            logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2862                    TABLE_ACCOUNTS, uid);
2863            new StartAccountSession(
2864                    accounts,
2865                    response,
2866                    accountType,
2867                    expectActivityLaunch,
2868                    null /* accountName */,
2869                    false /* authDetailsRequired */,
2870                    true /* updateLastAuthenticationTime */,
2871                    isPasswordForwardingAllowed) {
2872                @Override
2873                public void run() throws RemoteException {
2874                    mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2875                            requiredFeatures, options);
2876                }
2877
2878                @Override
2879                protected String toDebugString(long now) {
2880                    String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2881                    return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2882                            + accountType + ", requiredFeatures "
2883                            + (requiredFeatures != null ? requiredFeaturesStr : null);
2884                }
2885            }.bind();
2886        } finally {
2887            restoreCallingIdentity(identityToken);
2888        }
2889    }
2890
2891    /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2892    private abstract class StartAccountSession extends Session {
2893
2894        private final boolean mIsPasswordForwardingAllowed;
2895
2896        public StartAccountSession(
2897                UserAccounts accounts,
2898                IAccountManagerResponse response,
2899                String accountType,
2900                boolean expectActivityLaunch,
2901                String accountName,
2902                boolean authDetailsRequired,
2903                boolean updateLastAuthenticationTime,
2904                boolean isPasswordForwardingAllowed) {
2905            super(accounts, response, accountType, expectActivityLaunch,
2906                    true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2907                    updateLastAuthenticationTime);
2908            mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
2909        }
2910
2911        @Override
2912        public void onResult(Bundle result) {
2913            Bundle.setDefusable(result, true);
2914            mNumResults++;
2915            Intent intent = null;
2916            if (result != null
2917                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2918                checkKeyIntent(
2919                        Binder.getCallingUid(),
2920                        intent);
2921                // Omit passwords if the caller isn't permitted to see them.
2922                if (!mIsPasswordForwardingAllowed) {
2923                    result.remove(AccountManager.KEY_PASSWORD);
2924                }
2925            }
2926            IAccountManagerResponse response;
2927            if (mExpectActivityLaunch && result != null
2928                    && result.containsKey(AccountManager.KEY_INTENT)) {
2929                response = mResponse;
2930            } else {
2931                response = getResponseAndClose();
2932            }
2933            if (response == null) {
2934                return;
2935            }
2936            if (result == null) {
2937                if (Log.isLoggable(TAG, Log.VERBOSE)) {
2938                    Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2939                            + response);
2940                }
2941                sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2942                        "null bundle returned");
2943                return;
2944            }
2945
2946            if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2947                // All AccountManager error codes are greater
2948                // than 0
2949                sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2950                        result.getString(AccountManager.KEY_ERROR_MESSAGE));
2951                return;
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 (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
3462            final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3463            // Use null token which means any token. Having a token means the package
3464            // is trusted by the authenticator, hence it is fine to access the account.
3465            if (permissionIsGranted(account, null, uid, userId)) {
3466                return true;
3467            }
3468            // In addition to the permissions required to get an auth token we also allow
3469            // the account to be accessed by holders of the get accounts permissions.
3470            return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3471                    || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3472        } catch (NameNotFoundException e) {
3473            return false;
3474        }
3475    }
3476
3477    private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3478        final long identity = Binder.clearCallingIdentity();
3479        try {
3480            IPackageManager pm = ActivityThread.getPackageManager();
3481            if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3482                return false;
3483            }
3484            final int opCode = AppOpsManager.permissionToOpCode(permission);
3485            return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3486                    opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3487        } catch (RemoteException e) {
3488            /* ignore - local call */
3489        } finally {
3490            Binder.restoreCallingIdentity(identity);
3491        }
3492        return false;
3493    }
3494
3495    @Override
3496    public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3497            @NonNull String packageName, @NonNull UserHandle userHandle) {
3498        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
3499            throw new SecurityException("Can be called only by system UID");
3500        }
3501
3502        Preconditions.checkNotNull(account, "account cannot be null");
3503        Preconditions.checkNotNull(packageName, "packageName cannot be null");
3504        Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3505
3506        final int userId = userHandle.getIdentifier();
3507
3508        Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3509
3510        final int uid;
3511        try {
3512            uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3513        } catch (NameNotFoundException e) {
3514            Slog.e(TAG, "Unknown package " + packageName);
3515            return null;
3516        }
3517
3518        Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3519
3520        return PendingIntent.getActivityAsUser(
3521                mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3522                        | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3523                null, new UserHandle(userId)).getIntentSender();
3524    }
3525
3526    private Intent newRequestAccountAccessIntent(Account account, String packageName,
3527            int uid, RemoteCallback callback) {
3528        return newGrantCredentialsPermissionIntent(account, packageName, uid,
3529                new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3530            @Override
3531            public void onResult(Bundle value) throws RemoteException {
3532                handleAuthenticatorResponse(true);
3533            }
3534
3535            @Override
3536            public void onRequestContinued() {
3537                /* ignore */
3538            }
3539
3540            @Override
3541            public void onError(int errorCode, String errorMessage) throws RemoteException {
3542                handleAuthenticatorResponse(false);
3543            }
3544
3545            private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3546                cancelNotification(getCredentialPermissionNotificationId(account,
3547                        AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
3548                        UserHandle.getUserHandleForUid(uid));
3549                if (callback != null) {
3550                    Bundle result = new Bundle();
3551                    result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3552                    callback.sendResult(result);
3553                }
3554            }
3555        }), AccountManager.ACCOUNT_ACCESS_TOKEN, false);
3556    }
3557
3558    @Override
3559    public boolean someUserHasAccount(@NonNull final Account account) {
3560        if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3561            throw new SecurityException("Only system can check for accounts across users");
3562        }
3563        final long token = Binder.clearCallingIdentity();
3564        try {
3565            AccountAndUser[] allAccounts = getAllAccounts();
3566            for (int i = allAccounts.length - 1; i >= 0; i--) {
3567                if (allAccounts[i].account.equals(account)) {
3568                    return true;
3569                }
3570            }
3571            return false;
3572        } finally {
3573            Binder.restoreCallingIdentity(token);
3574        }
3575    }
3576
3577    private class GetAccountsByTypeAndFeatureSession extends Session {
3578        private final String[] mFeatures;
3579        private volatile Account[] mAccountsOfType = null;
3580        private volatile ArrayList<Account> mAccountsWithFeatures = null;
3581        private volatile int mCurrentAccount = 0;
3582        private final int mCallingUid;
3583
3584        public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
3585                IAccountManagerResponse response, String type, String[] features, int callingUid) {
3586            super(accounts, response, type, false /* expectActivityLaunch */,
3587                    true /* stripAuthTokenFromResult */, null /* accountName */,
3588                    false /* authDetailsRequired */);
3589            mCallingUid = callingUid;
3590            mFeatures = features;
3591        }
3592
3593        @Override
3594        public void run() throws RemoteException {
3595            synchronized (mAccounts.cacheLock) {
3596                mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3597                        null);
3598            }
3599            // check whether each account matches the requested features
3600            mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
3601            mCurrentAccount = 0;
3602
3603            checkAccount();
3604        }
3605
3606        public void checkAccount() {
3607            if (mCurrentAccount >= mAccountsOfType.length) {
3608                sendResult();
3609                return;
3610            }
3611
3612            final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3613            if (accountAuthenticator == null) {
3614                // It is possible that the authenticator has died, which is indicated by
3615                // mAuthenticator being set to null. If this happens then just abort.
3616                // There is no need to send back a result or error in this case since
3617                // that already happened when mAuthenticator was cleared.
3618                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3619                    Log.v(TAG, "checkAccount: aborting session since we are no longer"
3620                            + " connected to the authenticator, " + toDebugString());
3621                }
3622                return;
3623            }
3624            try {
3625                accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
3626            } catch (RemoteException e) {
3627                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
3628            }
3629        }
3630
3631        @Override
3632        public void onResult(Bundle result) {
3633            Bundle.setDefusable(result, true);
3634            mNumResults++;
3635            if (result == null) {
3636                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
3637                return;
3638            }
3639            if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
3640                mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3641            }
3642            mCurrentAccount++;
3643            checkAccount();
3644        }
3645
3646        public void sendResult() {
3647            IAccountManagerResponse response = getResponseAndClose();
3648            if (response != null) {
3649                try {
3650                    Account[] accounts = new Account[mAccountsWithFeatures.size()];
3651                    for (int i = 0; i < accounts.length; i++) {
3652                        accounts[i] = mAccountsWithFeatures.get(i);
3653                    }
3654                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3655                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3656                                + response);
3657                    }
3658                    Bundle result = new Bundle();
3659                    result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3660                    response.onResult(result);
3661                } catch (RemoteException e) {
3662                    // if the caller is dead then there is no one to care about remote exceptions
3663                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3664                        Log.v(TAG, "failure while notifying response", e);
3665                    }
3666                }
3667            }
3668        }
3669
3670
3671        @Override
3672        protected String toDebugString(long now) {
3673            return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3674                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3675        }
3676    }
3677
3678    /**
3679     * Returns the accounts visible to the client within the context of a specific user
3680     * @hide
3681     */
3682    @NonNull
3683    public Account[] getAccounts(int userId, String opPackageName) {
3684        int callingUid = Binder.getCallingUid();
3685        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3686                opPackageName);
3687        if (visibleAccountTypes.isEmpty()) {
3688            return new Account[0];
3689        }
3690        long identityToken = clearCallingIdentity();
3691        try {
3692            UserAccounts accounts = getUserAccounts(userId);
3693            return getAccountsInternal(
3694                    accounts,
3695                    callingUid,
3696                    null,  // packageName
3697                    visibleAccountTypes);
3698        } finally {
3699            restoreCallingIdentity(identityToken);
3700        }
3701    }
3702
3703    /**
3704     * Returns accounts for all running users.
3705     *
3706     * @hide
3707     */
3708    @NonNull
3709    public AccountAndUser[] getRunningAccounts() {
3710        final int[] runningUserIds;
3711        try {
3712            runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3713        } catch (RemoteException e) {
3714            // Running in system_server; should never happen
3715            throw new RuntimeException(e);
3716        }
3717        return getAccounts(runningUserIds);
3718    }
3719
3720    /** {@hide} */
3721    @NonNull
3722    public AccountAndUser[] getAllAccounts() {
3723        final List<UserInfo> users = getUserManager().getUsers(true);
3724        final int[] userIds = new int[users.size()];
3725        for (int i = 0; i < userIds.length; i++) {
3726            userIds[i] = users.get(i).id;
3727        }
3728        return getAccounts(userIds);
3729    }
3730
3731    @NonNull
3732    private AccountAndUser[] getAccounts(int[] userIds) {
3733        final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
3734        for (int userId : userIds) {
3735            UserAccounts userAccounts = getUserAccounts(userId);
3736            if (userAccounts == null) continue;
3737            synchronized (userAccounts.cacheLock) {
3738                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3739                        Binder.getCallingUid(), null);
3740                for (int a = 0; a < accounts.length; a++) {
3741                    runningAccounts.add(new AccountAndUser(accounts[a], userId));
3742                }
3743            }
3744        }
3745
3746        AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3747        return runningAccounts.toArray(accountsArray);
3748    }
3749
3750    @Override
3751    @NonNull
3752    public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3753        return getAccountsAsUser(type, userId, null, -1, opPackageName);
3754    }
3755
3756    @NonNull
3757    private Account[] getAccountsAsUser(
3758            String type,
3759            int userId,
3760            String callingPackage,
3761            int packageUid,
3762            String opPackageName) {
3763        int callingUid = Binder.getCallingUid();
3764        // Only allow the system process to read accounts of other users
3765        if (userId != UserHandle.getCallingUserId()
3766                && callingUid != Process.myUid()
3767                && mContext.checkCallingOrSelfPermission(
3768                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3769                    != PackageManager.PERMISSION_GRANTED) {
3770            throw new SecurityException("User " + UserHandle.getCallingUserId()
3771                    + " trying to get account for " + userId);
3772        }
3773
3774        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3775            Log.v(TAG, "getAccounts: accountType " + type
3776                    + ", caller's uid " + Binder.getCallingUid()
3777                    + ", pid " + Binder.getCallingPid());
3778        }
3779        // If the original calling app was using the framework account chooser activity, we'll
3780        // be passed in the original caller's uid here, which is what should be used for filtering.
3781        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3782            callingUid = packageUid;
3783            opPackageName = callingPackage;
3784        }
3785
3786        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3787                opPackageName);
3788        if (visibleAccountTypes.isEmpty()
3789                || (type != null && !visibleAccountTypes.contains(type))) {
3790            return new Account[0];
3791        } else if (visibleAccountTypes.contains(type)) {
3792            // Prune the list down to just the requested type.
3793            visibleAccountTypes = new ArrayList<>();
3794            visibleAccountTypes.add(type);
3795        } // else aggregate all the visible accounts (it won't matter if the
3796          // list is empty).
3797
3798        long identityToken = clearCallingIdentity();
3799        try {
3800            UserAccounts accounts = getUserAccounts(userId);
3801            return getAccountsInternal(
3802                    accounts,
3803                    callingUid,
3804                    callingPackage,
3805                    visibleAccountTypes);
3806        } finally {
3807            restoreCallingIdentity(identityToken);
3808        }
3809    }
3810
3811    @NonNull
3812    private Account[] getAccountsInternal(
3813            UserAccounts userAccounts,
3814            int callingUid,
3815            String callingPackage,
3816            List<String> visibleAccountTypes) {
3817        synchronized (userAccounts.cacheLock) {
3818            ArrayList<Account> visibleAccounts = new ArrayList<>();
3819            for (String visibleType : visibleAccountTypes) {
3820                Account[] accountsForType = getAccountsFromCacheLocked(
3821                        userAccounts, visibleType, callingUid, callingPackage);
3822                if (accountsForType != null) {
3823                    visibleAccounts.addAll(Arrays.asList(accountsForType));
3824                }
3825            }
3826            Account[] result = new Account[visibleAccounts.size()];
3827            for (int i = 0; i < visibleAccounts.size(); i++) {
3828                result[i] = visibleAccounts.get(i);
3829            }
3830            return result;
3831        }
3832    }
3833
3834    @Override
3835    public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3836        checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
3837        Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3838        for (Account account : accounts) {
3839            addSharedAccountAsUser(account, userId);
3840        }
3841    }
3842
3843    private boolean addSharedAccountAsUser(Account account, int userId) {
3844        userId = handleIncomingUser(userId);
3845        UserAccounts accounts = getUserAccounts(userId);
3846        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3847        ContentValues values = new ContentValues();
3848        values.put(ACCOUNTS_NAME, account.name);
3849        values.put(ACCOUNTS_TYPE, account.type);
3850        db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3851                new String[] {account.name, account.type});
3852        long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3853        if (accountId < 0) {
3854            Log.w(TAG, "insertAccountIntoDatabase: " + account
3855                    + ", skipping the DB insert failed");
3856            return false;
3857        }
3858        logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
3859        return true;
3860    }
3861
3862    @Override
3863    public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3864        userId = handleIncomingUser(userId);
3865        UserAccounts accounts = getUserAccounts(userId);
3866        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3867        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3868        final ContentValues values = new ContentValues();
3869        values.put(ACCOUNTS_NAME, newName);
3870        int r = db.update(
3871                TABLE_SHARED_ACCOUNTS,
3872                values,
3873                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3874                new String[] { account.name, account.type });
3875        if (r > 0) {
3876            int callingUid = getCallingUid();
3877            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3878                    sharedTableAccountId, accounts, callingUid);
3879            // Recursively rename the account.
3880            renameAccountInternal(accounts, account, newName);
3881        }
3882        return r > 0;
3883    }
3884
3885    @Override
3886    public boolean removeSharedAccountAsUser(Account account, int userId) {
3887        return removeSharedAccountAsUser(account, userId, getCallingUid());
3888    }
3889
3890    private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
3891        userId = handleIncomingUser(userId);
3892        UserAccounts accounts = getUserAccounts(userId);
3893        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3894        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3895        int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3896                new String[] {account.name, account.type});
3897        if (r > 0) {
3898            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3899                    sharedTableAccountId, accounts, callingUid);
3900            removeAccountInternal(accounts, account, callingUid);
3901        }
3902        return r > 0;
3903    }
3904
3905    @Override
3906    public Account[] getSharedAccountsAsUser(int userId) {
3907        userId = handleIncomingUser(userId);
3908        UserAccounts accounts = getUserAccounts(userId);
3909        ArrayList<Account> accountList = new ArrayList<>();
3910        Cursor cursor = null;
3911        try {
3912            cursor = accounts.openHelper.getReadableDatabase()
3913                    .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3914                    null, null, null, null, null);
3915            if (cursor != null && cursor.moveToFirst()) {
3916                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3917                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3918                do {
3919                    accountList.add(new Account(cursor.getString(nameIndex),
3920                            cursor.getString(typeIndex)));
3921                } while (cursor.moveToNext());
3922            }
3923        } finally {
3924            if (cursor != null) {
3925                cursor.close();
3926            }
3927        }
3928        Account[] accountArray = new Account[accountList.size()];
3929        accountList.toArray(accountArray);
3930        return accountArray;
3931    }
3932
3933    @Override
3934    @NonNull
3935    public Account[] getAccounts(String type, String opPackageName) {
3936        return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
3937    }
3938
3939    @Override
3940    @NonNull
3941    public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
3942        int callingUid = Binder.getCallingUid();
3943        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3944            throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3945                    + callingUid + " with uid=" + uid);
3946        }
3947        return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3948                opPackageName);
3949    }
3950
3951    @Override
3952    @NonNull
3953    public Account[] getAccountsByTypeForPackage(String type, String packageName,
3954            String opPackageName) {
3955        int packageUid = -1;
3956        try {
3957            packageUid = AppGlobals.getPackageManager().getPackageUid(
3958                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3959                    UserHandle.getCallingUserId());
3960        } catch (RemoteException re) {
3961            Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3962            return new Account[0];
3963        }
3964        return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3965                packageUid, opPackageName);
3966    }
3967
3968    @Override
3969    public void getAccountsByFeatures(
3970            IAccountManagerResponse response,
3971            String type,
3972            String[] features,
3973            String opPackageName) {
3974        int callingUid = Binder.getCallingUid();
3975        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3976            Log.v(TAG, "getAccounts: accountType " + type
3977                    + ", response " + response
3978                    + ", features " + stringArrayToString(features)
3979                    + ", caller's uid " + callingUid
3980                    + ", pid " + Binder.getCallingPid());
3981        }
3982        if (response == null) throw new IllegalArgumentException("response is null");
3983        if (type == null) throw new IllegalArgumentException("accountType is null");
3984        int userId = UserHandle.getCallingUserId();
3985
3986        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3987                opPackageName);
3988        if (!visibleAccountTypes.contains(type)) {
3989            Bundle result = new Bundle();
3990            // Need to return just the accounts that are from matching signatures.
3991            result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
3992            try {
3993                response.onResult(result);
3994            } catch (RemoteException e) {
3995                Log.e(TAG, "Cannot respond to caller do to exception." , e);
3996            }
3997            return;
3998        }
3999        long identityToken = clearCallingIdentity();
4000        try {
4001            UserAccounts userAccounts = getUserAccounts(userId);
4002            if (features == null || features.length == 0) {
4003                Account[] accounts;
4004                synchronized (userAccounts.cacheLock) {
4005                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
4006                }
4007                Bundle result = new Bundle();
4008                result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4009                onResult(response, result);
4010                return;
4011            }
4012            new GetAccountsByTypeAndFeatureSession(
4013                    userAccounts,
4014                    response,
4015                    type,
4016                    features,
4017                    callingUid).bind();
4018        } finally {
4019            restoreCallingIdentity(identityToken);
4020        }
4021    }
4022
4023    private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
4024        Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
4025                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
4026        try {
4027            if (cursor.moveToNext()) {
4028                return cursor.getLong(0);
4029            }
4030            return -1;
4031        } finally {
4032            cursor.close();
4033        }
4034    }
4035
4036    private long getAccountIdLocked(SQLiteDatabase db, Account account) {
4037        Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
4038                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
4039        try {
4040            if (cursor.moveToNext()) {
4041                return cursor.getLong(0);
4042            }
4043            return -1;
4044        } finally {
4045            cursor.close();
4046        }
4047    }
4048
4049    private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
4050        Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
4051                EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
4052                new String[]{key}, null, null, null);
4053        try {
4054            if (cursor.moveToNext()) {
4055                return cursor.getLong(0);
4056            }
4057            return -1;
4058        } finally {
4059            cursor.close();
4060        }
4061    }
4062
4063    private abstract class Session extends IAccountAuthenticatorResponse.Stub
4064            implements IBinder.DeathRecipient, ServiceConnection {
4065        IAccountManagerResponse mResponse;
4066        final String mAccountType;
4067        final boolean mExpectActivityLaunch;
4068        final long mCreationTime;
4069        final String mAccountName;
4070        // Indicates if we need to add auth details(like last credential time)
4071        final boolean mAuthDetailsRequired;
4072        // If set, we need to update the last authenticated time. This is
4073        // currently
4074        // used on
4075        // successful confirming credentials.
4076        final boolean mUpdateLastAuthenticatedTime;
4077
4078        public int mNumResults = 0;
4079        private int mNumRequestContinued = 0;
4080        private int mNumErrors = 0;
4081
4082        IAccountAuthenticator mAuthenticator = null;
4083
4084        private final boolean mStripAuthTokenFromResult;
4085        protected final UserAccounts mAccounts;
4086
4087        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4088                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4089                boolean authDetailsRequired) {
4090            this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4091                    accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4092        }
4093
4094        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4095                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4096                boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
4097            super();
4098            //if (response == null) throw new IllegalArgumentException("response is null");
4099            if (accountType == null) throw new IllegalArgumentException("accountType is null");
4100            mAccounts = accounts;
4101            mStripAuthTokenFromResult = stripAuthTokenFromResult;
4102            mResponse = response;
4103            mAccountType = accountType;
4104            mExpectActivityLaunch = expectActivityLaunch;
4105            mCreationTime = SystemClock.elapsedRealtime();
4106            mAccountName = accountName;
4107            mAuthDetailsRequired = authDetailsRequired;
4108            mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
4109
4110            synchronized (mSessions) {
4111                mSessions.put(toString(), this);
4112            }
4113            if (response != null) {
4114                try {
4115                    response.asBinder().linkToDeath(this, 0 /* flags */);
4116                } catch (RemoteException e) {
4117                    mResponse = null;
4118                    binderDied();
4119                }
4120            }
4121        }
4122
4123        IAccountManagerResponse getResponseAndClose() {
4124            if (mResponse == null) {
4125                // this session has already been closed
4126                return null;
4127            }
4128            IAccountManagerResponse response = mResponse;
4129            close(); // this clears mResponse so we need to save the response before this call
4130            return response;
4131        }
4132
4133        /**
4134         * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4135         * security policy.
4136         *
4137         * In particular we want to make sure that the Authenticator doesn't try to trick users
4138         * into launching aribtrary intents on the device via by tricking to click authenticator
4139         * supplied entries in the system Settings app.
4140         */
4141        protected void checkKeyIntent(
4142                int authUid,
4143                Intent intent) throws SecurityException {
4144            long bid = Binder.clearCallingIdentity();
4145            try {
4146                PackageManager pm = mContext.getPackageManager();
4147                ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4148                ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4149                int targetUid = targetActivityInfo.applicationInfo.uid;
4150                if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) {
4151                    String pkgName = targetActivityInfo.packageName;
4152                    String activityName = targetActivityInfo.name;
4153                    String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4154                            + "does not share a signature with the supplying authenticator (%s).";
4155                    throw new SecurityException(
4156                            String.format(tmpl, activityName, pkgName, mAccountType));
4157                }
4158            } finally {
4159                Binder.restoreCallingIdentity(bid);
4160            }
4161        }
4162
4163        private void close() {
4164            synchronized (mSessions) {
4165                if (mSessions.remove(toString()) == null) {
4166                    // the session was already closed, so bail out now
4167                    return;
4168                }
4169            }
4170            if (mResponse != null) {
4171                // stop listening for response deaths
4172                mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4173
4174                // clear this so that we don't accidentally send any further results
4175                mResponse = null;
4176            }
4177            cancelTimeout();
4178            unbind();
4179        }
4180
4181        @Override
4182        public void binderDied() {
4183            mResponse = null;
4184            close();
4185        }
4186
4187        protected String toDebugString() {
4188            return toDebugString(SystemClock.elapsedRealtime());
4189        }
4190
4191        protected String toDebugString(long now) {
4192            return "Session: expectLaunch " + mExpectActivityLaunch
4193                    + ", connected " + (mAuthenticator != null)
4194                    + ", stats (" + mNumResults + "/" + mNumRequestContinued
4195                    + "/" + mNumErrors + ")"
4196                    + ", lifetime " + ((now - mCreationTime) / 1000.0);
4197        }
4198
4199        void bind() {
4200            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4201                Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4202            }
4203            if (!bindToAuthenticator(mAccountType)) {
4204                Log.d(TAG, "bind attempt failed for " + toDebugString());
4205                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
4206            }
4207        }
4208
4209        private void unbind() {
4210            if (mAuthenticator != null) {
4211                mAuthenticator = null;
4212                mContext.unbindService(this);
4213            }
4214        }
4215
4216        public void cancelTimeout() {
4217            mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
4218        }
4219
4220        @Override
4221        public void onServiceConnected(ComponentName name, IBinder service) {
4222            mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
4223            try {
4224                run();
4225            } catch (RemoteException e) {
4226                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4227                        "remote exception");
4228            }
4229        }
4230
4231        @Override
4232        public void onServiceDisconnected(ComponentName name) {
4233            mAuthenticator = null;
4234            IAccountManagerResponse response = getResponseAndClose();
4235            if (response != null) {
4236                try {
4237                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4238                            "disconnected");
4239                } catch (RemoteException e) {
4240                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4241                        Log.v(TAG, "Session.onServiceDisconnected: "
4242                                + "caught RemoteException while responding", e);
4243                    }
4244                }
4245            }
4246        }
4247
4248        public abstract void run() throws RemoteException;
4249
4250        public void onTimedOut() {
4251            IAccountManagerResponse response = getResponseAndClose();
4252            if (response != null) {
4253                try {
4254                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4255                            "timeout");
4256                } catch (RemoteException e) {
4257                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4258                        Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4259                                e);
4260                    }
4261                }
4262            }
4263        }
4264
4265        @Override
4266        public void onResult(Bundle result) {
4267            Bundle.setDefusable(result, true);
4268            mNumResults++;
4269            Intent intent = null;
4270            if (result != null) {
4271                boolean isSuccessfulConfirmCreds = result.getBoolean(
4272                        AccountManager.KEY_BOOLEAN_RESULT, false);
4273                boolean isSuccessfulUpdateCredsOrAddAccount =
4274                        result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4275                        && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
4276                // We should only update lastAuthenticated time, if
4277                // mUpdateLastAuthenticatedTime is true and the confirmRequest
4278                // or updateRequest was successful
4279                boolean needUpdate = mUpdateLastAuthenticatedTime
4280                        && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
4281                if (needUpdate || mAuthDetailsRequired) {
4282                    boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4283                    if (needUpdate && accountPresent) {
4284                        updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4285                    }
4286                    if (mAuthDetailsRequired) {
4287                        long lastAuthenticatedTime = -1;
4288                        if (accountPresent) {
4289                            lastAuthenticatedTime = DatabaseUtils.longForQuery(
4290                                    mAccounts.openHelper.getReadableDatabase(),
4291                                    "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4292                                            + " FROM " +
4293                                            TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4294                                            + ACCOUNTS_TYPE + "=?",
4295                                    new String[] {
4296                                            mAccountName, mAccountType
4297                                    });
4298                        }
4299                        result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
4300                                lastAuthenticatedTime);
4301                    }
4302                }
4303            }
4304            if (result != null
4305                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
4306                checkKeyIntent(
4307                        Binder.getCallingUid(),
4308                        intent);
4309            }
4310            if (result != null
4311                    && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
4312                String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4313                String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
4314                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4315                    Account account = new Account(accountName, accountType);
4316                    cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4317                            new UserHandle(mAccounts.userId));
4318                }
4319            }
4320            IAccountManagerResponse response;
4321            if (mExpectActivityLaunch && result != null
4322                    && result.containsKey(AccountManager.KEY_INTENT)) {
4323                response = mResponse;
4324            } else {
4325                response = getResponseAndClose();
4326            }
4327            if (response != null) {
4328                try {
4329                    if (result == null) {
4330                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
4331                            Log.v(TAG, getClass().getSimpleName()
4332                                    + " calling onError() on response " + response);
4333                        }
4334                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
4335                                "null bundle returned");
4336                    } else {
4337                        if (mStripAuthTokenFromResult) {
4338                            result.remove(AccountManager.KEY_AUTHTOKEN);
4339                        }
4340                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
4341                            Log.v(TAG, getClass().getSimpleName()
4342                                    + " calling onResult() on response " + response);
4343                        }
4344                        if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4345                                (intent == null)) {
4346                            // All AccountManager error codes are greater than 0
4347                            response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4348                                    result.getString(AccountManager.KEY_ERROR_MESSAGE));
4349                        } else {
4350                            response.onResult(result);
4351                        }
4352                    }
4353                } catch (RemoteException e) {
4354                    // if the caller is dead then there is no one to care about remote exceptions
4355                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4356                        Log.v(TAG, "failure while notifying response", e);
4357                    }
4358                }
4359            }
4360        }
4361
4362        @Override
4363        public void onRequestContinued() {
4364            mNumRequestContinued++;
4365        }
4366
4367        @Override
4368        public void onError(int errorCode, String errorMessage) {
4369            mNumErrors++;
4370            IAccountManagerResponse response = getResponseAndClose();
4371            if (response != null) {
4372                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4373                    Log.v(TAG, getClass().getSimpleName()
4374                            + " calling onError() on response " + response);
4375                }
4376                try {
4377                    response.onError(errorCode, errorMessage);
4378                } catch (RemoteException e) {
4379                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
4380                        Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4381                    }
4382                }
4383            } else {
4384                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4385                    Log.v(TAG, "Session.onError: already closed");
4386                }
4387            }
4388        }
4389
4390        /**
4391         * find the component name for the authenticator and initiate a bind
4392         * if no authenticator or the bind fails then return false, otherwise return true
4393         */
4394        private boolean bindToAuthenticator(String authenticatorType) {
4395            final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4396            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4397                    AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
4398            if (authenticatorInfo == null) {
4399                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4400                    Log.v(TAG, "there is no authenticator for " + authenticatorType
4401                            + ", bailing out");
4402                }
4403                return false;
4404            }
4405
4406            if (!isLocalUnlockedUser(mAccounts.userId)
4407                    && !authenticatorInfo.componentInfo.directBootAware) {
4408                Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4409                        + " which isn't encryption aware");
4410                return false;
4411            }
4412
4413            Intent intent = new Intent();
4414            intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4415            intent.setComponent(authenticatorInfo.componentName);
4416            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4417                Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4418            }
4419            if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
4420                    UserHandle.of(mAccounts.userId))) {
4421                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4422                    Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4423                }
4424                return false;
4425            }
4426
4427            return true;
4428        }
4429    }
4430
4431    private class MessageHandler extends Handler {
4432        MessageHandler(Looper looper) {
4433            super(looper);
4434        }
4435
4436        @Override
4437        public void handleMessage(Message msg) {
4438            switch (msg.what) {
4439                case MESSAGE_TIMED_OUT:
4440                    Session session = (Session)msg.obj;
4441                    session.onTimedOut();
4442                    break;
4443
4444                case MESSAGE_COPY_SHARED_ACCOUNT:
4445                    copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
4446                    break;
4447
4448                default:
4449                    throw new IllegalStateException("unhandled message: " + msg.what);
4450            }
4451        }
4452    }
4453
4454    @VisibleForTesting
4455    String getPreNDatabaseName(int userId) {
4456        File systemDir = Environment.getDataSystemDirectory();
4457        File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4458                PRE_N_DATABASE_NAME);
4459        if (userId == 0) {
4460            // Migrate old file, if it exists, to the new location.
4461            // Make sure the new file doesn't already exist. A dummy file could have been
4462            // accidentally created in the old location, causing the new one to become corrupted
4463            // as well.
4464            File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
4465            if (oldFile.exists() && !databaseFile.exists()) {
4466                // Check for use directory; create if it doesn't exist, else renameTo will fail
4467                File userDir = Environment.getUserSystemDirectory(userId);
4468                if (!userDir.exists()) {
4469                    if (!userDir.mkdirs()) {
4470                        throw new IllegalStateException("User dir cannot be created: " + userDir);
4471                    }
4472                }
4473                if (!oldFile.renameTo(databaseFile)) {
4474                    throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4475                }
4476            }
4477        }
4478        return databaseFile.getPath();
4479    }
4480
4481    @VisibleForTesting
4482    String getDeDatabaseName(int userId) {
4483        File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4484                DE_DATABASE_NAME);
4485        return databaseFile.getPath();
4486    }
4487
4488    @VisibleForTesting
4489    String getCeDatabaseName(int userId) {
4490        File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4491                CE_DATABASE_NAME);
4492        return databaseFile.getPath();
4493    }
4494
4495    private static class DebugDbHelper{
4496        private DebugDbHelper() {
4497        }
4498
4499        private static String TABLE_DEBUG = "debug_table";
4500
4501        // Columns for the table
4502        private static String ACTION_TYPE = "action_type";
4503        private static String TIMESTAMP = "time";
4504        private static String CALLER_UID = "caller_uid";
4505        private static String TABLE_NAME = "table_name";
4506        private static String KEY = "primary_key";
4507
4508        // These actions correspond to the occurrence of real actions. Since
4509        // these are called by the authenticators, the uid associated will be
4510        // of the authenticator.
4511        private static String ACTION_SET_PASSWORD = "action_set_password";
4512        private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4513        private static String ACTION_ACCOUNT_ADD = "action_account_add";
4514        private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
4515        private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
4516        private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4517        private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4518
4519        // These actions don't necessarily correspond to any action on
4520        // accountDb taking place. As an example, there might be a request for
4521        // addingAccount, which might not lead to addition of account on grounds
4522        // of bad authentication. We will still be logging it to keep track of
4523        // who called.
4524        private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4525        private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
4526        private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
4527
4528        //This action doesn't add account to accountdb. Account is only
4529        // added in finishSession which may be in a different user profile.
4530        private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
4531        private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4532                "action_called_account_session_finish";
4533
4534        private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4535
4536        private static void createDebugTable(SQLiteDatabase db) {
4537            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4538                    + ACCOUNTS_ID + " INTEGER,"
4539                    + ACTION_TYPE + " TEXT NOT NULL, "
4540                    + TIMESTAMP + " DATETIME,"
4541                    + CALLER_UID + " INTEGER NOT NULL,"
4542                    + TABLE_NAME + " TEXT NOT NULL,"
4543                    + KEY + " INTEGER PRIMARY KEY)");
4544            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4545        }
4546    }
4547
4548    private void logRecord(UserAccounts accounts, String action, String tableName) {
4549        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4550        logRecord(db, action, tableName, -1, accounts);
4551    }
4552
4553    private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4554        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4555        logRecord(db, action, tableName, -1, accounts, uid);
4556    }
4557
4558    /*
4559     * This function receives an opened writable database.
4560     */
4561    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4562            UserAccounts userAccount) {
4563        logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4564    }
4565
4566    /*
4567     * This function receives an opened writable database.
4568     */
4569    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4570            UserAccounts userAccount, int callingUid) {
4571        SQLiteStatement logStatement = userAccount.statementForLogging;
4572        logStatement.bindLong(1, accountId);
4573        logStatement.bindString(2, action);
4574        logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
4575        logStatement.bindLong(4, callingUid);
4576        logStatement.bindString(5, tableName);
4577        logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
4578        logStatement.execute();
4579        logStatement.clearBindings();
4580        userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
4581                % MAX_DEBUG_DB_SIZE;
4582    }
4583
4584    /*
4585     * This should only be called once to compile the sql statement for logging
4586     * and to find the insertion point.
4587     */
4588    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
4589            UserAccounts userAccount) {
4590        // Initialize the count if not done earlier.
4591        int size = (int) getDebugTableRowCount(db);
4592        if (size >= MAX_DEBUG_DB_SIZE) {
4593            // Table is full, and we need to find the point where to insert.
4594            userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
4595        } else {
4596            userAccount.debugDbInsertionPoint = size;
4597        }
4598        compileSqlStatementForLogging(db, userAccount);
4599    }
4600
4601    private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
4602        String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
4603                + " VALUES (?,?,?,?,?,?)";
4604        userAccount.statementForLogging = db.compileStatement(sql);
4605    }
4606
4607    private long getDebugTableRowCount(SQLiteDatabase db) {
4608        String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
4609        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4610    }
4611
4612    /*
4613     * Finds the row key where the next insertion should take place. This should
4614     * be invoked only if the table has reached its full capacity.
4615     */
4616    private long getDebugTableInsertionPoint(SQLiteDatabase db) {
4617        // This query finds the smallest timestamp value (and if 2 records have
4618        // same timestamp, the choose the lower id).
4619        String queryCountDebugDbRows = new StringBuilder()
4620                .append("SELECT ").append(DebugDbHelper.KEY)
4621                .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
4622                .append(" ORDER BY ")
4623                .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
4624                .append(" LIMIT 1")
4625                .toString();
4626        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4627    }
4628
4629    static class PreNDatabaseHelper extends SQLiteOpenHelper {
4630        private final Context mContext;
4631        private final int mUserId;
4632
4633        public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
4634            super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
4635            mContext = context;
4636            mUserId = userId;
4637        }
4638
4639        @Override
4640        public void onCreate(SQLiteDatabase db) {
4641            // We use PreNDatabaseHelper only if pre-N db exists
4642            throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
4643        }
4644
4645        private void createSharedAccountsTable(SQLiteDatabase db) {
4646            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4647                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4648                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4649                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4650                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4651        }
4652
4653        private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4654            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4655                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4656        }
4657
4658        private void addOldAccountNameColumn(SQLiteDatabase db) {
4659            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4660        }
4661
4662        private void addDebugTable(SQLiteDatabase db) {
4663            DebugDbHelper.createDebugTable(db);
4664        }
4665
4666        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4667            db.execSQL(""
4668                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4669                    + " BEGIN"
4670                    + "   DELETE FROM " + TABLE_AUTHTOKENS
4671                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4672                    + "   DELETE FROM " + TABLE_EXTRAS
4673                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4674                    + "   DELETE FROM " + TABLE_GRANTS
4675                    + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4676                    + " END");
4677        }
4678
4679        private void createGrantsTable(SQLiteDatabase db) {
4680            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4681                    + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4682                    + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4683                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4684                    + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4685                    +   "," + GRANTS_GRANTEE_UID + "))");
4686        }
4687
4688        private void populateMetaTableWithAuthTypeAndUID(
4689                SQLiteDatabase db,
4690                Map<String, Integer> authTypeAndUIDMap) {
4691            Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator();
4692            while (iterator.hasNext()) {
4693                Entry<String, Integer> entry = iterator.next();
4694                ContentValues values = new ContentValues();
4695                values.put(META_KEY,
4696                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
4697                values.put(META_VALUE, entry.getValue());
4698                db.insert(TABLE_META, null, values);
4699            }
4700        }
4701
4702        /**
4703         * Pre-N database may need an upgrade before splitting
4704         */
4705        @Override
4706        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4707            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4708
4709            if (oldVersion == 1) {
4710                // no longer need to do anything since the work is done
4711                // when upgrading from version 2
4712                oldVersion++;
4713            }
4714
4715            if (oldVersion == 2) {
4716                createGrantsTable(db);
4717                db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4718                createAccountsDeletionTrigger(db);
4719                oldVersion++;
4720            }
4721
4722            if (oldVersion == 3) {
4723                db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4724                        " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4725                oldVersion++;
4726            }
4727
4728            if (oldVersion == 4) {
4729                createSharedAccountsTable(db);
4730                oldVersion++;
4731            }
4732
4733            if (oldVersion == 5) {
4734                addOldAccountNameColumn(db);
4735                oldVersion++;
4736            }
4737
4738            if (oldVersion == 6) {
4739                addLastSuccessfullAuthenticatedTimeColumn(db);
4740                oldVersion++;
4741            }
4742
4743            if (oldVersion == 7) {
4744                addDebugTable(db);
4745                oldVersion++;
4746            }
4747
4748            if (oldVersion == 8) {
4749                populateMetaTableWithAuthTypeAndUID(
4750                        db,
4751                        AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
4752                oldVersion++;
4753            }
4754
4755            if (oldVersion != newVersion) {
4756                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4757            }
4758        }
4759
4760        @Override
4761        public void onOpen(SQLiteDatabase db) {
4762            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4763        }
4764    }
4765
4766    static class DeDatabaseHelper extends SQLiteOpenHelper {
4767
4768        private final int mUserId;
4769        private volatile boolean mCeAttached;
4770
4771        private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
4772            super(context, deDatabaseName, null, DE_DATABASE_VERSION);
4773            mUserId = userId;
4774        }
4775
4776        /**
4777         * This call needs to be made while the mCacheLock is held. The way to
4778         * ensure this is to get the lock any time a method is called ont the DatabaseHelper
4779         * @param db The database.
4780         */
4781        @Override
4782        public void onCreate(SQLiteDatabase db) {
4783            Log.i(TAG, "Creating DE database for user " + mUserId);
4784            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4785                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
4786                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4787                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4788                    + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
4789                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
4790                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4791
4792            db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4793                    + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4794                    + META_VALUE + " TEXT)");
4795
4796            createGrantsTable(db);
4797            createSharedAccountsTable(db);
4798            createAccountsDeletionTrigger(db);
4799            DebugDbHelper.createDebugTable(db);
4800        }
4801
4802        private void createSharedAccountsTable(SQLiteDatabase db) {
4803            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4804                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4805                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4806                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4807                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4808        }
4809
4810        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4811            db.execSQL(""
4812                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4813                    + " BEGIN"
4814                    + "   DELETE FROM " + TABLE_GRANTS
4815                    + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4816                    + " END");
4817        }
4818
4819        private void createGrantsTable(SQLiteDatabase db) {
4820            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4821                    + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4822                    + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4823                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4824                    + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4825                    +   "," + GRANTS_GRANTEE_UID + "))");
4826        }
4827
4828        @Override
4829        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4830            Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4831
4832            if (oldVersion != newVersion) {
4833                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4834            }
4835        }
4836
4837        public void attachCeDatabase(File ceDbFile) {
4838            SQLiteDatabase db = getWritableDatabase();
4839            db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
4840            mCeAttached = true;
4841        }
4842
4843        public boolean isCeDatabaseAttached() {
4844            return mCeAttached;
4845        }
4846
4847
4848        public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
4849            if(!mCeAttached) {
4850                Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
4851                        + " is still locked. CE database is not yet available.", new Throwable());
4852            }
4853            return super.getReadableDatabase();
4854        }
4855
4856        public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
4857            if(!mCeAttached) {
4858                Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
4859                        + " is still locked. CE database is not yet available.", new Throwable());
4860            }
4861            return super.getWritableDatabase();
4862        }
4863
4864        @Override
4865        public void onOpen(SQLiteDatabase db) {
4866            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
4867        }
4868
4869        private void migratePreNDbToDe(File preNDbFile) {
4870            Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
4871            SQLiteDatabase db = getWritableDatabase();
4872            db.execSQL("ATTACH DATABASE '" +  preNDbFile.getPath() + "' AS preNDb");
4873            db.beginTransaction();
4874            // Copy accounts fields
4875            db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
4876                    + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
4877                    + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4878                    + ") "
4879                    + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
4880                    + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4881                    + " FROM preNDb." + TABLE_ACCOUNTS);
4882            // Copy SHARED_ACCOUNTS
4883            db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
4884                    + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
4885                    "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
4886                    + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
4887            // Copy DEBUG_TABLE
4888            db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
4889                    + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
4890                    + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
4891                    + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
4892                    "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
4893                    + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
4894                    + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
4895                    + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
4896            // Copy GRANTS
4897            db.execSQL("INSERT INTO " + TABLE_GRANTS
4898                    + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
4899                    + GRANTS_GRANTEE_UID + ") " +
4900                    "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
4901                    + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
4902            // Copy META
4903            db.execSQL("INSERT INTO " + TABLE_META
4904                    + "(" + META_KEY + "," + META_VALUE + ") "
4905                    + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
4906            db.setTransactionSuccessful();
4907            db.endTransaction();
4908
4909            db.execSQL("DETACH DATABASE preNDb");
4910        }
4911
4912        static DeDatabaseHelper create(
4913                Context context,
4914                int userId,
4915                File preNDatabaseFile,
4916                File deDatabaseFile) {
4917            boolean newDbExists = deDatabaseFile.exists();
4918            DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
4919                    deDatabaseFile.getPath());
4920            // If the db just created, and there is a legacy db, migrate it
4921            if (!newDbExists && preNDatabaseFile.exists()) {
4922                // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
4923                PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
4924                        preNDatabaseFile.getPath());
4925                // Open the database to force upgrade if required
4926                preNDatabaseHelper.getWritableDatabase();
4927                preNDatabaseHelper.close();
4928                // Move data without SPII to DE
4929                deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
4930            }
4931            return deDatabaseHelper;
4932        }
4933    }
4934
4935    static class CeDatabaseHelper extends SQLiteOpenHelper {
4936
4937        public CeDatabaseHelper(Context context, String ceDatabaseName) {
4938            super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
4939        }
4940
4941        /**
4942         * This call needs to be made while the mCacheLock is held.
4943         * @param db The database.
4944         */
4945        @Override
4946        public void onCreate(SQLiteDatabase db) {
4947            Log.i(TAG, "Creating CE database " + getDatabaseName());
4948            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4949                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4950                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4951                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4952                    + ACCOUNTS_PASSWORD + " TEXT, "
4953                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4954
4955            db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
4956                    + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
4957                    + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4958                    + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
4959                    + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
4960                    + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
4961
4962            db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
4963                    + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4964                    + EXTRAS_ACCOUNTS_ID + " INTEGER, "
4965                    + EXTRAS_KEY + " TEXT NOT NULL, "
4966                    + EXTRAS_VALUE + " TEXT, "
4967                    + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
4968
4969            createAccountsDeletionTrigger(db);
4970        }
4971
4972        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4973            db.execSQL(""
4974                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4975                    + " BEGIN"
4976                    + "   DELETE FROM " + TABLE_AUTHTOKENS
4977                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4978                    + "   DELETE FROM " + TABLE_EXTRAS
4979                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4980                    + " END");
4981        }
4982
4983        @Override
4984        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4985            Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
4986
4987            if (oldVersion == 9) {
4988                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4989                    Log.v(TAG, "onUpgrade upgrading to v10");
4990                }
4991                db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
4992                db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
4993                // Recreate the trigger, since the old one references the table to be removed
4994                db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
4995                createAccountsDeletionTrigger(db);
4996                db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
4997                db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
4998                oldVersion ++;
4999            }
5000
5001            if (oldVersion != newVersion) {
5002                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5003            }
5004        }
5005
5006        @Override
5007        public void onOpen(SQLiteDatabase db) {
5008            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
5009        }
5010
5011        static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
5012                String type) {
5013            Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
5014                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
5015                    new String[]{name, type}, null, null, null);
5016            try {
5017                if (cursor.moveToNext()) {
5018                    return cursor.getString(0);
5019                }
5020                return null;
5021            } finally {
5022                cursor.close();
5023            }
5024        }
5025
5026        static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
5027            // Select accounts from CE that do not exist in DE
5028            Cursor cursor = db.rawQuery(
5029                    "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
5030                            + " FROM " + CE_TABLE_ACCOUNTS
5031                            + " WHERE NOT EXISTS "
5032                            + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
5033                            + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
5034                            + " )", null);
5035            try {
5036                List<Account> accounts = new ArrayList<>(cursor.getCount());
5037                while (cursor.moveToNext()) {
5038                    String accountName = cursor.getString(0);
5039                    String accountType = cursor.getString(1);
5040                    accounts.add(new Account(accountName, accountType));
5041                }
5042                return accounts;
5043            } finally {
5044                cursor.close();
5045            }
5046        }
5047
5048        /**
5049         * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
5050         * it also performs migration to the new CE database.
5051         * @param context
5052         * @param userId id of the user where the database is located
5053         */
5054        static CeDatabaseHelper create(
5055                Context context,
5056                int userId,
5057                File preNDatabaseFile,
5058                File ceDatabaseFile) {
5059            boolean newDbExists = ceDatabaseFile.exists();
5060            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5061                Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
5062                        + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
5063            }
5064            boolean removeOldDb = false;
5065            if (!newDbExists && preNDatabaseFile.exists()) {
5066                removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
5067            }
5068            // Try to open and upgrade if necessary
5069            CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
5070            ceHelper.getWritableDatabase();
5071            ceHelper.close();
5072            if (removeOldDb) {
5073                Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
5074                if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
5075                    Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
5076                }
5077            }
5078            return ceHelper;
5079        }
5080
5081        private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
5082            Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
5083            try {
5084                FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
5085            } catch (IOException e) {
5086                Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
5087                // Try to remove potentially damaged file if I/O error occurred
5088                deleteDbFileWarnIfFailed(ceDbFile);
5089                return false;
5090            }
5091            return true;
5092        }
5093    }
5094
5095    public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
5096        return asBinder();
5097    }
5098
5099    /**
5100     * Searches array of arguments for the specified string
5101     * @param args array of argument strings
5102     * @param value value to search for
5103     * @return true if the value is contained in the array
5104     */
5105    private static boolean scanArgs(String[] args, String value) {
5106        if (args != null) {
5107            for (String arg : args) {
5108                if (value.equals(arg)) {
5109                    return true;
5110                }
5111            }
5112        }
5113        return false;
5114    }
5115
5116    @Override
5117    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
5118        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
5119                != PackageManager.PERMISSION_GRANTED) {
5120            fout.println("Permission Denial: can't dump AccountsManager from from pid="
5121                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
5122                    + " without permission " + android.Manifest.permission.DUMP);
5123            return;
5124        }
5125        final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
5126        final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
5127
5128        final List<UserInfo> users = getUserManager().getUsers();
5129        for (UserInfo user : users) {
5130            ipw.println("User " + user + ":");
5131            ipw.increaseIndent();
5132            dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5133            ipw.println();
5134            ipw.decreaseIndent();
5135        }
5136    }
5137
5138    private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5139            String[] args, boolean isCheckinRequest) {
5140        synchronized (userAccounts.cacheLock) {
5141            final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
5142
5143            if (isCheckinRequest) {
5144                // This is a checkin request. *Only* upload the account types and the count of each.
5145                Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
5146                        null, null, ACCOUNTS_TYPE, null, null);
5147                try {
5148                    while (cursor.moveToNext()) {
5149                        // print type,count
5150                        fout.println(cursor.getString(0) + "," + cursor.getString(1));
5151                    }
5152                } finally {
5153                    if (cursor != null) {
5154                        cursor.close();
5155                    }
5156                }
5157            } else {
5158                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
5159                        Process.myUid(), null);
5160                fout.println("Accounts: " + accounts.length);
5161                for (Account account : accounts) {
5162                    fout.println("  " + account);
5163                }
5164
5165                // Add debug information.
5166                fout.println();
5167                Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
5168                        null, null, null, null, DebugDbHelper.TIMESTAMP);
5169                fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
5170                fout.println("Accounts History");
5171                try {
5172                    while (cursor.moveToNext()) {
5173                        // print type,count
5174                        fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
5175                                cursor.getString(2) + "," + cursor.getString(3) + ","
5176                                + cursor.getString(4) + "," + cursor.getString(5));
5177                    }
5178                } finally {
5179                    cursor.close();
5180                }
5181
5182                fout.println();
5183                synchronized (mSessions) {
5184                    final long now = SystemClock.elapsedRealtime();
5185                    fout.println("Active Sessions: " + mSessions.size());
5186                    for (Session session : mSessions.values()) {
5187                        fout.println("  " + session.toDebugString(now));
5188                    }
5189                }
5190
5191                fout.println();
5192                mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
5193            }
5194        }
5195    }
5196
5197    private void doNotification(UserAccounts accounts, Account account, CharSequence message,
5198            Intent intent, String packageName, final int userId) {
5199        long identityToken = clearCallingIdentity();
5200        try {
5201            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5202                Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5203            }
5204
5205            if (intent.getComponent() != null &&
5206                    GrantCredentialsPermissionActivity.class.getName().equals(
5207                            intent.getComponent().getClassName())) {
5208                createNoCredentialsPermissionNotification(account, intent, packageName, userId);
5209            } else {
5210                Context contextForUser = getContextForUser(new UserHandle(userId));
5211                final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
5212                intent.addCategory(String.valueOf(notificationId));
5213
5214                final String notificationTitleFormat =
5215                        contextForUser.getText(R.string.notification_title).toString();
5216                Notification n = new Notification.Builder(contextForUser)
5217                        .setWhen(0)
5218                        .setSmallIcon(android.R.drawable.stat_sys_warning)
5219                        .setColor(contextForUser.getColor(
5220                                com.android.internal.R.color.system_notification_accent_color))
5221                        .setContentTitle(String.format(notificationTitleFormat, account.name))
5222                        .setContentText(message)
5223                        .setContentIntent(PendingIntent.getActivityAsUser(
5224                                mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
5225                                null, new UserHandle(userId)))
5226                        .build();
5227                installNotification(notificationId, n, packageName, userId);
5228            }
5229        } finally {
5230            restoreCallingIdentity(identityToken);
5231        }
5232    }
5233
5234    @VisibleForTesting
5235    protected void installNotification(int notificationId, final Notification notification,
5236            UserHandle user) {
5237        installNotification(notificationId, notification, "android", user.getIdentifier());
5238    }
5239
5240    private void installNotification(int notificationId, final Notification notification,
5241            String packageName, int userId) {
5242        final long token = clearCallingIdentity();
5243        try {
5244            INotificationManager notificationManager = NotificationManager.getService();
5245            try {
5246                notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
5247                        notificationId, notification, new int[1], userId);
5248            } catch (RemoteException e) {
5249                /* ignore - local call */
5250            }
5251        } finally {
5252            Binder.restoreCallingIdentity(token);
5253        }
5254    }
5255
5256    @VisibleForTesting
5257    protected void cancelNotification(int id, UserHandle user) {
5258        cancelNotification(id, mContext.getPackageName(), user);
5259    }
5260
5261    protected void cancelNotification(int id, String packageName, UserHandle user) {
5262        long identityToken = clearCallingIdentity();
5263        try {
5264            INotificationManager service = INotificationManager.Stub.asInterface(
5265                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
5266            service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
5267        } catch (RemoteException e) {
5268            /* ignore - local call */
5269        } finally {
5270            restoreCallingIdentity(identityToken);
5271        }
5272    }
5273
5274    private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
5275        for (String perm : permissions) {
5276            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5277                if (Log.isLoggable(TAG, Log.VERBOSE)) {
5278                    Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
5279                }
5280                final int opCode = AppOpsManager.permissionToOpCode(perm);
5281                if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
5282                        opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5283                    return true;
5284                }
5285            }
5286        }
5287        return false;
5288    }
5289
5290    private int handleIncomingUser(int userId) {
5291        try {
5292            return ActivityManagerNative.getDefault().handleIncomingUser(
5293                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5294        } catch (RemoteException re) {
5295            // Shouldn't happen, local.
5296        }
5297        return userId;
5298    }
5299
5300    private boolean isPrivileged(int callingUid) {
5301        final int callingUserId = UserHandle.getUserId(callingUid);
5302
5303        final PackageManager userPackageManager;
5304        try {
5305            userPackageManager = mContext.createPackageContextAsUser(
5306                    "android", 0, new UserHandle(callingUserId)).getPackageManager();
5307        } catch (NameNotFoundException e) {
5308            return false;
5309        }
5310
5311        String[] packages = userPackageManager.getPackagesForUid(callingUid);
5312        for (String name : packages) {
5313            try {
5314                PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
5315                if (packageInfo != null
5316                        && (packageInfo.applicationInfo.privateFlags
5317                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
5318                    return true;
5319                }
5320            } catch (PackageManager.NameNotFoundException e) {
5321                return false;
5322            }
5323        }
5324        return false;
5325    }
5326
5327    private boolean permissionIsGranted(
5328            Account account, String authTokenType, int callerUid, int userId) {
5329        if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5330            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5331                Log.v(TAG, "Access to " + account + " granted calling uid is system");
5332            }
5333            return true;
5334        }
5335
5336        if (isPrivileged(callerUid)) {
5337            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5338                Log.v(TAG, "Access to " + account + " granted calling uid "
5339                        + callerUid + " privileged");
5340            }
5341            return true;
5342        }
5343        if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5344            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5345                Log.v(TAG, "Access to " + account + " granted calling uid "
5346                        + callerUid + " manages the account");
5347            }
5348            return true;
5349        }
5350        if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5351            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5352                Log.v(TAG, "Access to " + account + " granted calling uid "
5353                        + callerUid + " user granted access");
5354            }
5355            return true;
5356        }
5357
5358        if (Log.isLoggable(TAG, Log.VERBOSE)) {
5359            Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5360        }
5361
5362        return false;
5363    }
5364
5365    private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5366            String opPackageName) {
5367        if (accountType == null) {
5368            return false;
5369        } else {
5370            return getTypesVisibleToCaller(callingUid, userId,
5371                    opPackageName).contains(accountType);
5372        }
5373    }
5374
5375    private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5376        if (accountType == null) {
5377            return false;
5378        } else {
5379            return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5380        }
5381    }
5382
5383    private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5384            String opPackageName) {
5385        boolean isPermitted =
5386                isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5387                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
5388        return getTypesForCaller(callingUid, userId, isPermitted);
5389    }
5390
5391    private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5392        return getTypesForCaller(callingUid, userId, false);
5393    }
5394
5395    private List<String> getTypesForCaller(
5396            int callingUid, int userId, boolean isOtherwisePermitted) {
5397        List<String> managedAccountTypes = new ArrayList<>();
5398        long identityToken = Binder.clearCallingIdentity();
5399        Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5400        try {
5401            serviceInfos = mAuthenticatorCache.getAllServices(userId);
5402        } finally {
5403            Binder.restoreCallingIdentity(identityToken);
5404        }
5405        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
5406                serviceInfos) {
5407            final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5408            if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5409                managedAccountTypes.add(serviceInfo.type.type);
5410            }
5411        }
5412        return managedAccountTypes;
5413    }
5414
5415    private boolean isAccountPresentForCaller(String accountName, String accountType) {
5416        if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5417            for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5418                if (account.name.equals(accountName)) {
5419                    return true;
5420                }
5421            }
5422        }
5423        return false;
5424    }
5425
5426    private static void checkManageUsersPermission(String message) {
5427        if (ActivityManager.checkComponentPermission(
5428                android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5429                != PackageManager.PERMISSION_GRANTED) {
5430            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5431        }
5432    }
5433
5434    private static void checkManageOrCreateUsersPermission(String message) {
5435        if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5436                Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5437                ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5438                        Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5439            throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5440                    + message);
5441        }
5442    }
5443
5444    private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5445            int callerUid) {
5446        if (callerUid == Process.SYSTEM_UID) {
5447            return true;
5448        }
5449        UserAccounts accounts = getUserAccountsForCaller();
5450        synchronized (accounts.cacheLock) {
5451            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
5452
5453            final String query;
5454            final String[] args;
5455
5456            if (authTokenType != null) {
5457                query = COUNT_OF_MATCHING_GRANTS;
5458                args = new String[] {String.valueOf(callerUid), authTokenType,
5459                        account.name, account.type};
5460            } else {
5461                query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
5462                args = new String[] {String.valueOf(callerUid), account.name,
5463                        account.type};
5464            }
5465            final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
5466            if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5467                // TODO: Skip this check when running automated tests. Replace this
5468                // with a more general solution.
5469                Log.d(TAG, "no credentials permission for usage of " + account + ", "
5470                        + authTokenType + " by uid " + callerUid
5471                        + " but ignoring since device is in test harness.");
5472                return true;
5473            }
5474            return permissionGranted;
5475        }
5476    }
5477
5478    private boolean isSystemUid(int callingUid) {
5479        String[] packages = null;
5480        long ident = Binder.clearCallingIdentity();
5481        try {
5482            packages = mPackageManager.getPackagesForUid(callingUid);
5483        } finally {
5484            Binder.restoreCallingIdentity(ident);
5485        }
5486        if (packages != null) {
5487            for (String name : packages) {
5488                try {
5489                    PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5490                    if (packageInfo != null
5491                            && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5492                                    != 0) {
5493                        return true;
5494                    }
5495                } catch (PackageManager.NameNotFoundException e) {
5496                    Log.w(TAG, String.format("Could not find package [%s]", name), e);
5497                }
5498            }
5499        } else {
5500            Log.w(TAG, "No known packages with uid " + callingUid);
5501        }
5502        return false;
5503    }
5504
5505    /** Succeeds if any of the specified permissions are granted. */
5506    private void checkReadAccountsPermitted(
5507            int callingUid,
5508            String accountType,
5509            int userId,
5510            String opPackageName) {
5511        if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
5512            String msg = String.format(
5513                    "caller uid %s cannot access %s accounts",
5514                    callingUid,
5515                    accountType);
5516            Log.w(TAG, "  " + msg);
5517            throw new SecurityException(msg);
5518        }
5519    }
5520
5521    private boolean canUserModifyAccounts(int userId, int callingUid) {
5522        // the managing app can always modify accounts
5523        if (isProfileOwner(callingUid)) {
5524            return true;
5525        }
5526        if (getUserManager().getUserRestrictions(new UserHandle(userId))
5527                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5528            return false;
5529        }
5530        return true;
5531    }
5532
5533    private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5534        // the managing app can always modify accounts
5535        if (isProfileOwner(callingUid)) {
5536            return true;
5537        }
5538        DevicePolicyManager dpm = (DevicePolicyManager) mContext
5539                .getSystemService(Context.DEVICE_POLICY_SERVICE);
5540        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
5541        if (typesArray == null) {
5542            return true;
5543        }
5544        for (String forbiddenType : typesArray) {
5545            if (forbiddenType.equals(accountType)) {
5546                return false;
5547            }
5548        }
5549        return true;
5550    }
5551
5552    private boolean isProfileOwner(int uid) {
5553        final DevicePolicyManagerInternal dpmi =
5554                LocalServices.getService(DevicePolicyManagerInternal.class);
5555        return (dpmi != null)
5556                && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5557    }
5558
5559    @Override
5560    public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5561            throws RemoteException {
5562        final int callingUid = getCallingUid();
5563
5564        if (callingUid != Process.SYSTEM_UID) {
5565            throw new SecurityException();
5566        }
5567
5568        if (value) {
5569            grantAppPermission(account, authTokenType, uid);
5570        } else {
5571            revokeAppPermission(account, authTokenType, uid);
5572        }
5573    }
5574
5575    /**
5576     * Allow callers with the given uid permission to get credentials for account/authTokenType.
5577     * <p>
5578     * Although this is public it can only be accessed via the AccountManagerService object
5579     * which is in the system. This means we don't need to protect it with permissions.
5580     * @hide
5581     */
5582    private void grantAppPermission(Account account, String authTokenType, int uid) {
5583        if (account == null || authTokenType == null) {
5584            Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
5585            return;
5586        }
5587        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5588        synchronized (accounts.cacheLock) {
5589            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
5590            db.beginTransaction();
5591            try {
5592                long accountId = getAccountIdLocked(db, account);
5593                if (accountId >= 0) {
5594                    ContentValues values = new ContentValues();
5595                    values.put(GRANTS_ACCOUNTS_ID, accountId);
5596                    values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
5597                    values.put(GRANTS_GRANTEE_UID, uid);
5598                    db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
5599                    db.setTransactionSuccessful();
5600                }
5601            } finally {
5602                db.endTransaction();
5603            }
5604            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5605                    UserHandle.of(accounts.userId));
5606
5607            cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
5608        }
5609    }
5610
5611    /**
5612     * Don't allow callers with the given uid permission to get credentials for
5613     * account/authTokenType.
5614     * <p>
5615     * Although this is public it can only be accessed via the AccountManagerService object
5616     * which is in the system. This means we don't need to protect it with permissions.
5617     * @hide
5618     */
5619    private void revokeAppPermission(Account account, String authTokenType, int uid) {
5620        if (account == null || authTokenType == null) {
5621            Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
5622            return;
5623        }
5624        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5625        synchronized (accounts.cacheLock) {
5626            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
5627            db.beginTransaction();
5628            try {
5629                long accountId = getAccountIdLocked(db, account);
5630                if (accountId >= 0) {
5631                    db.delete(TABLE_GRANTS,
5632                            GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
5633                                    + GRANTS_GRANTEE_UID + "=?",
5634                            new String[]{String.valueOf(accountId), authTokenType,
5635                                    String.valueOf(uid)});
5636                    db.setTransactionSuccessful();
5637                }
5638            } finally {
5639                db.endTransaction();
5640            }
5641            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5642                    new UserHandle(accounts.userId));
5643        }
5644    }
5645
5646    static final private String stringArrayToString(String[] value) {
5647        return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
5648    }
5649
5650    private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5651        final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
5652        if (oldAccountsForType != null) {
5653            ArrayList<Account> newAccountsList = new ArrayList<Account>();
5654            for (Account curAccount : oldAccountsForType) {
5655                if (!curAccount.equals(account)) {
5656                    newAccountsList.add(curAccount);
5657                }
5658            }
5659            if (newAccountsList.isEmpty()) {
5660                accounts.accountCache.remove(account.type);
5661            } else {
5662                Account[] newAccountsForType = new Account[newAccountsList.size()];
5663                newAccountsForType = newAccountsList.toArray(newAccountsForType);
5664                accounts.accountCache.put(account.type, newAccountsForType);
5665            }
5666        }
5667        accounts.userDataCache.remove(account);
5668        accounts.authTokenCache.remove(account);
5669        accounts.previousNameCache.remove(account);
5670    }
5671
5672    /**
5673     * This assumes that the caller has already checked that the account is not already present.
5674     */
5675    private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
5676        Account[] accountsForType = accounts.accountCache.get(account.type);
5677        int oldLength = (accountsForType != null) ? accountsForType.length : 0;
5678        Account[] newAccountsForType = new Account[oldLength + 1];
5679        if (accountsForType != null) {
5680            System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
5681        }
5682        newAccountsForType[oldLength] = account;
5683        accounts.accountCache.put(account.type, newAccountsForType);
5684    }
5685
5686    private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
5687            int callingUid, String callingPackage) {
5688        if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
5689                || callingUid == Process.myUid()) {
5690            return unfiltered;
5691        }
5692        UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
5693        if (user != null && user.isRestricted()) {
5694            String[] packages = mPackageManager.getPackagesForUid(callingUid);
5695            // If any of the packages is a white listed package, return the full set,
5696            // otherwise return non-shared accounts only.
5697            // This might be a temporary way to specify a whitelist
5698            String whiteList = mContext.getResources().getString(
5699                    com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
5700            for (String packageName : packages) {
5701                if (whiteList.contains(";" + packageName + ";")) {
5702                    return unfiltered;
5703                }
5704            }
5705            ArrayList<Account> allowed = new ArrayList<Account>();
5706            Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
5707            if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
5708            String requiredAccountType = "";
5709            try {
5710                // If there's an explicit callingPackage specified, check if that package
5711                // opted in to see restricted accounts.
5712                if (callingPackage != null) {
5713                    PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
5714                    if (pi != null && pi.restrictedAccountType != null) {
5715                        requiredAccountType = pi.restrictedAccountType;
5716                    }
5717                } else {
5718                    // Otherwise check if the callingUid has a package that has opted in
5719                    for (String packageName : packages) {
5720                        PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
5721                        if (pi != null && pi.restrictedAccountType != null) {
5722                            requiredAccountType = pi.restrictedAccountType;
5723                            break;
5724                        }
5725                    }
5726                }
5727            } catch (NameNotFoundException nnfe) {
5728            }
5729            for (Account account : unfiltered) {
5730                if (account.type.equals(requiredAccountType)) {
5731                    allowed.add(account);
5732                } else {
5733                    boolean found = false;
5734                    for (Account shared : sharedAccounts) {
5735                        if (shared.equals(account)) {
5736                            found = true;
5737                            break;
5738                        }
5739                    }
5740                    if (!found) {
5741                        allowed.add(account);
5742                    }
5743                }
5744            }
5745            Account[] filtered = new Account[allowed.size()];
5746            allowed.toArray(filtered);
5747            return filtered;
5748        } else {
5749            return unfiltered;
5750        }
5751    }
5752
5753    /*
5754     * packageName can be null. If not null, it should be used to filter out restricted accounts
5755     * that the package is not allowed to access.
5756     */
5757    protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
5758            int callingUid, String callingPackage) {
5759        if (accountType != null) {
5760            final Account[] accounts = userAccounts.accountCache.get(accountType);
5761            if (accounts == null) {
5762                return EMPTY_ACCOUNT_ARRAY;
5763            } else {
5764                return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
5765                        callingUid, callingPackage);
5766            }
5767        } else {
5768            int totalLength = 0;
5769            for (Account[] accounts : userAccounts.accountCache.values()) {
5770                totalLength += accounts.length;
5771            }
5772            if (totalLength == 0) {
5773                return EMPTY_ACCOUNT_ARRAY;
5774            }
5775            Account[] accounts = new Account[totalLength];
5776            totalLength = 0;
5777            for (Account[] accountsOfType : userAccounts.accountCache.values()) {
5778                System.arraycopy(accountsOfType, 0, accounts, totalLength,
5779                        accountsOfType.length);
5780                totalLength += accountsOfType.length;
5781            }
5782            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
5783        }
5784    }
5785
5786    protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5787            Account account, String key, String value) {
5788        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
5789        if (userDataForAccount == null) {
5790            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
5791            accounts.userDataCache.put(account, userDataForAccount);
5792        }
5793        if (value == null) {
5794            userDataForAccount.remove(key);
5795        } else {
5796            userDataForAccount.put(key, value);
5797        }
5798    }
5799
5800    protected String readCachedTokenInternal(
5801            UserAccounts accounts,
5802            Account account,
5803            String tokenType,
5804            String callingPackage,
5805            byte[] pkgSigDigest) {
5806        synchronized (accounts.cacheLock) {
5807            return accounts.accountTokenCaches.get(
5808                    account, tokenType, callingPackage, pkgSigDigest);
5809        }
5810    }
5811
5812    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5813            Account account, String key, String value) {
5814        HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
5815        if (authTokensForAccount == null) {
5816            authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
5817            accounts.authTokenCache.put(account, authTokensForAccount);
5818        }
5819        if (value == null) {
5820            authTokensForAccount.remove(key);
5821        } else {
5822            authTokensForAccount.put(key, value);
5823        }
5824    }
5825
5826    protected String readAuthTokenInternal(UserAccounts accounts, Account account,
5827            String authTokenType) {
5828        synchronized (accounts.cacheLock) {
5829            HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
5830            if (authTokensForAccount == null) {
5831                // need to populate the cache for this account
5832                final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
5833                authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
5834                accounts.authTokenCache.put(account, authTokensForAccount);
5835            }
5836            return authTokensForAccount.get(authTokenType);
5837        }
5838    }
5839
5840    protected String readUserDataInternalLocked(
5841            UserAccounts accounts, Account account, String key) {
5842        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
5843        if (userDataForAccount == null) {
5844            // need to populate the cache for this account
5845            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
5846            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
5847            accounts.userDataCache.put(account, userDataForAccount);
5848        }
5849        return userDataForAccount.get(key);
5850    }
5851
5852    protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
5853            final SQLiteDatabase db, Account account) {
5854        HashMap<String, String> userDataForAccount = new HashMap<>();
5855        Cursor cursor = db.query(CE_TABLE_EXTRAS,
5856                COLUMNS_EXTRAS_KEY_AND_VALUE,
5857                SELECTION_USERDATA_BY_ACCOUNT,
5858                new String[]{account.name, account.type},
5859                null, null, null);
5860        try {
5861            while (cursor.moveToNext()) {
5862                final String tmpkey = cursor.getString(0);
5863                final String value = cursor.getString(1);
5864                userDataForAccount.put(tmpkey, value);
5865            }
5866        } finally {
5867            cursor.close();
5868        }
5869        return userDataForAccount;
5870    }
5871
5872    protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
5873            final SQLiteDatabase db, Account account) {
5874        HashMap<String, String> authTokensForAccount = new HashMap<>();
5875        Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
5876                COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
5877                SELECTION_AUTHTOKENS_BY_ACCOUNT,
5878                new String[]{account.name, account.type},
5879                null, null, null);
5880        try {
5881            while (cursor.moveToNext()) {
5882                final String type = cursor.getString(0);
5883                final String authToken = cursor.getString(1);
5884                authTokensForAccount.put(type, authToken);
5885            }
5886        } finally {
5887            cursor.close();
5888        }
5889        return authTokensForAccount;
5890    }
5891
5892    private Context getContextForUser(UserHandle user) {
5893        try {
5894            return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
5895        } catch (NameNotFoundException e) {
5896            // Default to mContext, not finding the package system is running as is unlikely.
5897            return mContext;
5898        }
5899    }
5900
5901    private void sendResponse(IAccountManagerResponse response, Bundle result) {
5902        try {
5903            response.onResult(result);
5904        } catch (RemoteException e) {
5905            // if the caller is dead then there is no one to care about remote
5906            // exceptions
5907            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5908                Log.v(TAG, "failure while notifying response", e);
5909            }
5910        }
5911    }
5912
5913    private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
5914            String errorMessage) {
5915        try {
5916            response.onError(errorCode, errorMessage);
5917        } catch (RemoteException e) {
5918            // if the caller is dead then there is no one to care about remote
5919            // exceptions
5920            if (Log.isLoggable(TAG, Log.VERBOSE)) {
5921                Log.v(TAG, "failure while notifying response", e);
5922            }
5923        }
5924    }
5925
5926    private final class AccountManagerInternalImpl extends AccountManagerInternal {
5927        @Override
5928        public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
5929                @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
5930            if (account == null) {
5931                Slog.w(TAG, "account cannot be null");
5932                return;
5933            }
5934            if (packageName == null) {
5935                Slog.w(TAG, "packageName cannot be null");
5936                return;
5937            }
5938            if (userId < UserHandle.USER_SYSTEM) {
5939                Slog.w(TAG, "user id must be concrete");
5940                return;
5941            }
5942            if (callback == null) {
5943                Slog.w(TAG, "callback cannot be null");
5944                return;
5945            }
5946
5947            if (hasAccountAccess(account, packageName, new UserHandle(userId))) {
5948                Bundle result = new Bundle();
5949                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
5950                callback.sendResult(result);
5951                return;
5952            }
5953
5954            final int uid;
5955            try {
5956                uid = mPackageManager.getPackageUidAsUser(packageName, userId);
5957            } catch (NameNotFoundException e) {
5958                Slog.e(TAG, "Unknown package " + packageName);
5959                return;
5960            }
5961
5962            Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
5963            doNotification(mUsers.get(userId), account, null, intent, packageName, userId);
5964        }
5965    }
5966}
5967