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