OSDN Git Service

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