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