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