AccountManagerService.java revision 255dd04271088590fedc46c8e22b2fd4ab142d39
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * you may not use this file except in compliance with the License. 669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * You may obtain a copy of the License at 769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 1169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * distributed under the License is distributed on an "AS IS" BASIS, 1269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * See the License for the specific language governing permissions and 1469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage com.android.server.accounts; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.Manifest; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.Account; 210795272aa226f4e965968a03daddc53ce30b7cdaMathias Agopianimport android.accounts.AccountAndUser; 220795272aa226f4e965968a03daddc53ce30b7cdaMathias Agopianimport android.accounts.AccountAuthenticatorResponse; 230795272aa226f4e965968a03daddc53ce30b7cdaMathias Agopianimport android.accounts.AccountManager; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.AuthenticatorDescription; 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.CantAddAccountActivity; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.GrantCredentialsPermissionActivity; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.IAccountAuthenticator; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.IAccountAuthenticatorResponse; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.IAccountManager; 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.accounts.IAccountManagerResponse; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.ActivityManager; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.ActivityManagerNative; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.AppGlobals; 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.Notification; 35242d65bf9faf1d2bc3468490e510551140e23462San Mehatimport android.app.NotificationManager; 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.PendingIntent; 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.admin.DevicePolicyManager; 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.BroadcastReceiver; 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ComponentName; 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ContentValues; 416af763bec7c3f4d50fee8dd0046409bb8a7fe8f6Glenn Kastenimport android.content.Context; 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Intent; 43957e58670baad8c5995f1368e3b5280f0dbd891fSan Mehatimport android.content.IntentFilter; 44160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport android.content.ServiceConnection; 45a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehatimport android.content.pm.ApplicationInfo; 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.pm.PackageInfo; 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.pm.PackageManager; 48160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport android.content.pm.PackageManager.NameNotFoundException; 49160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport android.content.pm.RegisteredServicesCache; 50160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport android.content.pm.RegisteredServicesCacheListener; 51160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport android.content.pm.ResolveInfo; 52160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tateimport android.content.pm.UserInfo; 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.Cursor; 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.DatabaseUtils; 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase; 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper; 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Binder; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Bundle; 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Environment; 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler; 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.IBinder; 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Looper; 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message; 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcel; 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Process; 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.RemoteException; 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock; 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.UserHandle; 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.UserManager; 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextUtils; 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Pair; 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Slog; 74e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport android.util.SparseArray; 75e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat 76e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport com.android.internal.R; 77e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport com.android.internal.util.ArrayUtils; 78e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport com.android.internal.util.IndentingPrintWriter; 79e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport com.android.server.FgThread; 80e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat 81e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport com.google.android.collect.Lists; 82e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport com.google.android.collect.Sets; 83e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat 84e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.io.File; 85e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.io.FileDescriptor; 86e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.io.PrintWriter; 87e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.ArrayList; 88e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.Arrays; 89e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.Collection; 90e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.HashMap; 91e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.HashSet; 92e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.LinkedHashMap; 93e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.List; 94e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehatimport java.util.Map; 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.atomic.AtomicInteger; 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.atomic.AtomicReference; 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A system service that provides account, password, and authtoken management for all 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * accounts on the device. Some of these calls are implemented with the help of the corresponding 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link IAccountAuthenticator} services. This service is not accessed by users directly, 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * instead one uses an instance of {@link AccountManager}, which can be accessed as follows: 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * AccountManager accountManager = AccountManager.get(context); 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class AccountManagerService 107887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn extends IAccountManager.Stub 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project implements RegisteredServicesCacheListener<AuthenticatorDescription> { 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TAG = "AccountManagerService"; 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int TIMEOUT_DELAY_MS = 1000 * 60; 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String DATABASE_NAME = "accounts.db"; 11369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes private static final int DATABASE_VERSION = 6; 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final Context mContext; 11669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final PackageManager mPackageManager; 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private UserManager mUserManager; 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final MessageHandler mMessageHandler; 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Messages that can be sent on mHandler 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int MESSAGE_TIMED_OUT = 3; 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4; 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final IAccountAuthenticatorCache mAuthenticatorCache; 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TABLE_ACCOUNTS = "accounts"; 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String ACCOUNTS_ID = "_id"; 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String ACCOUNTS_NAME = "name"; 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String ACCOUNTS_TYPE = "type"; 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String ACCOUNTS_PASSWORD = "password"; 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name"; 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TABLE_AUTHTOKENS = "authtokens"; 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String AUTHTOKENS_ID = "_id"; 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id"; 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String AUTHTOKENS_TYPE = "type"; 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TABLE_GRANTS = "grants"; 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; 14469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String GRANTS_GRANTEE_UID = "uid"; 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 14769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes private static final String TABLE_EXTRAS = "extras"; 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String EXTRAS_ID = "_id"; 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String EXTRAS_ACCOUNTS_ID = "accounts_id"; 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String EXTRAS_KEY = "key"; 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String EXTRAS_VALUE = "value"; 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TABLE_META = "meta"; 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String META_KEY = "key"; 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String META_VALUE = "value"; 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts"; 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final Intent ACCOUNTS_CHANGED_INTENT; 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String COUNT_OF_MATCHING_GRANTS = "" 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + " AND " + GRANTS_GRANTEE_UID + "=?" 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?" 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + " AND " + ACCOUNTS_NAME + "=?" 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + " AND " + ACCOUNTS_TYPE + "=?"; 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = 172e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 173e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, 174887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn AUTHTOKENS_AUTHTOKEN}; 175887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn 176887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn private static final String SELECTION_USERDATA_BY_ACCOUNT = 177e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; 178e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; 179e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat 180e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); 18169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes private final AtomicInteger mNotificationIds = new AtomicInteger(1); 1823e458241d9930465a20a861ecb42744355d48e48San Mehat 1833e458241d9930465a20a861ecb42744355d48e48San Mehat static class UserAccounts { 1843e458241d9930465a20a861ecb42744355d48e48San Mehat private final int userId; 1853e458241d9930465a20a861ecb42744355d48e48San Mehat private final DatabaseHelper openHelper; 1863e458241d9930465a20a861ecb42744355d48e48San Mehat private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> 1873e458241d9930465a20a861ecb42744355d48e48San Mehat credentialsPermissionNotificationIds = 18869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); 1893e458241d9930465a20a861ecb42744355d48e48San Mehat private final HashMap<Account, Integer> signinRequiredNotificationIds = 1903e458241d9930465a20a861ecb42744355d48e48San Mehat new HashMap<Account, Integer>(); 1913e458241d9930465a20a861ecb42744355d48e48San Mehat private final Object cacheLock = new Object(); 1923e458241d9930465a20a861ecb42744355d48e48San Mehat /** protected by the {@link #cacheLock} */ 193a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat private final HashMap<String, Account[]> accountCache = 194a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat new LinkedHashMap<String, Account[]>(); 195a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat /** protected by the {@link #cacheLock} */ 196a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat private final HashMap<Account, HashMap<String, String>> userDataCache = 197a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat new HashMap<Account, HashMap<String, String>>(); 198a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat /** protected by the {@link #cacheLock} */ 199a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat private final HashMap<Account, HashMap<String, String>> authTokenCache = 200a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat new HashMap<Account, HashMap<String, String>>(); 201a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat /** 202a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat * protected by the {@link #cacheLock} 203a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat * 204a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat * Caches the previous names associated with an account. Previous names 205a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat * should be cached because we expect that when an Account is renamed, 20669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and 207a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat * want to know if the accounts they care about have been renamed. 2085baa3a62a97544669fba6d65a11c07f252e654ddSteve Block * 209a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat * The previous names are wrapped in an {@link AtomicReference} so that 2105baa3a62a97544669fba6d65a11c07f252e654ddSteve Block * we can distinguish between those accounts with no previous names and 211a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat * those whose previous names haven't been cached (yet). 212a5109a878eeff22e32ee5ce1b1cd15e8daad5234San Mehat */ 2133e458241d9930465a20a861ecb42744355d48e48San Mehat private final HashMap<Account, AtomicReference<String>> previousNameCache = 2143e458241d9930465a20a861ecb42744355d48e48San Mehat new HashMap<Account, AtomicReference<String>>(); 2151fd0ec738b0a2b97cc28701aa37b1a9869afc684San Mehat 2161fd0ec738b0a2b97cc28701aa37b1a9869afc684San Mehat UserAccounts(Context context, int userId) { 2171fd0ec738b0a2b97cc28701aa37b1a9869afc684San Mehat this.userId = userId; 2183e458241d9930465a20a861ecb42744355d48e48San Mehat synchronized (cacheLock) { 2193e458241d9930465a20a861ecb42744355d48e48San Mehat openHelper = new DatabaseHelper(context, userId); 2203e458241d9930465a20a861ecb42744355d48e48San Mehat } 2213e458241d9930465a20a861ecb42744355d48e48San Mehat } 2227e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat } 2237e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat 2247e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>(); 2253e458241d9930465a20a861ecb42744355d48e48San Mehat 2263e458241d9930465a20a861ecb42744355d48e48San Mehat private static AtomicReference<AccountManagerService> sThis = 2277e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat new AtomicReference<AccountManagerService>(); 2287e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; 2297e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat 2307e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat static { 2317e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION); 2327e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2331fd0ec738b0a2b97cc28701aa37b1a9869afc684San Mehat } 2347e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat 2357e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat 2367e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat /** 2377e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat * This should only be called by system code. One should only call this after the service 2387e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat * has started. 2397e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat * @return a reference to the AccountManagerService instance 2407e63789a0e0689d940609b1daceebc1bc08dcbefSan Mehat * @hide 24169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes */ 24284bb52ed614f3a13370c923a8bc2a8b7c6db8d30Dianne Hackborn public static AccountManagerService getSingleton() { 243242d65bf9faf1d2bc3468490e510551140e23462San Mehat return sThis.get(); 2440a42b811aea490a9a605b75f0320101f6eafd283San Mehat } 245242d65bf9faf1d2bc3468490e510551140e23462San Mehat 2463e458241d9930465a20a861ecb42744355d48e48San Mehat public AccountManagerService(Context context) { 2473e458241d9930465a20a861ecb42744355d48e48San Mehat this(context, context.getPackageManager(), new AccountAuthenticatorCache(context)); 2483e458241d9930465a20a861ecb42744355d48e48San Mehat } 2493e458241d9930465a20a861ecb42744355d48e48San Mehat 250160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate public AccountManagerService(Context context, PackageManager packageManager, 251160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate IAccountAuthenticatorCache authenticatorCache) { 252160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate mContext = context; 253160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate mPackageManager = packageManager; 25471f2cf116aab893e224056c38ab146bd1538dd3eSteve Block 255160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate mMessageHandler = new MessageHandler(FgThread.get().getLooper()); 256160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate 257160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate mAuthenticatorCache = authenticatorCache; 258160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate mAuthenticatorCache.setListener(this, null /* Handler */); 259160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate 260160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate sThis.set(this); 261160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate 262160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate IntentFilter intentFilter = new IntentFilter(); 263160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 264160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate intentFilter.addDataScheme("package"); 265160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate mContext.registerReceiver(new BroadcastReceiver() { 266160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate @Override 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onReceive(Context context1, Intent intent) { 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project purgeOldGrantsAll(); 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 270160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate }, intentFilter); 271160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate 272160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate IntentFilter userFilter = new IntentFilter(); 273160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate userFilter.addAction(Intent.ACTION_USER_REMOVED); 274160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate userFilter.addAction(Intent.ACTION_USER_STARTED); 275160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate mContext.registerReceiverAsUser(new BroadcastReceiver() { 276160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate @Override 277160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate public void onReceive(Context context, Intent intent) { 278160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate String action = intent.getAction(); 279160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate if (Intent.ACTION_USER_REMOVED.equals(action)) { 280160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate onUserRemoved(intent); 281160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate } else if (Intent.ACTION_USER_STARTED.equals(action)) { 282160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate onUserStarted(intent); 283160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate } 284160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate } 285887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn }, UserHandle.ALL, userFilter, null, null); 286887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn } 287887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn 288887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn @Override 289887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 290887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn throws RemoteException { 291887f355f99ff83d568ef2885a4fdcaae475583dfDianne Hackborn try { 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return super.onTransact(code, data, reply, flags); 29369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } catch (RuntimeException e) { 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // The account manager only throws security exceptions, so let's 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // log all others. 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!(e instanceof SecurityException)) { 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Slog.wtf(TAG, "Account Manager Crash", e); 2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw e; 3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void systemReady() { 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private UserManager getUserManager() { 3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mUserManager == null) { 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mUserManager = UserManager.get(mContext); 3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mUserManager; 3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* Caller should lock mUsers */ 3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private UserAccounts initUserLocked(int userId) { 3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts = mUsers.get(userId); 3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (accounts == null) { 3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts = new UserAccounts(mContext, userId); 3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mUsers.append(userId, accounts); 3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project purgeOldGrants(accounts); 3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); 32110e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown } 32210e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown return accounts; 32310e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown } 32410e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown 32510e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown private void purgeOldGrantsAll() { 32610e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown synchronized (mUsers) { 32710e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown for (int i = 0; i < mUsers.size(); i++) { 3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project purgeOldGrants(mUsers.valueAt(i)); 32910e89712863f5b91a2982dc1783fbdfe39c1485dJeff Brown } 3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void purgeOldGrants(UserAccounts accounts) { 3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized (accounts.cacheLock) { 3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final Cursor cursor = db.query(TABLE_GRANTS, 33769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes new String[]{GRANTS_GRANTEE_UID}, 3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project null, null, GRANTS_GRANTEE_UID, null, null); 3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 34069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes while (cursor.moveToNext()) { 3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int uid = cursor.getInt(0); 3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null; 3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (packageExists) { 3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project continue; 3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.d(TAG, "deleting grants for UID " + uid 3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + " because its package is no longer installed"); 3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", 3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new String[]{Integer.toString(uid)}); 3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cursor.close(); 3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Validate internal set of accounts against installed authenticators for 3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * given user. Clears cached authenticators before validating. 3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void validateAccounts(int userId) { 3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final UserAccounts accounts = getUserAccounts(userId); 3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Invalidate user-specific cache to make sure we catch any 3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // removed authenticators. 3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); 3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3690bca96bcbfe559f9330a01f723c5c9cba51ec05aMarco Nelissen /** 3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Validate internal set of accounts against installed authenticators for 3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * given user. Clear cached authenticators before validating when requested. 37269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes */ 3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void validateAccountsInternal( 3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts, boolean invalidateAuthenticatorCache) { 3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (invalidateAuthenticatorCache) { 3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAuthenticatorCache.invalidateCache(accounts.userId); 37769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet(); 3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : 38169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes mAuthenticatorCache.getAllServices(accounts.userId)) { 3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project knownAuth.add(service.type); 3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized (accounts.cacheLock) { 3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean accountDeleted = false; 3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Cursor cursor = db.query(TABLE_ACCOUNTS, 3890bca96bcbfe559f9330a01f723c5c9cba51ec05aMarco Nelissen new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, 39069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes null, null, null, null, null); 3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts.accountCache.clear(); 39369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes final HashMap<String, ArrayList<String>> accountNamesByType = 3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new LinkedHashMap<String, ArrayList<String>>(); 3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (cursor.moveToNext()) { 3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final long accountId = cursor.getLong(0); 3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final String accountType = cursor.getString(1); 3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final String accountName = cursor.getString(2); 3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) { 4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Slog.w(TAG, "deleting account " + accountName + " because type " 4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + accountType + " no longer has a registered authenticator"); 4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); 4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accountDeleted = true; 4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final Account account = new Account(accountName, accountType); 4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts.userDataCache.remove(account); 4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts.authTokenCache.remove(account); 4080bca96bcbfe559f9330a01f723c5c9cba51ec05aMarco Nelissen } else { 4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ArrayList<String> accountNames = accountNamesByType.get(accountType); 4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (accountNames == null) { 4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accountNames = new ArrayList<String>(); 4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accountNamesByType.put(accountType, accountNames); 4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accountNames.add(accountName); 4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 41669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (Map.Entry<String, ArrayList<String>> cur 4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project : accountNamesByType.entrySet()) { 4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final String accountType = cur.getKey(); 4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final ArrayList<String> accountNames = cur.getValue(); 4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final Account[] accountsForType = new Account[accountNames.size()]; 4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int i = 0; 4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String accountName : accountNames) { 42469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes accountsForType[i] = new Account(accountName, accountType); 4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ++i; 42669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts.accountCache.put(accountType, accountsForType); 4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 42969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } finally { 4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cursor.close(); 4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (accountDeleted) { 4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sendAccountsChangedBroadcast(accounts.userId); 4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 43669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private UserAccounts getUserAccountsForCaller() { 4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return getUserAccounts(UserHandle.getCallingUserId()); 4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 44269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes protected UserAccounts getUserAccounts(int userId) { 4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized (mUsers) { 4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts = mUsers.get(userId); 44569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (accounts == null) { 4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts = initUserLocked(userId); 4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mUsers.append(userId, accounts); 4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return accounts; 4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 45269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes 4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void onUserRemoved(Intent intent) { 4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (userId < 1) return; 4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts; 45869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes synchronized (mUsers) { 4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts = mUsers.get(userId); 4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mUsers.remove(userId); 4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 46269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (accounts == null) { 4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File dbFile = new File(getDatabaseName(userId)); 4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dbFile.delete(); 4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 46769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes 4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized (accounts.cacheLock) { 4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts.openHelper.close(); 4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File dbFile = new File(getDatabaseName(userId)); 4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dbFile.delete(); 47269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 47469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes 4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void onUserStarted(Intent intent) { 4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (userId < 1) return; 4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Check if there's a shared account that needs to be created as an account 48069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes Account[] sharedAccounts = getSharedAccountsAsUser(userId); 4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sharedAccounts == null || sharedAccounts.length == 0) return; 4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Account[] accounts = getAccountsAsUser(null, userId); 4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (Account sa : sharedAccounts) { 4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ArrayUtils.contains(accounts, sa)) continue; 4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Account doesn't exist. Copy it now. 48669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes copyAccountToUser(sa, UserHandle.USER_OWNER, userId); 4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 48869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) { 4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */); 4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getPassword(Account account) { 497add868cebaf62cffe96e79764ea0b7f2320a03ebAmith Yamasani if (Log.isLoggable(TAG, Log.VERBOSE)) { 4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(TAG, "getPassword: " + account 4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", caller's uid " + Binder.getCallingUid() 5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", pid " + Binder.getCallingPid()); 5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (account == null) throw new IllegalArgumentException("account is null"); 5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project checkAuthenticateAccountsPermission(account); 5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts = getUserAccountsForCaller(); 5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long identityToken = clearCallingIdentity(); 5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return readPasswordInternal(accounts, account); 5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project restoreCallingIdentity(identityToken); 5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private String readPasswordInternal(UserAccounts accounts, Account account) { 5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (account == null) { 5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized (accounts.cacheLock) { 5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 52169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, 5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new String[]{account.name, account.type}, null, null, null); 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cursor.moveToNext()) { 52669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return cursor.getString(0); 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cursor.close(); 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 53569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes @Override 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getPreviousName(Account account) { 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Log.isLoggable(TAG, Log.VERBOSE)) { 53869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes Log.v(TAG, "getPreviousName: " + account 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", caller's uid " + Binder.getCallingUid() 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", pid " + Binder.getCallingPid()); 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (account == null) throw new IllegalArgumentException("account is null"); 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts = getUserAccountsForCaller(); 54469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes long identityToken = clearCallingIdentity(); 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 54669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return readPreviousNameInternal(accounts, account); 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 54869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes restoreCallingIdentity(identityToken); 5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 55269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes private String readPreviousNameInternal(UserAccounts accounts, Account account) { 5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (account == null) { 5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized (accounts.cacheLock) { 5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account); 5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (previousNameRef == null) { 55969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Cursor cursor = db.query( 56169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes TABLE_ACCOUNTS, 5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new String[]{ ACCOUNTS_PREVIOUS_NAME }, 5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new String[] { account.name, account.type }, 5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project null, 5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project null, 5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project null); 5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cursor.moveToNext()) { 57069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes String previousName = cursor.getString(0); 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project previousNameRef = new AtomicReference<String>(previousName); 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project accounts.previousNameCache.put(account, previousNameRef); 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return previousName; 5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cursor.close(); 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return previousNameRef.get(); 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getUserData(Account account, String key) { 5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Log.isLoggable(TAG, Log.VERBOSE)) { 5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(TAG, "getUserData: " + account 5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", key " + key 59169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes + ", caller's uid " + Binder.getCallingUid() 5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", pid " + Binder.getCallingPid()); 5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (account == null) throw new IllegalArgumentException("account is null"); 59569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (key == null) throw new IllegalArgumentException("key is null"); 5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project checkAuthenticateAccountsPermission(account); 59769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes UserAccounts accounts = getUserAccountsForCaller(); 5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long identityToken = clearCallingIdentity(); 5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return readUserDataInternal(accounts, account, key); 60169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } finally { 6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project restoreCallingIdentity(identityToken); 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 60669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes @Override 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public AuthenticatorDescription[] getAuthenticatorTypes(int userId) { 6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Log.isLoggable(TAG, Log.VERBOSE)) { 6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(TAG, "getAuthenticatorTypes: " 61069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes + "for user id " + userId 6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + "caller's uid " + Binder.getCallingUid() 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", pid " + Binder.getCallingPid()); 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Only allow the system process to read accounts of other users 6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() 6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + " trying get authenticator types for " + userId); 6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final long identityToken = clearCallingIdentity(); 6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> 6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project authenticatorCollection = mAuthenticatorCache.getAllServices(userId); 6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AuthenticatorDescription[] types = 6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new AuthenticatorDescription[authenticatorCollection.size()]; 6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int i = 0; 6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator 625c64edde69d18498fb2954f71a546357b07ab996aEvan Millar : authenticatorCollection) { 62669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes types[i] = authenticator.type; 627c64edde69d18498fb2954f71a546357b07ab996aEvan Millar i++; 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 62969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return types; 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project restoreCallingIdentity(identityToken); 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 63469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void enforceCrossUserPermission(int userId, String errorMessage) { 6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (userId != UserHandle.getCallingUserId() 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project && Binder.getCallingUid() != Process.myUid() 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project && mContext.checkCallingOrSelfPermission( 6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project != PackageManager.PERMISSION_GRANTED) { 6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new SecurityException(errorMessage); 6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean addAccountExplicitly(Account account, String password, Bundle extras) { 6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Log.isLoggable(TAG, Log.VERBOSE)) { 6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(TAG, "addAccountExplicitly: " + account 6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", caller's uid " + Binder.getCallingUid() 6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", pid " + Binder.getCallingPid()); 6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (account == null) throw new IllegalArgumentException("account is null"); 6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project checkAuthenticateAccountsPermission(account); 6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* 655c64edde69d18498fb2954f71a546357b07ab996aEvan Millar * Child users are not allowed to add accounts. Only the accounts that are 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * shared by the parent profile can be added to child profile. 65769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * 6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * TODO: Only allow accounts that were shared to be added by 65969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes * a limited user. 6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts = getUserAccountsForCaller(); 6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // fails if the account already exists 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long identityToken = clearCallingIdentity(); 6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return addAccountInternal(accounts, account, password, extras, false); 667c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } finally { 6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project restoreCallingIdentity(identityToken); 6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 67169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes 6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean copyAccountToUser(final Account account, int userFrom, int userTo) { 6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final UserAccounts fromAccounts = getUserAccounts(userFrom); 674c64edde69d18498fb2954f71a546357b07ab996aEvan Millar final UserAccounts toAccounts = getUserAccounts(userTo); 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (fromAccounts == null || toAccounts == null) { 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long identityToken = clearCallingIdentity(); 680c64edde69d18498fb2954f71a546357b07ab996aEvan Millar try { 6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new Session(fromAccounts, null, account.type, false, 6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project false /* stripAuthTokenFromResult */) { 6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected String toDebugString(long now) { 6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return super.toDebugString(now) + ", getAccountCredentialsForClone" 68669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes + ", " + account.type; 687c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 690c64edde69d18498fb2954f71a546357b07ab996aEvan Millar public void run() throws RemoteException { 6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAuthenticator.getAccountCredentialsForCloning(this, account); 6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 69569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes public void onResult(Bundle result) { 6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (result != null) { 69769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Create a Session for the target user and pass in the bundle 6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project completeCloningAccount(result, account, toAccounts); 7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.onResult(result); 7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }.bind(); 7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project restoreCallingIdentity(identityToken); 7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void completeCloningAccount(final Bundle result, final Account account, 7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final UserAccounts targetUser) { 7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long id = clearCallingIdentity(); 7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 71769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes new Session(targetUser, null, account.type, false, 7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project false /* stripAuthTokenFromResult */) { 7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected String toDebugString(long now) { 7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return super.toDebugString(now) + ", getAccountCredentialsForClone" 7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", " + account.type; 7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 72569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes @Override 7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void run() throws RemoteException { 7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Confirm that the owner's account still exists before this step. 7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); 729c64edde69d18498fb2954f71a546357b07ab996aEvan Millar synchronized (owner.cacheLock) { 73069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes Account[] ownerAccounts = getAccounts(UserHandle.USER_OWNER); 731c64edde69d18498fb2954f71a546357b07ab996aEvan Millar for (Account acc : ownerAccounts) { 732c64edde69d18498fb2954f71a546357b07ab996aEvan Millar if (acc.equals(account)) { 733c64edde69d18498fb2954f71a546357b07ab996aEvan Millar mAuthenticator.addAccountFromCredentials(this, account, result); 734c64edde69d18498fb2954f71a546357b07ab996aEvan Millar break; 73569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 73669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 737c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 73869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 739c64edde69d18498fb2954f71a546357b07ab996aEvan Millar 74069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes @Override 741c64edde69d18498fb2954f71a546357b07ab996aEvan Millar public void onResult(Bundle result) { 742c64edde69d18498fb2954f71a546357b07ab996aEvan Millar if (result != null) { 743c64edde69d18498fb2954f71a546357b07ab996aEvan Millar if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 744c64edde69d18498fb2954f71a546357b07ab996aEvan Millar // TODO: Anything? 745c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } else { 746c64edde69d18498fb2954f71a546357b07ab996aEvan Millar // TODO: Show error notification 747c64edde69d18498fb2954f71a546357b07ab996aEvan Millar // TODO: Should we remove the shadow account to avoid retries? 748c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 74969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return; 750c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } else { 751c64edde69d18498fb2954f71a546357b07ab996aEvan Millar super.onResult(result); 752c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 753c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 754c64edde69d18498fb2954f71a546357b07ab996aEvan Millar 755c64edde69d18498fb2954f71a546357b07ab996aEvan Millar @Override 756c64edde69d18498fb2954f71a546357b07ab996aEvan Millar public void onError(int errorCode, String errorMessage) { 757c64edde69d18498fb2954f71a546357b07ab996aEvan Millar super.onError(errorCode, errorMessage); 758c64edde69d18498fb2954f71a546357b07ab996aEvan Millar // TODO: Show error notification to user 759c64edde69d18498fb2954f71a546357b07ab996aEvan Millar // TODO: Should we remove the shadow account so that it doesn't keep trying? 76069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 761c64edde69d18498fb2954f71a546357b07ab996aEvan Millar 762c64edde69d18498fb2954f71a546357b07ab996aEvan Millar }.bind(); 763c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } finally { 764c64edde69d18498fb2954f71a546357b07ab996aEvan Millar restoreCallingIdentity(id); 76569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 766c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 767c64edde69d18498fb2954f71a546357b07ab996aEvan Millar 768c64edde69d18498fb2954f71a546357b07ab996aEvan Millar private boolean addAccountInternal(UserAccounts accounts, Account account, String password, 76969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes Bundle extras, boolean restricted) { 770c64edde69d18498fb2954f71a546357b07ab996aEvan Millar if (account == null) { 771c64edde69d18498fb2954f71a546357b07ab996aEvan Millar return false; 772c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 773c64edde69d18498fb2954f71a546357b07ab996aEvan Millar synchronized (accounts.cacheLock) { 774c64edde69d18498fb2954f71a546357b07ab996aEvan Millar final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 77569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes db.beginTransaction(); 77669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes try { 777c64edde69d18498fb2954f71a546357b07ab996aEvan Millar long numMatches = DatabaseUtils.longForQuery(db, 77869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes "select count(*) from " + TABLE_ACCOUNTS 779c64edde69d18498fb2954f71a546357b07ab996aEvan Millar + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 780c64edde69d18498fb2954f71a546357b07ab996aEvan Millar new String[]{account.name, account.type}); 7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (numMatches > 0) { 7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(TAG, "insertAccountIntoDatabase: " + account 7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", skipping since the account already exists"); 7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 78569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ContentValues values = new ContentValues(); 7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(ACCOUNTS_NAME, account.name); 7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(ACCOUNTS_TYPE, account.type); 7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(ACCOUNTS_PASSWORD, password); 7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); 7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (accountId < 0) { 7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(TAG, "insertAccountIntoDatabase: " + account 7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", skipping the DB insert failed"); 7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (extras != null) { 7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String key : extras.keySet()) { 7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final String value = extras.getString(key); 7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (insertExtraLocked(db, accountId, key, value) < 0) { 800906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn Log.w(TAG, "insertAccountIntoDatabase: " + account 801906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn + ", skipping since insertExtra failed for key " + key); 802906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn return false; 803906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn } 804906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn } 805906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn } 806906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn db.setTransactionSuccessful(); 8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project insertAccountIntoCacheLocked(accounts, account); 8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project db.endTransaction(); 8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sendAccountsChangedBroadcast(accounts.userId); 81269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (accounts.userId == UserHandle.USER_OWNER) { 8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addAccountToLimitedUsers(account); 81569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 81669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return true; 8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Adds the account to all limited users as shared accounts. If the user is currently 8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * running, then clone the account too. 8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param account the account to share with limited users 8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void addAccountToLimitedUsers(Account account) { 8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project List<UserInfo> users = getUserManager().getUsers(); 8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (UserInfo user : users) { 8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (user.isRestricted()) { 8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project addSharedAccountAsUser(account, user.id); 8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) { 8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mMessageHandler.sendMessage(mMessageHandler.obtainMessage( 8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id, 8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project account)); 8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (RemoteException re) { 8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Shouldn't happen 8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) { 8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ContentValues values = new ContentValues(); 8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(EXTRAS_KEY, key); 8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(EXTRAS_ACCOUNTS_ID, accountId); 8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(EXTRAS_VALUE, value); 8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values); 8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void hasFeatures(IAccountManagerResponse response, 8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Account account, String[] features) { 8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Log.isLoggable(TAG, Log.VERBOSE)) { 8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(TAG, "hasFeatures: " + account 855160edb3645f8b7012bab70ae6e6e8c4a5733082bChristopher Tate + ", response " + response 8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", features " + stringArrayToString(features) 8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + ", caller's uid " + Binder.getCallingUid() 858e9d376b801b7890b1ef5006ed55de4208e64bb63San Mehat + ", pid " + Binder.getCallingPid()); 8593e458241d9930465a20a861ecb42744355d48e48San Mehat } 8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (response == null) throw new IllegalArgumentException("response is null"); 8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (account == null) throw new IllegalArgumentException("account is null"); 8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (features == null) throw new IllegalArgumentException("features is null"); 8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project checkReadAccountsPermission(); 8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UserAccounts accounts = getUserAccountsForCaller(); 865906497c574d45d8dfd295b16dece0d0bc32c0895Dianne Hackborn long identityToken = clearCallingIdentity(); 8660bca96bcbfe559f9330a01f723c5c9cba51ec05aMarco Nelissen try { 8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new TestFeaturesSession(accounts, response, account, features).bind(); 8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project restoreCallingIdentity(identityToken); 870c64edde69d18498fb2954f71a546357b07ab996aEvan Millar } 8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private class TestFeaturesSession extends Session { 8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final String[] mFeatures; 8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final Account mAccount; 8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Account account, String[] features) { 8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(accounts, response, account.type, false /* expectActivityLaunch */, 8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project true /* stripAuthTokenFromResult */); 8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFeatures = features; 8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mAccount = account; 8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 884 885 @Override 886 public void run() throws RemoteException { 887 try { 888 mAuthenticator.hasFeatures(this, mAccount, mFeatures); 889 } catch (RemoteException e) { 890 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 891 } 892 } 893 894 @Override 895 public void onResult(Bundle result) { 896 IAccountManagerResponse response = getResponseAndClose(); 897 if (response != null) { 898 try { 899 if (result == null) { 900 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 901 return; 902 } 903 if (Log.isLoggable(TAG, Log.VERBOSE)) { 904 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 905 + response); 906 } 907 final Bundle newResult = new Bundle(); 908 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, 909 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); 910 response.onResult(newResult); 911 } catch (RemoteException e) { 912 // if the caller is dead then there is no one to care about remote exceptions 913 if (Log.isLoggable(TAG, Log.VERBOSE)) { 914 Log.v(TAG, "failure while notifying response", e); 915 } 916 } 917 } 918 } 919 920 @Override 921 protected String toDebugString(long now) { 922 return super.toDebugString(now) + ", hasFeatures" 923 + ", " + mAccount 924 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 925 } 926 } 927 928 @Override 929 public void renameAccount( 930 IAccountManagerResponse response, Account accountToRename, String newName) { 931 if (Log.isLoggable(TAG, Log.VERBOSE)) { 932 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName 933 + ", caller's uid " + Binder.getCallingUid() 934 + ", pid " + Binder.getCallingPid()); 935 } 936 if (accountToRename == null) throw new IllegalArgumentException("account is null"); 937 checkAuthenticateAccountsPermission(accountToRename); 938 UserAccounts accounts = getUserAccountsForCaller(); 939 long identityToken = clearCallingIdentity(); 940 try { 941 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName); 942 Bundle result = new Bundle(); 943 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name); 944 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type); 945 try { 946 response.onResult(result); 947 } catch (RemoteException e) { 948 Log.w(TAG, e.getMessage()); 949 } 950 } finally { 951 restoreCallingIdentity(identityToken); 952 } 953 } 954 955 private Account renameAccountInternal( 956 UserAccounts accounts, Account accountToRename, String newName) { 957 Account resultAccount = null; 958 /* 959 * Cancel existing notifications. Let authenticators 960 * re-post notifications as required. But we don't know if 961 * the authenticators have bound their notifications to 962 * now stale account name data. 963 * 964 * With a rename api, we might not need to do this anymore but it 965 * shouldn't hurt. 966 */ 967 cancelNotification( 968 getSigninRequiredNotificationId(accounts, accountToRename), 969 new UserHandle(accounts.userId)); 970 synchronized(accounts.credentialsPermissionNotificationIds) { 971 for (Pair<Pair<Account, String>, Integer> pair: 972 accounts.credentialsPermissionNotificationIds.keySet()) { 973 if (accountToRename.equals(pair.first.first)) { 974 int id = accounts.credentialsPermissionNotificationIds.get(pair); 975 cancelNotification(id, new UserHandle(accounts.userId)); 976 } 977 } 978 } 979 synchronized (accounts.cacheLock) { 980 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 981 db.beginTransaction(); 982 boolean isSuccessful = false; 983 Account renamedAccount = new Account(newName, accountToRename.type); 984 try { 985 final ContentValues values = new ContentValues(); 986 values.put(ACCOUNTS_NAME, newName); 987 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name); 988 final long accountId = getAccountIdLocked(db, accountToRename); 989 if (accountId >= 0) { 990 final String[] argsAccountId = { String.valueOf(accountId) }; 991 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 992 db.setTransactionSuccessful(); 993 isSuccessful = true; 994 } 995 } finally { 996 db.endTransaction(); 997 if (isSuccessful) { 998 /* 999 * Database transaction was successful. Clean up cached 1000 * data associated with the account in the user profile. 1001 */ 1002 insertAccountIntoCacheLocked(accounts, renamedAccount); 1003 /* 1004 * Extract the data and token caches before removing the 1005 * old account to preserve the user data associated with 1006 * the account. 1007 */ 1008 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename); 1009 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename); 1010 removeAccountFromCacheLocked(accounts, accountToRename); 1011 /* 1012 * Update the cached data associated with the renamed 1013 * account. 1014 */ 1015 accounts.userDataCache.put(renamedAccount, tmpData); 1016 accounts.authTokenCache.put(renamedAccount, tmpTokens); 1017 accounts.previousNameCache.put( 1018 renamedAccount, 1019 new AtomicReference<String>(accountToRename.name)); 1020 resultAccount = renamedAccount; 1021 1022 if (accounts.userId == UserHandle.USER_OWNER) { 1023 /* 1024 * Owner's account was renamed, rename the account for 1025 * those users with which the account was shared. 1026 */ 1027 List<UserInfo> users = mUserManager.getUsers(true); 1028 for (UserInfo user : users) { 1029 if (!user.isPrimary() && user.isRestricted()) { 1030 renameSharedAccountAsUser(accountToRename, newName, user.id); 1031 } 1032 } 1033 } 1034 sendAccountsChangedBroadcast(accounts.userId); 1035 } 1036 } 1037 } 1038 return resultAccount; 1039 } 1040 1041 @Override 1042 public void removeAccount(IAccountManagerResponse response, Account account) { 1043 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1044 Log.v(TAG, "removeAccount: " + account 1045 + ", response " + response 1046 + ", caller's uid " + Binder.getCallingUid() 1047 + ", pid " + Binder.getCallingPid()); 1048 } 1049 if (response == null) throw new IllegalArgumentException("response is null"); 1050 if (account == null) throw new IllegalArgumentException("account is null"); 1051 checkManageAccountsPermission(); 1052 UserHandle user = Binder.getCallingUserHandle(); 1053 UserAccounts accounts = getUserAccountsForCaller(); 1054 int userId = Binder.getCallingUserHandle().getIdentifier(); 1055 if (!canUserModifyAccounts(userId)) { 1056 try { 1057 // TODO: This should be ERROR_CODE_USER_RESTRICTED instead. See http://b/16322768 1058 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, 1059 "User cannot modify accounts"); 1060 } catch (RemoteException re) { 1061 } 1062 return; 1063 } 1064 if (!canUserModifyAccountsForType(userId, account.type)) { 1065 try { 1066 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1067 "User cannot modify accounts of this type (policy)."); 1068 } catch (RemoteException re) { 1069 } 1070 return; 1071 } 1072 1073 long identityToken = clearCallingIdentity(); 1074 1075 cancelNotification(getSigninRequiredNotificationId(accounts, account), user); 1076 synchronized (accounts.credentialsPermissionNotificationIds) { 1077 for (Pair<Pair<Account, String>, Integer> pair: 1078 accounts.credentialsPermissionNotificationIds.keySet()) { 1079 if (account.equals(pair.first.first)) { 1080 int id = accounts.credentialsPermissionNotificationIds.get(pair); 1081 cancelNotification(id, user); 1082 } 1083 } 1084 } 1085 1086 try { 1087 new RemoveAccountSession(accounts, response, account).bind(); 1088 } finally { 1089 restoreCallingIdentity(identityToken); 1090 } 1091 } 1092 1093 @Override 1094 public void removeAccountAsUser(IAccountManagerResponse response, Account account, 1095 int userId) { 1096 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1097 Log.v(TAG, "removeAccount: " + account 1098 + ", response " + response 1099 + ", caller's uid " + Binder.getCallingUid() 1100 + ", pid " + Binder.getCallingPid() 1101 + ", for user id " + userId); 1102 } 1103 if (response == null) throw new IllegalArgumentException("response is null"); 1104 if (account == null) throw new IllegalArgumentException("account is null"); 1105 1106 // Only allow the system process to modify accounts of other users 1107 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() 1108 + " trying to remove account for " + userId); 1109 checkManageAccountsPermission(); 1110 1111 UserAccounts accounts = getUserAccounts(userId); 1112 if (!canUserModifyAccounts(userId)) { 1113 try { 1114 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 1115 "User cannot modify accounts"); 1116 } catch (RemoteException re) { 1117 } 1118 return; 1119 } 1120 if (!canUserModifyAccountsForType(userId, account.type)) { 1121 try { 1122 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1123 "User cannot modify accounts of this type (policy)."); 1124 } catch (RemoteException re) { 1125 } 1126 return; 1127 } 1128 1129 UserHandle user = new UserHandle(userId); 1130 long identityToken = clearCallingIdentity(); 1131 1132 cancelNotification(getSigninRequiredNotificationId(accounts, account), user); 1133 synchronized(accounts.credentialsPermissionNotificationIds) { 1134 for (Pair<Pair<Account, String>, Integer> pair: 1135 accounts.credentialsPermissionNotificationIds.keySet()) { 1136 if (account.equals(pair.first.first)) { 1137 int id = accounts.credentialsPermissionNotificationIds.get(pair); 1138 cancelNotification(id, user); 1139 } 1140 } 1141 } 1142 1143 try { 1144 new RemoveAccountSession(accounts, response, account).bind(); 1145 } finally { 1146 restoreCallingIdentity(identityToken); 1147 } 1148 } 1149 1150 private class RemoveAccountSession extends Session { 1151 final Account mAccount; 1152 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, 1153 Account account) { 1154 super(accounts, response, account.type, false /* expectActivityLaunch */, 1155 true /* stripAuthTokenFromResult */); 1156 mAccount = account; 1157 } 1158 1159 @Override 1160 protected String toDebugString(long now) { 1161 return super.toDebugString(now) + ", removeAccount" 1162 + ", account " + mAccount; 1163 } 1164 1165 @Override 1166 public void run() throws RemoteException { 1167 mAuthenticator.getAccountRemovalAllowed(this, mAccount); 1168 } 1169 1170 @Override 1171 public void onResult(Bundle result) { 1172 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) 1173 && !result.containsKey(AccountManager.KEY_INTENT)) { 1174 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); 1175 if (removalAllowed) { 1176 removeAccountInternal(mAccounts, mAccount); 1177 } 1178 IAccountManagerResponse response = getResponseAndClose(); 1179 if (response != null) { 1180 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1181 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1182 + response); 1183 } 1184 Bundle result2 = new Bundle(); 1185 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed); 1186 try { 1187 response.onResult(result2); 1188 } catch (RemoteException e) { 1189 // ignore 1190 } 1191 } 1192 } 1193 super.onResult(result); 1194 } 1195 } 1196 1197 /* For testing */ 1198 protected void removeAccountInternal(Account account) { 1199 removeAccountInternal(getUserAccountsForCaller(), account); 1200 } 1201 1202 private void removeAccountInternal(UserAccounts accounts, Account account) { 1203 synchronized (accounts.cacheLock) { 1204 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1205 db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 1206 new String[]{account.name, account.type}); 1207 removeAccountFromCacheLocked(accounts, account); 1208 sendAccountsChangedBroadcast(accounts.userId); 1209 } 1210 if (accounts.userId == UserHandle.USER_OWNER) { 1211 // Owner's account was removed, remove from any users that are sharing 1212 // this account. 1213 long id = Binder.clearCallingIdentity(); 1214 try { 1215 List<UserInfo> users = mUserManager.getUsers(true); 1216 for (UserInfo user : users) { 1217 if (!user.isPrimary() && user.isRestricted()) { 1218 removeSharedAccountAsUser(account, user.id); 1219 } 1220 } 1221 } finally { 1222 Binder.restoreCallingIdentity(id); 1223 } 1224 } 1225 } 1226 1227 @Override 1228 public void invalidateAuthToken(String accountType, String authToken) { 1229 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1230 Log.v(TAG, "invalidateAuthToken: accountType " + accountType 1231 + ", caller's uid " + Binder.getCallingUid() 1232 + ", pid " + Binder.getCallingPid()); 1233 } 1234 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1235 if (authToken == null) throw new IllegalArgumentException("authToken is null"); 1236 checkManageAccountsOrUseCredentialsPermissions(); 1237 UserAccounts accounts = getUserAccountsForCaller(); 1238 long identityToken = clearCallingIdentity(); 1239 try { 1240 synchronized (accounts.cacheLock) { 1241 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1242 db.beginTransaction(); 1243 try { 1244 invalidateAuthTokenLocked(accounts, db, accountType, authToken); 1245 db.setTransactionSuccessful(); 1246 } finally { 1247 db.endTransaction(); 1248 } 1249 } 1250 } finally { 1251 restoreCallingIdentity(identityToken); 1252 } 1253 } 1254 1255 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, 1256 String accountType, String authToken) { 1257 if (authToken == null || accountType == null) { 1258 return; 1259 } 1260 Cursor cursor = db.rawQuery( 1261 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID 1262 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME 1263 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE 1264 + " FROM " + TABLE_ACCOUNTS 1265 + " JOIN " + TABLE_AUTHTOKENS 1266 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID 1267 + " = " + AUTHTOKENS_ACCOUNTS_ID 1268 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND " 1269 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?", 1270 new String[]{authToken, accountType}); 1271 try { 1272 while (cursor.moveToNext()) { 1273 long authTokenId = cursor.getLong(0); 1274 String accountName = cursor.getString(1); 1275 String authTokenType = cursor.getString(2); 1276 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); 1277 writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType), 1278 authTokenType, null); 1279 } 1280 } finally { 1281 cursor.close(); 1282 } 1283 } 1284 1285 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, 1286 String authToken) { 1287 if (account == null || type == null) { 1288 return false; 1289 } 1290 cancelNotification(getSigninRequiredNotificationId(accounts, account), 1291 new UserHandle(accounts.userId)); 1292 synchronized (accounts.cacheLock) { 1293 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1294 db.beginTransaction(); 1295 try { 1296 long accountId = getAccountIdLocked(db, account); 1297 if (accountId < 0) { 1298 return false; 1299 } 1300 db.delete(TABLE_AUTHTOKENS, 1301 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", 1302 new String[]{type}); 1303 ContentValues values = new ContentValues(); 1304 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); 1305 values.put(AUTHTOKENS_TYPE, type); 1306 values.put(AUTHTOKENS_AUTHTOKEN, authToken); 1307 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { 1308 db.setTransactionSuccessful(); 1309 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken); 1310 return true; 1311 } 1312 return false; 1313 } finally { 1314 db.endTransaction(); 1315 } 1316 } 1317 } 1318 1319 @Override 1320 public String peekAuthToken(Account account, String authTokenType) { 1321 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1322 Log.v(TAG, "peekAuthToken: " + account 1323 + ", authTokenType " + authTokenType 1324 + ", caller's uid " + Binder.getCallingUid() 1325 + ", pid " + Binder.getCallingPid()); 1326 } 1327 if (account == null) throw new IllegalArgumentException("account is null"); 1328 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1329 checkAuthenticateAccountsPermission(account); 1330 UserAccounts accounts = getUserAccountsForCaller(); 1331 long identityToken = clearCallingIdentity(); 1332 try { 1333 return readAuthTokenInternal(accounts, account, authTokenType); 1334 } finally { 1335 restoreCallingIdentity(identityToken); 1336 } 1337 } 1338 1339 @Override 1340 public void setAuthToken(Account account, String authTokenType, String authToken) { 1341 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1342 Log.v(TAG, "setAuthToken: " + account 1343 + ", authTokenType " + authTokenType 1344 + ", caller's uid " + Binder.getCallingUid() 1345 + ", pid " + Binder.getCallingPid()); 1346 } 1347 if (account == null) throw new IllegalArgumentException("account is null"); 1348 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1349 checkAuthenticateAccountsPermission(account); 1350 UserAccounts accounts = getUserAccountsForCaller(); 1351 long identityToken = clearCallingIdentity(); 1352 try { 1353 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); 1354 } finally { 1355 restoreCallingIdentity(identityToken); 1356 } 1357 } 1358 1359 @Override 1360 public void setPassword(Account account, String password) { 1361 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1362 Log.v(TAG, "setAuthToken: " + account 1363 + ", caller's uid " + Binder.getCallingUid() 1364 + ", pid " + Binder.getCallingPid()); 1365 } 1366 if (account == null) throw new IllegalArgumentException("account is null"); 1367 checkAuthenticateAccountsPermission(account); 1368 UserAccounts accounts = getUserAccountsForCaller(); 1369 long identityToken = clearCallingIdentity(); 1370 try { 1371 setPasswordInternal(accounts, account, password); 1372 } finally { 1373 restoreCallingIdentity(identityToken); 1374 } 1375 } 1376 1377 private void setPasswordInternal(UserAccounts accounts, Account account, String password) { 1378 if (account == null) { 1379 return; 1380 } 1381 synchronized (accounts.cacheLock) { 1382 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1383 db.beginTransaction(); 1384 try { 1385 final ContentValues values = new ContentValues(); 1386 values.put(ACCOUNTS_PASSWORD, password); 1387 final long accountId = getAccountIdLocked(db, account); 1388 if (accountId >= 0) { 1389 final String[] argsAccountId = {String.valueOf(accountId)}; 1390 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); 1391 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); 1392 accounts.authTokenCache.remove(account); 1393 db.setTransactionSuccessful(); 1394 } 1395 } finally { 1396 db.endTransaction(); 1397 } 1398 sendAccountsChangedBroadcast(accounts.userId); 1399 } 1400 } 1401 1402 private void sendAccountsChangedBroadcast(int userId) { 1403 Log.i(TAG, "the accounts changed, sending broadcast of " 1404 + ACCOUNTS_CHANGED_INTENT.getAction()); 1405 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); 1406 } 1407 1408 @Override 1409 public void clearPassword(Account account) { 1410 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1411 Log.v(TAG, "clearPassword: " + account 1412 + ", caller's uid " + Binder.getCallingUid() 1413 + ", pid " + Binder.getCallingPid()); 1414 } 1415 if (account == null) throw new IllegalArgumentException("account is null"); 1416 checkManageAccountsPermission(); 1417 UserAccounts accounts = getUserAccountsForCaller(); 1418 long identityToken = clearCallingIdentity(); 1419 try { 1420 setPasswordInternal(accounts, account, null); 1421 } finally { 1422 restoreCallingIdentity(identityToken); 1423 } 1424 } 1425 1426 @Override 1427 public void setUserData(Account account, String key, String value) { 1428 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1429 Log.v(TAG, "setUserData: " + account 1430 + ", key " + key 1431 + ", caller's uid " + Binder.getCallingUid() 1432 + ", pid " + Binder.getCallingPid()); 1433 } 1434 if (key == null) throw new IllegalArgumentException("key is null"); 1435 if (account == null) throw new IllegalArgumentException("account is null"); 1436 checkAuthenticateAccountsPermission(account); 1437 UserAccounts accounts = getUserAccountsForCaller(); 1438 long identityToken = clearCallingIdentity(); 1439 try { 1440 setUserdataInternal(accounts, account, key, value); 1441 } finally { 1442 restoreCallingIdentity(identityToken); 1443 } 1444 } 1445 1446 private void setUserdataInternal(UserAccounts accounts, Account account, String key, 1447 String value) { 1448 if (account == null || key == null) { 1449 return; 1450 } 1451 synchronized (accounts.cacheLock) { 1452 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 1453 db.beginTransaction(); 1454 try { 1455 long accountId = getAccountIdLocked(db, account); 1456 if (accountId < 0) { 1457 return; 1458 } 1459 long extrasId = getExtrasIdLocked(db, accountId, key); 1460 if (extrasId < 0 ) { 1461 extrasId = insertExtraLocked(db, accountId, key, value); 1462 if (extrasId < 0) { 1463 return; 1464 } 1465 } else { 1466 ContentValues values = new ContentValues(); 1467 values.put(EXTRAS_VALUE, value); 1468 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { 1469 return; 1470 } 1471 1472 } 1473 writeUserDataIntoCacheLocked(accounts, db, account, key, value); 1474 db.setTransactionSuccessful(); 1475 } finally { 1476 db.endTransaction(); 1477 } 1478 } 1479 } 1480 1481 private void onResult(IAccountManagerResponse response, Bundle result) { 1482 if (result == null) { 1483 Log.e(TAG, "the result is unexpectedly null", new Exception()); 1484 } 1485 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1486 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 1487 + response); 1488 } 1489 try { 1490 response.onResult(result); 1491 } catch (RemoteException e) { 1492 // if the caller is dead then there is no one to care about remote 1493 // exceptions 1494 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1495 Log.v(TAG, "failure while notifying response", e); 1496 } 1497 } 1498 } 1499 1500 @Override 1501 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType, 1502 final String authTokenType) 1503 throws RemoteException { 1504 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1505 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1506 1507 final int callingUid = getCallingUid(); 1508 clearCallingIdentity(); 1509 if (callingUid != Process.SYSTEM_UID) { 1510 throw new SecurityException("can only call from system"); 1511 } 1512 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); 1513 long identityToken = clearCallingIdentity(); 1514 try { 1515 new Session(accounts, response, accountType, false, 1516 false /* stripAuthTokenFromResult */) { 1517 @Override 1518 protected String toDebugString(long now) { 1519 return super.toDebugString(now) + ", getAuthTokenLabel" 1520 + ", " + accountType 1521 + ", authTokenType " + authTokenType; 1522 } 1523 1524 @Override 1525 public void run() throws RemoteException { 1526 mAuthenticator.getAuthTokenLabel(this, authTokenType); 1527 } 1528 1529 @Override 1530 public void onResult(Bundle result) { 1531 if (result != null) { 1532 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); 1533 Bundle bundle = new Bundle(); 1534 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label); 1535 super.onResult(bundle); 1536 return; 1537 } else { 1538 super.onResult(result); 1539 } 1540 } 1541 }.bind(); 1542 } finally { 1543 restoreCallingIdentity(identityToken); 1544 } 1545 } 1546 1547 @Override 1548 public void getAuthToken(IAccountManagerResponse response, final Account account, 1549 final String authTokenType, final boolean notifyOnAuthFailure, 1550 final boolean expectActivityLaunch, Bundle loginOptionsIn) { 1551 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1552 Log.v(TAG, "getAuthToken: " + account 1553 + ", response " + response 1554 + ", authTokenType " + authTokenType 1555 + ", notifyOnAuthFailure " + notifyOnAuthFailure 1556 + ", expectActivityLaunch " + expectActivityLaunch 1557 + ", caller's uid " + Binder.getCallingUid() 1558 + ", pid " + Binder.getCallingPid()); 1559 } 1560 if (response == null) throw new IllegalArgumentException("response is null"); 1561 try { 1562 if (account == null) { 1563 Slog.w(TAG, "getAuthToken called with null account"); 1564 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null"); 1565 return; 1566 } 1567 if (authTokenType == null) { 1568 Slog.w(TAG, "getAuthToken called with null authTokenType"); 1569 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null"); 1570 return; 1571 } 1572 } catch (RemoteException e) { 1573 Slog.w(TAG, "Failed to report error back to the client." + e); 1574 return; 1575 } 1576 1577 checkBinderPermission(Manifest.permission.USE_CREDENTIALS); 1578 final UserAccounts accounts = getUserAccountsForCaller(); 1579 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 1580 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 1581 AuthenticatorDescription.newKey(account.type), accounts.userId); 1582 final boolean customTokens = 1583 authenticatorInfo != null && authenticatorInfo.type.customTokens; 1584 1585 // skip the check if customTokens 1586 final int callerUid = Binder.getCallingUid(); 1587 final boolean permissionGranted = customTokens || 1588 permissionIsGranted(account, authTokenType, callerUid); 1589 1590 final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() : 1591 loginOptionsIn; 1592 // let authenticator know the identity of the caller 1593 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); 1594 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid()); 1595 if (notifyOnAuthFailure) { 1596 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); 1597 } 1598 1599 long identityToken = clearCallingIdentity(); 1600 try { 1601 // if the caller has permission, do the peek. otherwise go the more expensive 1602 // route of starting a Session 1603 if (!customTokens && permissionGranted) { 1604 String authToken = readAuthTokenInternal(accounts, account, authTokenType); 1605 if (authToken != null) { 1606 Bundle result = new Bundle(); 1607 result.putString(AccountManager.KEY_AUTHTOKEN, authToken); 1608 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 1609 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 1610 onResult(response, result); 1611 return; 1612 } 1613 } 1614 1615 new Session(accounts, response, account.type, expectActivityLaunch, 1616 false /* stripAuthTokenFromResult */) { 1617 @Override 1618 protected String toDebugString(long now) { 1619 if (loginOptions != null) loginOptions.keySet(); 1620 return super.toDebugString(now) + ", getAuthToken" 1621 + ", " + account 1622 + ", authTokenType " + authTokenType 1623 + ", loginOptions " + loginOptions 1624 + ", notifyOnAuthFailure " + notifyOnAuthFailure; 1625 } 1626 1627 @Override 1628 public void run() throws RemoteException { 1629 // If the caller doesn't have permission then create and return the 1630 // "grant permission" intent instead of the "getAuthToken" intent. 1631 if (!permissionGranted) { 1632 mAuthenticator.getAuthTokenLabel(this, authTokenType); 1633 } else { 1634 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions); 1635 } 1636 } 1637 1638 @Override 1639 public void onResult(Bundle result) { 1640 if (result != null) { 1641 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { 1642 Intent intent = newGrantCredentialsPermissionIntent(account, callerUid, 1643 new AccountAuthenticatorResponse(this), 1644 authTokenType, 1645 result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL)); 1646 Bundle bundle = new Bundle(); 1647 bundle.putParcelable(AccountManager.KEY_INTENT, intent); 1648 onResult(bundle); 1649 return; 1650 } 1651 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); 1652 if (authToken != null) { 1653 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); 1654 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 1655 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { 1656 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 1657 "the type and name should not be empty"); 1658 return; 1659 } 1660 if (!customTokens) { 1661 saveAuthTokenToDatabase(mAccounts, new Account(name, type), 1662 authTokenType, authToken); 1663 } 1664 } 1665 1666 Intent intent = result.getParcelable(AccountManager.KEY_INTENT); 1667 if (intent != null && notifyOnAuthFailure && !customTokens) { 1668 doNotification(mAccounts, 1669 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), 1670 intent, accounts.userId); 1671 } 1672 } 1673 super.onResult(result); 1674 } 1675 }.bind(); 1676 } finally { 1677 restoreCallingIdentity(identityToken); 1678 } 1679 } 1680 1681 private void createNoCredentialsPermissionNotification(Account account, Intent intent, 1682 int userId) { 1683 int uid = intent.getIntExtra( 1684 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); 1685 String authTokenType = intent.getStringExtra( 1686 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); 1687 String authTokenLabel = intent.getStringExtra( 1688 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL); 1689 1690 Notification n = new Notification(android.R.drawable.stat_sys_warning, null, 1691 0 /* when */); 1692 final String titleAndSubtitle = 1693 mContext.getString(R.string.permission_request_notification_with_subtitle, 1694 account.name); 1695 final int index = titleAndSubtitle.indexOf('\n'); 1696 String title = titleAndSubtitle; 1697 String subtitle = ""; 1698 if (index > 0) { 1699 title = titleAndSubtitle.substring(0, index); 1700 subtitle = titleAndSubtitle.substring(index + 1); 1701 } 1702 UserHandle user = new UserHandle(userId); 1703 n.color = mContext.getResources().getColor( 1704 com.android.internal.R.color.system_notification_accent_color); 1705 n.setLatestEventInfo(mContext, title, subtitle, 1706 PendingIntent.getActivityAsUser(mContext, 0, intent, 1707 PendingIntent.FLAG_CANCEL_CURRENT, null, user)); 1708 installNotification(getCredentialPermissionNotificationId( 1709 account, authTokenType, uid), n, user); 1710 } 1711 1712 private Intent newGrantCredentialsPermissionIntent(Account account, int uid, 1713 AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) { 1714 1715 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); 1716 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. 1717 // Since it was set in Eclair+ we can't change it without breaking apps using 1718 // the intent from a non-Activity context. 1719 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1720 intent.addCategory( 1721 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid))); 1722 1723 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); 1724 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); 1725 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); 1726 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid); 1727 1728 return intent; 1729 } 1730 1731 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, 1732 int uid) { 1733 Integer id; 1734 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 1735 synchronized (accounts.credentialsPermissionNotificationIds) { 1736 final Pair<Pair<Account, String>, Integer> key = 1737 new Pair<Pair<Account, String>, Integer>( 1738 new Pair<Account, String>(account, authTokenType), uid); 1739 id = accounts.credentialsPermissionNotificationIds.get(key); 1740 if (id == null) { 1741 id = mNotificationIds.incrementAndGet(); 1742 accounts.credentialsPermissionNotificationIds.put(key, id); 1743 } 1744 } 1745 return id; 1746 } 1747 1748 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { 1749 Integer id; 1750 synchronized (accounts.signinRequiredNotificationIds) { 1751 id = accounts.signinRequiredNotificationIds.get(account); 1752 if (id == null) { 1753 id = mNotificationIds.incrementAndGet(); 1754 accounts.signinRequiredNotificationIds.put(account, id); 1755 } 1756 } 1757 return id; 1758 } 1759 1760 @Override 1761 public void addAccount(final IAccountManagerResponse response, final String accountType, 1762 final String authTokenType, final String[] requiredFeatures, 1763 final boolean expectActivityLaunch, final Bundle optionsIn) { 1764 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1765 Log.v(TAG, "addAccount: accountType " + accountType 1766 + ", response " + response 1767 + ", authTokenType " + authTokenType 1768 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 1769 + ", expectActivityLaunch " + expectActivityLaunch 1770 + ", caller's uid " + Binder.getCallingUid() 1771 + ", pid " + Binder.getCallingPid()); 1772 } 1773 if (response == null) throw new IllegalArgumentException("response is null"); 1774 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1775 checkManageAccountsPermission(); 1776 1777 // Is user disallowed from modifying accounts? 1778 int userId = Binder.getCallingUserHandle().getIdentifier(); 1779 if (!canUserModifyAccounts(userId)) { 1780 try { 1781 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 1782 "User is not allowed to add an account!"); 1783 } catch (RemoteException re) { 1784 } 1785 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED); 1786 return; 1787 } 1788 if (!canUserModifyAccountsForType(userId, accountType)) { 1789 try { 1790 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1791 "User cannot modify accounts of this type (policy)."); 1792 } catch (RemoteException re) { 1793 } 1794 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE); 1795 return; 1796 } 1797 1798 UserAccounts accounts = getUserAccountsForCaller(); 1799 final int pid = Binder.getCallingPid(); 1800 final int uid = Binder.getCallingUid(); 1801 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 1802 options.putInt(AccountManager.KEY_CALLER_UID, uid); 1803 options.putInt(AccountManager.KEY_CALLER_PID, pid); 1804 1805 long identityToken = clearCallingIdentity(); 1806 try { 1807 new Session(accounts, response, accountType, expectActivityLaunch, 1808 true /* stripAuthTokenFromResult */) { 1809 @Override 1810 public void run() throws RemoteException { 1811 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 1812 options); 1813 } 1814 1815 @Override 1816 protected String toDebugString(long now) { 1817 return super.toDebugString(now) + ", addAccount" 1818 + ", accountType " + accountType 1819 + ", requiredFeatures " 1820 + (requiredFeatures != null 1821 ? TextUtils.join(",", requiredFeatures) 1822 : null); 1823 } 1824 }.bind(); 1825 } finally { 1826 restoreCallingIdentity(identityToken); 1827 } 1828 } 1829 1830 @Override 1831 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType, 1832 final String authTokenType, final String[] requiredFeatures, 1833 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) { 1834 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1835 Log.v(TAG, "addAccount: accountType " + accountType 1836 + ", response " + response 1837 + ", authTokenType " + authTokenType 1838 + ", requiredFeatures " + stringArrayToString(requiredFeatures) 1839 + ", expectActivityLaunch " + expectActivityLaunch 1840 + ", caller's uid " + Binder.getCallingUid() 1841 + ", pid " + Binder.getCallingPid() 1842 + ", for user id " + userId); 1843 } 1844 if (response == null) throw new IllegalArgumentException("response is null"); 1845 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 1846 checkManageAccountsPermission(); 1847 1848 // Only allow the system process to add accounts of other users 1849 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() 1850 + " trying to add account for " + userId); 1851 1852 // Is user disallowed from modifying accounts? 1853 if (!canUserModifyAccounts(userId)) { 1854 try { 1855 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, 1856 "User is not allowed to add an account!"); 1857 } catch (RemoteException re) { 1858 } 1859 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED); 1860 return; 1861 } 1862 if (!canUserModifyAccountsForType(userId, accountType)) { 1863 try { 1864 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, 1865 "User cannot modify accounts of this type (policy)."); 1866 } catch (RemoteException re) { 1867 } 1868 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE); 1869 return; 1870 } 1871 1872 UserAccounts accounts = getUserAccounts(userId); 1873 final int pid = Binder.getCallingPid(); 1874 final int uid = Binder.getCallingUid(); 1875 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; 1876 options.putInt(AccountManager.KEY_CALLER_UID, uid); 1877 options.putInt(AccountManager.KEY_CALLER_PID, pid); 1878 1879 long identityToken = clearCallingIdentity(); 1880 try { 1881 new Session(accounts, response, accountType, expectActivityLaunch, 1882 true /* stripAuthTokenFromResult */) { 1883 @Override 1884 public void run() throws RemoteException { 1885 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, 1886 options); 1887 } 1888 1889 @Override 1890 protected String toDebugString(long now) { 1891 return super.toDebugString(now) + ", addAccount" 1892 + ", accountType " + accountType 1893 + ", requiredFeatures " 1894 + (requiredFeatures != null 1895 ? TextUtils.join(",", requiredFeatures) 1896 : null); 1897 } 1898 }.bind(); 1899 } finally { 1900 restoreCallingIdentity(identityToken); 1901 } 1902 } 1903 1904 private void showCantAddAccount(int errorCode) { 1905 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class); 1906 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode); 1907 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1908 long identityToken = clearCallingIdentity(); 1909 try { 1910 mContext.startActivity(cantAddAccount); 1911 } finally { 1912 restoreCallingIdentity(identityToken); 1913 } 1914 } 1915 1916 @Override 1917 public void confirmCredentialsAsUser(IAccountManagerResponse response, 1918 final Account account, final Bundle options, final boolean expectActivityLaunch, 1919 int userId) { 1920 // Only allow the system process to read accounts of other users 1921 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() 1922 + " trying to confirm account credentials for " + userId); 1923 1924 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1925 Log.v(TAG, "confirmCredentials: " + account 1926 + ", response " + response 1927 + ", expectActivityLaunch " + expectActivityLaunch 1928 + ", caller's uid " + Binder.getCallingUid() 1929 + ", pid " + Binder.getCallingPid()); 1930 } 1931 if (response == null) throw new IllegalArgumentException("response is null"); 1932 if (account == null) throw new IllegalArgumentException("account is null"); 1933 checkManageAccountsPermission(); 1934 UserAccounts accounts = getUserAccounts(userId); 1935 long identityToken = clearCallingIdentity(); 1936 try { 1937 new Session(accounts, response, account.type, expectActivityLaunch, 1938 true /* stripAuthTokenFromResult */) { 1939 @Override 1940 public void run() throws RemoteException { 1941 mAuthenticator.confirmCredentials(this, account, options); 1942 } 1943 @Override 1944 protected String toDebugString(long now) { 1945 return super.toDebugString(now) + ", confirmCredentials" 1946 + ", " + account; 1947 } 1948 }.bind(); 1949 } finally { 1950 restoreCallingIdentity(identityToken); 1951 } 1952 } 1953 1954 @Override 1955 public void updateCredentials(IAccountManagerResponse response, final Account account, 1956 final String authTokenType, final boolean expectActivityLaunch, 1957 final Bundle loginOptions) { 1958 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1959 Log.v(TAG, "updateCredentials: " + account 1960 + ", response " + response 1961 + ", authTokenType " + authTokenType 1962 + ", expectActivityLaunch " + expectActivityLaunch 1963 + ", caller's uid " + Binder.getCallingUid() 1964 + ", pid " + Binder.getCallingPid()); 1965 } 1966 if (response == null) throw new IllegalArgumentException("response is null"); 1967 if (account == null) throw new IllegalArgumentException("account is null"); 1968 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); 1969 checkManageAccountsPermission(); 1970 UserAccounts accounts = getUserAccountsForCaller(); 1971 long identityToken = clearCallingIdentity(); 1972 try { 1973 new Session(accounts, response, account.type, expectActivityLaunch, 1974 true /* stripAuthTokenFromResult */) { 1975 @Override 1976 public void run() throws RemoteException { 1977 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); 1978 } 1979 @Override 1980 protected String toDebugString(long now) { 1981 if (loginOptions != null) loginOptions.keySet(); 1982 return super.toDebugString(now) + ", updateCredentials" 1983 + ", " + account 1984 + ", authTokenType " + authTokenType 1985 + ", loginOptions " + loginOptions; 1986 } 1987 }.bind(); 1988 } finally { 1989 restoreCallingIdentity(identityToken); 1990 } 1991 } 1992 1993 @Override 1994 public void editProperties(IAccountManagerResponse response, final String accountType, 1995 final boolean expectActivityLaunch) { 1996 if (Log.isLoggable(TAG, Log.VERBOSE)) { 1997 Log.v(TAG, "editProperties: accountType " + accountType 1998 + ", response " + response 1999 + ", expectActivityLaunch " + expectActivityLaunch 2000 + ", caller's uid " + Binder.getCallingUid() 2001 + ", pid " + Binder.getCallingPid()); 2002 } 2003 if (response == null) throw new IllegalArgumentException("response is null"); 2004 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2005 checkManageAccountsPermission(); 2006 UserAccounts accounts = getUserAccountsForCaller(); 2007 long identityToken = clearCallingIdentity(); 2008 try { 2009 new Session(accounts, response, accountType, expectActivityLaunch, 2010 true /* stripAuthTokenFromResult */) { 2011 @Override 2012 public void run() throws RemoteException { 2013 mAuthenticator.editProperties(this, mAccountType); 2014 } 2015 @Override 2016 protected String toDebugString(long now) { 2017 return super.toDebugString(now) + ", editProperties" 2018 + ", accountType " + accountType; 2019 } 2020 }.bind(); 2021 } finally { 2022 restoreCallingIdentity(identityToken); 2023 } 2024 } 2025 2026 private class GetAccountsByTypeAndFeatureSession extends Session { 2027 private final String[] mFeatures; 2028 private volatile Account[] mAccountsOfType = null; 2029 private volatile ArrayList<Account> mAccountsWithFeatures = null; 2030 private volatile int mCurrentAccount = 0; 2031 private final int mCallingUid; 2032 2033 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, 2034 IAccountManagerResponse response, String type, String[] features, int callingUid) { 2035 super(accounts, response, type, false /* expectActivityLaunch */, 2036 true /* stripAuthTokenFromResult */); 2037 mCallingUid = callingUid; 2038 mFeatures = features; 2039 } 2040 2041 @Override 2042 public void run() throws RemoteException { 2043 synchronized (mAccounts.cacheLock) { 2044 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, 2045 null); 2046 } 2047 // check whether each account matches the requested features 2048 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); 2049 mCurrentAccount = 0; 2050 2051 checkAccount(); 2052 } 2053 2054 public void checkAccount() { 2055 if (mCurrentAccount >= mAccountsOfType.length) { 2056 sendResult(); 2057 return; 2058 } 2059 2060 final IAccountAuthenticator accountAuthenticator = mAuthenticator; 2061 if (accountAuthenticator == null) { 2062 // It is possible that the authenticator has died, which is indicated by 2063 // mAuthenticator being set to null. If this happens then just abort. 2064 // There is no need to send back a result or error in this case since 2065 // that already happened when mAuthenticator was cleared. 2066 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2067 Log.v(TAG, "checkAccount: aborting session since we are no longer" 2068 + " connected to the authenticator, " + toDebugString()); 2069 } 2070 return; 2071 } 2072 try { 2073 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); 2074 } catch (RemoteException e) { 2075 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); 2076 } 2077 } 2078 2079 @Override 2080 public void onResult(Bundle result) { 2081 mNumResults++; 2082 if (result == null) { 2083 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); 2084 return; 2085 } 2086 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 2087 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); 2088 } 2089 mCurrentAccount++; 2090 checkAccount(); 2091 } 2092 2093 public void sendResult() { 2094 IAccountManagerResponse response = getResponseAndClose(); 2095 if (response != null) { 2096 try { 2097 Account[] accounts = new Account[mAccountsWithFeatures.size()]; 2098 for (int i = 0; i < accounts.length; i++) { 2099 accounts[i] = mAccountsWithFeatures.get(i); 2100 } 2101 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2102 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " 2103 + response); 2104 } 2105 Bundle result = new Bundle(); 2106 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 2107 response.onResult(result); 2108 } catch (RemoteException e) { 2109 // if the caller is dead then there is no one to care about remote exceptions 2110 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2111 Log.v(TAG, "failure while notifying response", e); 2112 } 2113 } 2114 } 2115 } 2116 2117 2118 @Override 2119 protected String toDebugString(long now) { 2120 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" 2121 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); 2122 } 2123 } 2124 2125 /** 2126 * Returns the accounts for a specific user 2127 * @hide 2128 */ 2129 public Account[] getAccounts(int userId) { 2130 checkReadAccountsPermission(); 2131 UserAccounts accounts = getUserAccounts(userId); 2132 int callingUid = Binder.getCallingUid(); 2133 long identityToken = clearCallingIdentity(); 2134 try { 2135 synchronized (accounts.cacheLock) { 2136 return getAccountsFromCacheLocked(accounts, null, callingUid, null); 2137 } 2138 } finally { 2139 restoreCallingIdentity(identityToken); 2140 } 2141 } 2142 2143 /** 2144 * Returns accounts for all running users. 2145 * 2146 * @hide 2147 */ 2148 public AccountAndUser[] getRunningAccounts() { 2149 final int[] runningUserIds; 2150 try { 2151 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); 2152 } catch (RemoteException e) { 2153 // Running in system_server; should never happen 2154 throw new RuntimeException(e); 2155 } 2156 return getAccounts(runningUserIds); 2157 } 2158 2159 /** {@hide} */ 2160 public AccountAndUser[] getAllAccounts() { 2161 final List<UserInfo> users = getUserManager().getUsers(); 2162 final int[] userIds = new int[users.size()]; 2163 for (int i = 0; i < userIds.length; i++) { 2164 userIds[i] = users.get(i).id; 2165 } 2166 return getAccounts(userIds); 2167 } 2168 2169 private AccountAndUser[] getAccounts(int[] userIds) { 2170 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); 2171 for (int userId : userIds) { 2172 UserAccounts userAccounts = getUserAccounts(userId); 2173 if (userAccounts == null) continue; 2174 synchronized (userAccounts.cacheLock) { 2175 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, 2176 Binder.getCallingUid(), null); 2177 for (int a = 0; a < accounts.length; a++) { 2178 runningAccounts.add(new AccountAndUser(accounts[a], userId)); 2179 } 2180 } 2181 } 2182 2183 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; 2184 return runningAccounts.toArray(accountsArray); 2185 } 2186 2187 @Override 2188 public Account[] getAccountsAsUser(String type, int userId) { 2189 return getAccountsAsUser(type, userId, null, -1); 2190 } 2191 2192 private Account[] getAccountsAsUser(String type, int userId, String callingPackage, 2193 int packageUid) { 2194 int callingUid = Binder.getCallingUid(); 2195 // Only allow the system process to read accounts of other users 2196 if (userId != UserHandle.getCallingUserId() 2197 && callingUid != Process.myUid() 2198 && mContext.checkCallingOrSelfPermission( 2199 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) 2200 != PackageManager.PERMISSION_GRANTED) { 2201 throw new SecurityException("User " + UserHandle.getCallingUserId() 2202 + " trying to get account for " + userId); 2203 } 2204 2205 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2206 Log.v(TAG, "getAccounts: accountType " + type 2207 + ", caller's uid " + Binder.getCallingUid() 2208 + ", pid " + Binder.getCallingPid()); 2209 } 2210 // If the original calling app was using the framework account chooser activity, we'll 2211 // be passed in the original caller's uid here, which is what should be used for filtering. 2212 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { 2213 callingUid = packageUid; 2214 } 2215 checkReadAccountsPermission(); 2216 UserAccounts accounts = getUserAccounts(userId); 2217 long identityToken = clearCallingIdentity(); 2218 try { 2219 synchronized (accounts.cacheLock) { 2220 return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage); 2221 } 2222 } finally { 2223 restoreCallingIdentity(identityToken); 2224 } 2225 } 2226 2227 @Override 2228 public boolean addSharedAccountAsUser(Account account, int userId) { 2229 userId = handleIncomingUser(userId); 2230 SQLiteDatabase db = getUserAccounts(userId).openHelper.getWritableDatabase(); 2231 ContentValues values = new ContentValues(); 2232 values.put(ACCOUNTS_NAME, account.name); 2233 values.put(ACCOUNTS_TYPE, account.type); 2234 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2235 new String[] {account.name, account.type}); 2236 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values); 2237 if (accountId < 0) { 2238 Log.w(TAG, "insertAccountIntoDatabase: " + account 2239 + ", skipping the DB insert failed"); 2240 return false; 2241 } 2242 return true; 2243 } 2244 2245 @Override 2246 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) { 2247 userId = handleIncomingUser(userId); 2248 UserAccounts accounts = getUserAccounts(userId); 2249 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2250 final ContentValues values = new ContentValues(); 2251 values.put(ACCOUNTS_NAME, newName); 2252 values.put(ACCOUNTS_PREVIOUS_NAME, account.name); 2253 int r = db.update( 2254 TABLE_SHARED_ACCOUNTS, 2255 values, 2256 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2257 new String[] { account.name, account.type }); 2258 if (r > 0) { 2259 // Recursively rename the account. 2260 renameAccountInternal(accounts, account, newName); 2261 } 2262 return r > 0; 2263 } 2264 2265 @Override 2266 public boolean removeSharedAccountAsUser(Account account, int userId) { 2267 userId = handleIncomingUser(userId); 2268 UserAccounts accounts = getUserAccounts(userId); 2269 SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 2270 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", 2271 new String[] {account.name, account.type}); 2272 if (r > 0) { 2273 removeAccountInternal(accounts, account); 2274 } 2275 return r > 0; 2276 } 2277 2278 @Override 2279 public Account[] getSharedAccountsAsUser(int userId) { 2280 userId = handleIncomingUser(userId); 2281 UserAccounts accounts = getUserAccounts(userId); 2282 ArrayList<Account> accountList = new ArrayList<Account>(); 2283 Cursor cursor = null; 2284 try { 2285 cursor = accounts.openHelper.getReadableDatabase() 2286 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE}, 2287 null, null, null, null, null); 2288 if (cursor != null && cursor.moveToFirst()) { 2289 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME); 2290 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE); 2291 do { 2292 accountList.add(new Account(cursor.getString(nameIndex), 2293 cursor.getString(typeIndex))); 2294 } while (cursor.moveToNext()); 2295 } 2296 } finally { 2297 if (cursor != null) { 2298 cursor.close(); 2299 } 2300 } 2301 Account[] accountArray = new Account[accountList.size()]; 2302 accountList.toArray(accountArray); 2303 return accountArray; 2304 } 2305 2306 @Override 2307 public Account[] getAccounts(String type) { 2308 return getAccountsAsUser(type, UserHandle.getCallingUserId()); 2309 } 2310 2311 @Override 2312 public Account[] getAccountsForPackage(String packageName, int uid) { 2313 int callingUid = Binder.getCallingUid(); 2314 if (!UserHandle.isSameApp(callingUid, Process.myUid())) { 2315 throw new SecurityException("getAccountsForPackage() called from unauthorized uid " 2316 + callingUid + " with uid=" + uid); 2317 } 2318 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid); 2319 } 2320 2321 @Override 2322 public Account[] getAccountsByTypeForPackage(String type, String packageName) { 2323 checkBinderPermission(android.Manifest.permission.INTERACT_ACROSS_USERS); 2324 int packageUid = -1; 2325 try { 2326 packageUid = AppGlobals.getPackageManager().getPackageUid( 2327 packageName, UserHandle.getCallingUserId()); 2328 } catch (RemoteException re) { 2329 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); 2330 return new Account[0]; 2331 } 2332 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid); 2333 } 2334 2335 @Override 2336 public void getAccountsByFeatures(IAccountManagerResponse response, 2337 String type, String[] features) { 2338 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2339 Log.v(TAG, "getAccounts: accountType " + type 2340 + ", response " + response 2341 + ", features " + stringArrayToString(features) 2342 + ", caller's uid " + Binder.getCallingUid() 2343 + ", pid " + Binder.getCallingPid()); 2344 } 2345 if (response == null) throw new IllegalArgumentException("response is null"); 2346 if (type == null) throw new IllegalArgumentException("accountType is null"); 2347 checkReadAccountsPermission(); 2348 UserAccounts userAccounts = getUserAccountsForCaller(); 2349 int callingUid = Binder.getCallingUid(); 2350 long identityToken = clearCallingIdentity(); 2351 try { 2352 if (features == null || features.length == 0) { 2353 Account[] accounts; 2354 synchronized (userAccounts.cacheLock) { 2355 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); 2356 } 2357 Bundle result = new Bundle(); 2358 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); 2359 onResult(response, result); 2360 return; 2361 } 2362 new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features, 2363 callingUid).bind(); 2364 } finally { 2365 restoreCallingIdentity(identityToken); 2366 } 2367 } 2368 2369 private long getAccountIdLocked(SQLiteDatabase db, Account account) { 2370 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, 2371 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); 2372 try { 2373 if (cursor.moveToNext()) { 2374 return cursor.getLong(0); 2375 } 2376 return -1; 2377 } finally { 2378 cursor.close(); 2379 } 2380 } 2381 2382 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { 2383 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID}, 2384 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", 2385 new String[]{key}, null, null, null); 2386 try { 2387 if (cursor.moveToNext()) { 2388 return cursor.getLong(0); 2389 } 2390 return -1; 2391 } finally { 2392 cursor.close(); 2393 } 2394 } 2395 2396 private abstract class Session extends IAccountAuthenticatorResponse.Stub 2397 implements IBinder.DeathRecipient, ServiceConnection { 2398 IAccountManagerResponse mResponse; 2399 final String mAccountType; 2400 final boolean mExpectActivityLaunch; 2401 final long mCreationTime; 2402 2403 public int mNumResults = 0; 2404 private int mNumRequestContinued = 0; 2405 private int mNumErrors = 0; 2406 2407 IAccountAuthenticator mAuthenticator = null; 2408 2409 private final boolean mStripAuthTokenFromResult; 2410 protected final UserAccounts mAccounts; 2411 2412 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, 2413 boolean expectActivityLaunch, boolean stripAuthTokenFromResult) { 2414 super(); 2415 //if (response == null) throw new IllegalArgumentException("response is null"); 2416 if (accountType == null) throw new IllegalArgumentException("accountType is null"); 2417 mAccounts = accounts; 2418 mStripAuthTokenFromResult = stripAuthTokenFromResult; 2419 mResponse = response; 2420 mAccountType = accountType; 2421 mExpectActivityLaunch = expectActivityLaunch; 2422 mCreationTime = SystemClock.elapsedRealtime(); 2423 synchronized (mSessions) { 2424 mSessions.put(toString(), this); 2425 } 2426 if (response != null) { 2427 try { 2428 response.asBinder().linkToDeath(this, 0 /* flags */); 2429 } catch (RemoteException e) { 2430 mResponse = null; 2431 binderDied(); 2432 } 2433 } 2434 } 2435 2436 IAccountManagerResponse getResponseAndClose() { 2437 if (mResponse == null) { 2438 // this session has already been closed 2439 return null; 2440 } 2441 IAccountManagerResponse response = mResponse; 2442 close(); // this clears mResponse so we need to save the response before this call 2443 return response; 2444 } 2445 2446 private void close() { 2447 synchronized (mSessions) { 2448 if (mSessions.remove(toString()) == null) { 2449 // the session was already closed, so bail out now 2450 return; 2451 } 2452 } 2453 if (mResponse != null) { 2454 // stop listening for response deaths 2455 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); 2456 2457 // clear this so that we don't accidentally send any further results 2458 mResponse = null; 2459 } 2460 cancelTimeout(); 2461 unbind(); 2462 } 2463 2464 @Override 2465 public void binderDied() { 2466 mResponse = null; 2467 close(); 2468 } 2469 2470 protected String toDebugString() { 2471 return toDebugString(SystemClock.elapsedRealtime()); 2472 } 2473 2474 protected String toDebugString(long now) { 2475 return "Session: expectLaunch " + mExpectActivityLaunch 2476 + ", connected " + (mAuthenticator != null) 2477 + ", stats (" + mNumResults + "/" + mNumRequestContinued 2478 + "/" + mNumErrors + ")" 2479 + ", lifetime " + ((now - mCreationTime) / 1000.0); 2480 } 2481 2482 void bind() { 2483 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2484 Log.v(TAG, "initiating bind to authenticator type " + mAccountType); 2485 } 2486 if (!bindToAuthenticator(mAccountType)) { 2487 Log.d(TAG, "bind attempt failed for " + toDebugString()); 2488 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); 2489 } 2490 } 2491 2492 private void unbind() { 2493 if (mAuthenticator != null) { 2494 mAuthenticator = null; 2495 mContext.unbindService(this); 2496 } 2497 } 2498 2499 public void scheduleTimeout() { 2500 mMessageHandler.sendMessageDelayed( 2501 mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS); 2502 } 2503 2504 public void cancelTimeout() { 2505 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); 2506 } 2507 2508 @Override 2509 public void onServiceConnected(ComponentName name, IBinder service) { 2510 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); 2511 try { 2512 run(); 2513 } catch (RemoteException e) { 2514 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 2515 "remote exception"); 2516 } 2517 } 2518 2519 @Override 2520 public void onServiceDisconnected(ComponentName name) { 2521 mAuthenticator = null; 2522 IAccountManagerResponse response = getResponseAndClose(); 2523 if (response != null) { 2524 try { 2525 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 2526 "disconnected"); 2527 } catch (RemoteException e) { 2528 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2529 Log.v(TAG, "Session.onServiceDisconnected: " 2530 + "caught RemoteException while responding", e); 2531 } 2532 } 2533 } 2534 } 2535 2536 public abstract void run() throws RemoteException; 2537 2538 public void onTimedOut() { 2539 IAccountManagerResponse response = getResponseAndClose(); 2540 if (response != null) { 2541 try { 2542 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, 2543 "timeout"); 2544 } catch (RemoteException e) { 2545 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2546 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", 2547 e); 2548 } 2549 } 2550 } 2551 } 2552 2553 @Override 2554 public void onResult(Bundle result) { 2555 mNumResults++; 2556 Intent intent = null; 2557 if (result != null 2558 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { 2559 /* 2560 * The Authenticator API allows third party authenticators to 2561 * supply arbitrary intents to other apps that they can run, 2562 * this can be very bad when those apps are in the system like 2563 * the System Settings. 2564 */ 2565 int authenticatorUid = Binder.getCallingUid(); 2566 long bid = Binder.clearCallingIdentity(); 2567 try { 2568 PackageManager pm = mContext.getPackageManager(); 2569 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); 2570 int targetUid = resolveInfo.activityInfo.applicationInfo.uid; 2571 if (PackageManager.SIGNATURE_MATCH != 2572 pm.checkSignatures(authenticatorUid, targetUid)) { 2573 throw new SecurityException( 2574 "Activity to be started with KEY_INTENT must " + 2575 "share Authenticator's signatures"); 2576 } 2577 } finally { 2578 Binder.restoreCallingIdentity(bid); 2579 } 2580 } 2581 if (result != null 2582 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { 2583 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); 2584 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); 2585 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 2586 Account account = new Account(accountName, accountType); 2587 cancelNotification(getSigninRequiredNotificationId(mAccounts, account), 2588 new UserHandle(mAccounts.userId)); 2589 } 2590 } 2591 IAccountManagerResponse response; 2592 if (mExpectActivityLaunch && result != null 2593 && result.containsKey(AccountManager.KEY_INTENT)) { 2594 response = mResponse; 2595 } else { 2596 response = getResponseAndClose(); 2597 } 2598 if (response != null) { 2599 try { 2600 if (result == null) { 2601 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2602 Log.v(TAG, getClass().getSimpleName() 2603 + " calling onError() on response " + response); 2604 } 2605 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, 2606 "null bundle returned"); 2607 } else { 2608 if (mStripAuthTokenFromResult) { 2609 result.remove(AccountManager.KEY_AUTHTOKEN); 2610 } 2611 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2612 Log.v(TAG, getClass().getSimpleName() 2613 + " calling onResult() on response " + response); 2614 } 2615 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && 2616 (intent == null)) { 2617 // All AccountManager error codes are greater than 0 2618 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE), 2619 result.getString(AccountManager.KEY_ERROR_MESSAGE)); 2620 } else { 2621 response.onResult(result); 2622 } 2623 } 2624 } catch (RemoteException e) { 2625 // if the caller is dead then there is no one to care about remote exceptions 2626 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2627 Log.v(TAG, "failure while notifying response", e); 2628 } 2629 } 2630 } 2631 } 2632 2633 @Override 2634 public void onRequestContinued() { 2635 mNumRequestContinued++; 2636 } 2637 2638 @Override 2639 public void onError(int errorCode, String errorMessage) { 2640 mNumErrors++; 2641 IAccountManagerResponse response = getResponseAndClose(); 2642 if (response != null) { 2643 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2644 Log.v(TAG, getClass().getSimpleName() 2645 + " calling onError() on response " + response); 2646 } 2647 try { 2648 response.onError(errorCode, errorMessage); 2649 } catch (RemoteException e) { 2650 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2651 Log.v(TAG, "Session.onError: caught RemoteException while responding", e); 2652 } 2653 } 2654 } else { 2655 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2656 Log.v(TAG, "Session.onError: already closed"); 2657 } 2658 } 2659 } 2660 2661 /** 2662 * find the component name for the authenticator and initiate a bind 2663 * if no authenticator or the bind fails then return false, otherwise return true 2664 */ 2665 private boolean bindToAuthenticator(String authenticatorType) { 2666 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; 2667 authenticatorInfo = mAuthenticatorCache.getServiceInfo( 2668 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); 2669 if (authenticatorInfo == null) { 2670 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2671 Log.v(TAG, "there is no authenticator for " + authenticatorType 2672 + ", bailing out"); 2673 } 2674 return false; 2675 } 2676 2677 Intent intent = new Intent(); 2678 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); 2679 intent.setComponent(authenticatorInfo.componentName); 2680 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2681 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); 2682 } 2683 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, 2684 new UserHandle(mAccounts.userId))) { 2685 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2686 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); 2687 } 2688 return false; 2689 } 2690 2691 2692 return true; 2693 } 2694 } 2695 2696 private class MessageHandler extends Handler { 2697 MessageHandler(Looper looper) { 2698 super(looper); 2699 } 2700 2701 @Override 2702 public void handleMessage(Message msg) { 2703 switch (msg.what) { 2704 case MESSAGE_TIMED_OUT: 2705 Session session = (Session)msg.obj; 2706 session.onTimedOut(); 2707 break; 2708 2709 case MESSAGE_COPY_SHARED_ACCOUNT: 2710 copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2); 2711 break; 2712 2713 default: 2714 throw new IllegalStateException("unhandled message: " + msg.what); 2715 } 2716 } 2717 } 2718 2719 private static String getDatabaseName(int userId) { 2720 File systemDir = Environment.getSystemSecureDirectory(); 2721 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME); 2722 if (userId == 0) { 2723 // Migrate old file, if it exists, to the new location. 2724 // Make sure the new file doesn't already exist. A dummy file could have been 2725 // accidentally created in the old location, causing the new one to become corrupted 2726 // as well. 2727 File oldFile = new File(systemDir, DATABASE_NAME); 2728 if (oldFile.exists() && !databaseFile.exists()) { 2729 // Check for use directory; create if it doesn't exist, else renameTo will fail 2730 File userDir = Environment.getUserSystemDirectory(userId); 2731 if (!userDir.exists()) { 2732 if (!userDir.mkdirs()) { 2733 throw new IllegalStateException("User dir cannot be created: " + userDir); 2734 } 2735 } 2736 if (!oldFile.renameTo(databaseFile)) { 2737 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); 2738 } 2739 } 2740 } 2741 return databaseFile.getPath(); 2742 } 2743 2744 static class DatabaseHelper extends SQLiteOpenHelper { 2745 2746 public DatabaseHelper(Context context, int userId) { 2747 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION); 2748 } 2749 2750 /** 2751 * This call needs to be made while the mCacheLock is held. The way to 2752 * ensure this is to get the lock any time a method is called ont the DatabaseHelper 2753 * @param db The database. 2754 */ 2755 @Override 2756 public void onCreate(SQLiteDatabase db) { 2757 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " 2758 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2759 + ACCOUNTS_NAME + " TEXT NOT NULL, " 2760 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 2761 + ACCOUNTS_PASSWORD + " TEXT, " 2762 + ACCOUNTS_PREVIOUS_NAME + " TEXT, " 2763 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 2764 2765 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " 2766 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2767 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " 2768 + AUTHTOKENS_TYPE + " TEXT NOT NULL, " 2769 + AUTHTOKENS_AUTHTOKEN + " TEXT, " 2770 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); 2771 2772 createGrantsTable(db); 2773 2774 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " 2775 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2776 + EXTRAS_ACCOUNTS_ID + " INTEGER, " 2777 + EXTRAS_KEY + " TEXT NOT NULL, " 2778 + EXTRAS_VALUE + " TEXT, " 2779 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); 2780 2781 db.execSQL("CREATE TABLE " + TABLE_META + " ( " 2782 + META_KEY + " TEXT PRIMARY KEY NOT NULL, " 2783 + META_VALUE + " TEXT)"); 2784 2785 createSharedAccountsTable(db); 2786 2787 createAccountsDeletionTrigger(db); 2788 } 2789 2790 private void createSharedAccountsTable(SQLiteDatabase db) { 2791 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( " 2792 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 2793 + ACCOUNTS_NAME + " TEXT NOT NULL, " 2794 + ACCOUNTS_TYPE + " TEXT NOT NULL, " 2795 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); 2796 } 2797 2798 private void addOldAccountNameColumn(SQLiteDatabase db) { 2799 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME); 2800 } 2801 2802 private void createAccountsDeletionTrigger(SQLiteDatabase db) { 2803 db.execSQL("" 2804 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS 2805 + " BEGIN" 2806 + " DELETE FROM " + TABLE_AUTHTOKENS 2807 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 2808 + " DELETE FROM " + TABLE_EXTRAS 2809 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 2810 + " DELETE FROM " + TABLE_GRANTS 2811 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" 2812 + " END"); 2813 } 2814 2815 private void createGrantsTable(SQLiteDatabase db) { 2816 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " 2817 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " 2818 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " 2819 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " 2820 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE 2821 + "," + GRANTS_GRANTEE_UID + "))"); 2822 } 2823 2824 @Override 2825 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 2826 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); 2827 2828 if (oldVersion == 1) { 2829 // no longer need to do anything since the work is done 2830 // when upgrading from version 2 2831 oldVersion++; 2832 } 2833 2834 if (oldVersion == 2) { 2835 createGrantsTable(db); 2836 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); 2837 createAccountsDeletionTrigger(db); 2838 oldVersion++; 2839 } 2840 2841 if (oldVersion == 3) { 2842 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + 2843 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); 2844 oldVersion++; 2845 } 2846 2847 if (oldVersion == 4) { 2848 createSharedAccountsTable(db); 2849 oldVersion++; 2850 } 2851 2852 if (oldVersion == 5) { 2853 addOldAccountNameColumn(db); 2854 oldVersion++; 2855 } 2856 2857 if (oldVersion != newVersion) { 2858 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion); 2859 } 2860 } 2861 2862 @Override 2863 public void onOpen(SQLiteDatabase db) { 2864 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); 2865 } 2866 } 2867 2868 public IBinder onBind(Intent intent) { 2869 return asBinder(); 2870 } 2871 2872 /** 2873 * Searches array of arguments for the specified string 2874 * @param args array of argument strings 2875 * @param value value to search for 2876 * @return true if the value is contained in the array 2877 */ 2878 private static boolean scanArgs(String[] args, String value) { 2879 if (args != null) { 2880 for (String arg : args) { 2881 if (value.equals(arg)) { 2882 return true; 2883 } 2884 } 2885 } 2886 return false; 2887 } 2888 2889 @Override 2890 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 2891 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2892 != PackageManager.PERMISSION_GRANTED) { 2893 fout.println("Permission Denial: can't dump AccountsManager from from pid=" 2894 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 2895 + " without permission " + android.Manifest.permission.DUMP); 2896 return; 2897 } 2898 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); 2899 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); 2900 2901 final List<UserInfo> users = getUserManager().getUsers(); 2902 for (UserInfo user : users) { 2903 ipw.println("User " + user + ":"); 2904 ipw.increaseIndent(); 2905 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); 2906 ipw.println(); 2907 ipw.decreaseIndent(); 2908 } 2909 } 2910 2911 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, 2912 String[] args, boolean isCheckinRequest) { 2913 synchronized (userAccounts.cacheLock) { 2914 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); 2915 2916 if (isCheckinRequest) { 2917 // This is a checkin request. *Only* upload the account types and the count of each. 2918 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, 2919 null, null, ACCOUNTS_TYPE, null, null); 2920 try { 2921 while (cursor.moveToNext()) { 2922 // print type,count 2923 fout.println(cursor.getString(0) + "," + cursor.getString(1)); 2924 } 2925 } finally { 2926 if (cursor != null) { 2927 cursor.close(); 2928 } 2929 } 2930 } else { 2931 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, 2932 Process.myUid(), null); 2933 fout.println("Accounts: " + accounts.length); 2934 for (Account account : accounts) { 2935 fout.println(" " + account); 2936 } 2937 2938 fout.println(); 2939 synchronized (mSessions) { 2940 final long now = SystemClock.elapsedRealtime(); 2941 fout.println("Active Sessions: " + mSessions.size()); 2942 for (Session session : mSessions.values()) { 2943 fout.println(" " + session.toDebugString(now)); 2944 } 2945 } 2946 2947 fout.println(); 2948 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); 2949 } 2950 } 2951 } 2952 2953 private void doNotification(UserAccounts accounts, Account account, CharSequence message, 2954 Intent intent, int userId) { 2955 long identityToken = clearCallingIdentity(); 2956 try { 2957 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2958 Log.v(TAG, "doNotification: " + message + " intent:" + intent); 2959 } 2960 2961 if (intent.getComponent() != null && 2962 GrantCredentialsPermissionActivity.class.getName().equals( 2963 intent.getComponent().getClassName())) { 2964 createNoCredentialsPermissionNotification(account, intent, userId); 2965 } else { 2966 final Integer notificationId = getSigninRequiredNotificationId(accounts, account); 2967 intent.addCategory(String.valueOf(notificationId)); 2968 Notification n = new Notification(android.R.drawable.stat_sys_warning, null, 2969 0 /* when */); 2970 UserHandle user = new UserHandle(userId); 2971 final String notificationTitleFormat = 2972 mContext.getText(R.string.notification_title).toString(); 2973 n.color = mContext.getResources().getColor( 2974 com.android.internal.R.color.system_notification_accent_color); 2975 n.setLatestEventInfo(mContext, 2976 String.format(notificationTitleFormat, account.name), 2977 message, PendingIntent.getActivityAsUser( 2978 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, 2979 null, user)); 2980 installNotification(notificationId, n, user); 2981 } 2982 } finally { 2983 restoreCallingIdentity(identityToken); 2984 } 2985 } 2986 2987 protected void installNotification(final int notificationId, final Notification n, 2988 UserHandle user) { 2989 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 2990 .notifyAsUser(null, notificationId, n, user); 2991 } 2992 2993 protected void cancelNotification(int id, UserHandle user) { 2994 long identityToken = clearCallingIdentity(); 2995 try { 2996 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) 2997 .cancelAsUser(null, id, user); 2998 } finally { 2999 restoreCallingIdentity(identityToken); 3000 } 3001 } 3002 3003 /** Succeeds if any of the specified permissions are granted. */ 3004 private void checkBinderPermission(String... permissions) { 3005 final int uid = Binder.getCallingUid(); 3006 3007 for (String perm : permissions) { 3008 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { 3009 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3010 Log.v(TAG, " caller uid " + uid + " has " + perm); 3011 } 3012 return; 3013 } 3014 } 3015 3016 String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions); 3017 Log.w(TAG, " " + msg); 3018 throw new SecurityException(msg); 3019 } 3020 3021 private int handleIncomingUser(int userId) { 3022 try { 3023 return ActivityManagerNative.getDefault().handleIncomingUser( 3024 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); 3025 } catch (RemoteException re) { 3026 // Shouldn't happen, local. 3027 } 3028 return userId; 3029 } 3030 3031 private boolean isPrivileged(int callingUid) { 3032 final int callingUserId = UserHandle.getUserId(callingUid); 3033 3034 final PackageManager userPackageManager; 3035 try { 3036 userPackageManager = mContext.createPackageContextAsUser( 3037 "android", 0, new UserHandle(callingUserId)).getPackageManager(); 3038 } catch (NameNotFoundException e) { 3039 return false; 3040 } 3041 3042 String[] packages = userPackageManager.getPackagesForUid(callingUid); 3043 for (String name : packages) { 3044 try { 3045 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); 3046 if (packageInfo != null 3047 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { 3048 return true; 3049 } 3050 } catch (PackageManager.NameNotFoundException e) { 3051 return false; 3052 } 3053 } 3054 return false; 3055 } 3056 3057 private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { 3058 final boolean isPrivileged = isPrivileged(callerUid); 3059 final boolean fromAuthenticator = account != null 3060 && hasAuthenticatorUid(account.type, callerUid); 3061 final boolean hasExplicitGrants = account != null 3062 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); 3063 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3064 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " 3065 + callerUid + ", " + account 3066 + ": is authenticator? " + fromAuthenticator 3067 + ", has explicit permission? " + hasExplicitGrants); 3068 } 3069 return fromAuthenticator || hasExplicitGrants || isPrivileged; 3070 } 3071 3072 private boolean hasAuthenticatorUid(String accountType, int callingUid) { 3073 final int callingUserId = UserHandle.getUserId(callingUid); 3074 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : 3075 mAuthenticatorCache.getAllServices(callingUserId)) { 3076 if (serviceInfo.type.type.equals(accountType)) { 3077 return (serviceInfo.uid == callingUid) || 3078 (mPackageManager.checkSignatures(serviceInfo.uid, callingUid) 3079 == PackageManager.SIGNATURE_MATCH); 3080 } 3081 } 3082 return false; 3083 } 3084 3085 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, 3086 int callerUid) { 3087 if (callerUid == Process.SYSTEM_UID) { 3088 return true; 3089 } 3090 UserAccounts accounts = getUserAccountsForCaller(); 3091 synchronized (accounts.cacheLock) { 3092 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 3093 String[] args = { String.valueOf(callerUid), authTokenType, 3094 account.name, account.type}; 3095 final boolean permissionGranted = 3096 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; 3097 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { 3098 // TODO: Skip this check when running automated tests. Replace this 3099 // with a more general solution. 3100 Log.d(TAG, "no credentials permission for usage of " + account + ", " 3101 + authTokenType + " by uid " + callerUid 3102 + " but ignoring since device is in test harness."); 3103 return true; 3104 } 3105 return permissionGranted; 3106 } 3107 } 3108 3109 private void checkCallingUidAgainstAuthenticator(Account account) { 3110 final int uid = Binder.getCallingUid(); 3111 if (account == null || !hasAuthenticatorUid(account.type, uid)) { 3112 String msg = "caller uid " + uid + " is different than the authenticator's uid"; 3113 Log.w(TAG, msg); 3114 throw new SecurityException(msg); 3115 } 3116 if (Log.isLoggable(TAG, Log.VERBOSE)) { 3117 Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid"); 3118 } 3119 } 3120 3121 private void checkAuthenticateAccountsPermission(Account account) { 3122 checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS); 3123 checkCallingUidAgainstAuthenticator(account); 3124 } 3125 3126 private void checkReadAccountsPermission() { 3127 checkBinderPermission(Manifest.permission.GET_ACCOUNTS); 3128 } 3129 3130 private void checkManageAccountsPermission() { 3131 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS); 3132 } 3133 3134 private void checkManageAccountsOrUseCredentialsPermissions() { 3135 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS, 3136 Manifest.permission.USE_CREDENTIALS); 3137 } 3138 3139 private boolean canUserModifyAccounts(int userId) { 3140 if (getUserManager().getUserRestrictions(new UserHandle(userId)) 3141 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { 3142 return false; 3143 } 3144 return true; 3145 } 3146 3147 private boolean canUserModifyAccountsForType(int userId, String accountType) { 3148 DevicePolicyManager dpm = (DevicePolicyManager) mContext 3149 .getSystemService(Context.DEVICE_POLICY_SERVICE); 3150 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 3151 if (typesArray == null) { 3152 return true; 3153 } 3154 for (String forbiddenType : typesArray) { 3155 if (forbiddenType.equals(accountType)) { 3156 return false; 3157 } 3158 } 3159 return true; 3160 } 3161 3162 @Override 3163 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) 3164 throws RemoteException { 3165 final int callingUid = getCallingUid(); 3166 3167 if (callingUid != Process.SYSTEM_UID) { 3168 throw new SecurityException(); 3169 } 3170 3171 if (value) { 3172 grantAppPermission(account, authTokenType, uid); 3173 } else { 3174 revokeAppPermission(account, authTokenType, uid); 3175 } 3176 } 3177 3178 /** 3179 * Allow callers with the given uid permission to get credentials for account/authTokenType. 3180 * <p> 3181 * Although this is public it can only be accessed via the AccountManagerService object 3182 * which is in the system. This means we don't need to protect it with permissions. 3183 * @hide 3184 */ 3185 private void grantAppPermission(Account account, String authTokenType, int uid) { 3186 if (account == null || authTokenType == null) { 3187 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); 3188 return; 3189 } 3190 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 3191 synchronized (accounts.cacheLock) { 3192 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3193 db.beginTransaction(); 3194 try { 3195 long accountId = getAccountIdLocked(db, account); 3196 if (accountId >= 0) { 3197 ContentValues values = new ContentValues(); 3198 values.put(GRANTS_ACCOUNTS_ID, accountId); 3199 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); 3200 values.put(GRANTS_GRANTEE_UID, uid); 3201 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); 3202 db.setTransactionSuccessful(); 3203 } 3204 } finally { 3205 db.endTransaction(); 3206 } 3207 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 3208 new UserHandle(accounts.userId)); 3209 } 3210 } 3211 3212 /** 3213 * Don't allow callers with the given uid permission to get credentials for 3214 * account/authTokenType. 3215 * <p> 3216 * Although this is public it can only be accessed via the AccountManagerService object 3217 * which is in the system. This means we don't need to protect it with permissions. 3218 * @hide 3219 */ 3220 private void revokeAppPermission(Account account, String authTokenType, int uid) { 3221 if (account == null || authTokenType == null) { 3222 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); 3223 return; 3224 } 3225 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); 3226 synchronized (accounts.cacheLock) { 3227 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); 3228 db.beginTransaction(); 3229 try { 3230 long accountId = getAccountIdLocked(db, account); 3231 if (accountId >= 0) { 3232 db.delete(TABLE_GRANTS, 3233 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " 3234 + GRANTS_GRANTEE_UID + "=?", 3235 new String[]{String.valueOf(accountId), authTokenType, 3236 String.valueOf(uid)}); 3237 db.setTransactionSuccessful(); 3238 } 3239 } finally { 3240 db.endTransaction(); 3241 } 3242 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), 3243 new UserHandle(accounts.userId)); 3244 } 3245 } 3246 3247 static final private String stringArrayToString(String[] value) { 3248 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; 3249 } 3250 3251 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { 3252 final Account[] oldAccountsForType = accounts.accountCache.get(account.type); 3253 if (oldAccountsForType != null) { 3254 ArrayList<Account> newAccountsList = new ArrayList<Account>(); 3255 for (Account curAccount : oldAccountsForType) { 3256 if (!curAccount.equals(account)) { 3257 newAccountsList.add(curAccount); 3258 } 3259 } 3260 if (newAccountsList.isEmpty()) { 3261 accounts.accountCache.remove(account.type); 3262 } else { 3263 Account[] newAccountsForType = new Account[newAccountsList.size()]; 3264 newAccountsForType = newAccountsList.toArray(newAccountsForType); 3265 accounts.accountCache.put(account.type, newAccountsForType); 3266 } 3267 } 3268 accounts.userDataCache.remove(account); 3269 accounts.authTokenCache.remove(account); 3270 accounts.previousNameCache.remove(account); 3271 } 3272 3273 /** 3274 * This assumes that the caller has already checked that the account is not already present. 3275 */ 3276 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { 3277 Account[] accountsForType = accounts.accountCache.get(account.type); 3278 int oldLength = (accountsForType != null) ? accountsForType.length : 0; 3279 Account[] newAccountsForType = new Account[oldLength + 1]; 3280 if (accountsForType != null) { 3281 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); 3282 } 3283 newAccountsForType[oldLength] = account; 3284 accounts.accountCache.put(account.type, newAccountsForType); 3285 } 3286 3287 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, 3288 int callingUid, String callingPackage) { 3289 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 3290 || callingUid == Process.myUid()) { 3291 return unfiltered; 3292 } 3293 UserInfo user = mUserManager.getUserInfo(userAccounts.userId); 3294 if (user != null && user.isRestricted()) { 3295 String[] packages = mPackageManager.getPackagesForUid(callingUid); 3296 // If any of the packages is a white listed package, return the full set, 3297 // otherwise return non-shared accounts only. 3298 // This might be a temporary way to specify a whitelist 3299 String whiteList = mContext.getResources().getString( 3300 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts); 3301 for (String packageName : packages) { 3302 if (whiteList.contains(";" + packageName + ";")) { 3303 return unfiltered; 3304 } 3305 } 3306 ArrayList<Account> allowed = new ArrayList<Account>(); 3307 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); 3308 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; 3309 String requiredAccountType = ""; 3310 try { 3311 // If there's an explicit callingPackage specified, check if that package 3312 // opted in to see restricted accounts. 3313 if (callingPackage != null) { 3314 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0); 3315 if (pi != null && pi.restrictedAccountType != null) { 3316 requiredAccountType = pi.restrictedAccountType; 3317 } 3318 } else { 3319 // Otherwise check if the callingUid has a package that has opted in 3320 for (String packageName : packages) { 3321 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 3322 if (pi != null && pi.restrictedAccountType != null) { 3323 requiredAccountType = pi.restrictedAccountType; 3324 break; 3325 } 3326 } 3327 } 3328 } catch (NameNotFoundException nnfe) { 3329 } 3330 for (Account account : unfiltered) { 3331 if (account.type.equals(requiredAccountType)) { 3332 allowed.add(account); 3333 } else { 3334 boolean found = false; 3335 for (Account shared : sharedAccounts) { 3336 if (shared.equals(account)) { 3337 found = true; 3338 break; 3339 } 3340 } 3341 if (!found) { 3342 allowed.add(account); 3343 } 3344 } 3345 } 3346 Account[] filtered = new Account[allowed.size()]; 3347 allowed.toArray(filtered); 3348 return filtered; 3349 } else { 3350 return unfiltered; 3351 } 3352 } 3353 3354 /* 3355 * packageName can be null. If not null, it should be used to filter out restricted accounts 3356 * that the package is not allowed to access. 3357 */ 3358 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, 3359 int callingUid, String callingPackage) { 3360 if (accountType != null) { 3361 final Account[] accounts = userAccounts.accountCache.get(accountType); 3362 if (accounts == null) { 3363 return EMPTY_ACCOUNT_ARRAY; 3364 } else { 3365 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), 3366 callingUid, callingPackage); 3367 } 3368 } else { 3369 int totalLength = 0; 3370 for (Account[] accounts : userAccounts.accountCache.values()) { 3371 totalLength += accounts.length; 3372 } 3373 if (totalLength == 0) { 3374 return EMPTY_ACCOUNT_ARRAY; 3375 } 3376 Account[] accounts = new Account[totalLength]; 3377 totalLength = 0; 3378 for (Account[] accountsOfType : userAccounts.accountCache.values()) { 3379 System.arraycopy(accountsOfType, 0, accounts, totalLength, 3380 accountsOfType.length); 3381 totalLength += accountsOfType.length; 3382 } 3383 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); 3384 } 3385 } 3386 3387 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 3388 Account account, String key, String value) { 3389 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 3390 if (userDataForAccount == null) { 3391 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 3392 accounts.userDataCache.put(account, userDataForAccount); 3393 } 3394 if (value == null) { 3395 userDataForAccount.remove(key); 3396 } else { 3397 userDataForAccount.put(key, value); 3398 } 3399 } 3400 3401 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, 3402 Account account, String key, String value) { 3403 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 3404 if (authTokensForAccount == null) { 3405 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 3406 accounts.authTokenCache.put(account, authTokensForAccount); 3407 } 3408 if (value == null) { 3409 authTokensForAccount.remove(key); 3410 } else { 3411 authTokensForAccount.put(key, value); 3412 } 3413 } 3414 3415 protected String readAuthTokenInternal(UserAccounts accounts, Account account, 3416 String authTokenType) { 3417 synchronized (accounts.cacheLock) { 3418 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); 3419 if (authTokensForAccount == null) { 3420 // need to populate the cache for this account 3421 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 3422 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); 3423 accounts.authTokenCache.put(account, authTokensForAccount); 3424 } 3425 return authTokensForAccount.get(authTokenType); 3426 } 3427 } 3428 3429 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) { 3430 synchronized (accounts.cacheLock) { 3431 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); 3432 if (userDataForAccount == null) { 3433 // need to populate the cache for this account 3434 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); 3435 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); 3436 accounts.userDataCache.put(account, userDataForAccount); 3437 } 3438 return userDataForAccount.get(key); 3439 } 3440 } 3441 3442 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( 3443 final SQLiteDatabase db, Account account) { 3444 HashMap<String, String> userDataForAccount = new HashMap<String, String>(); 3445 Cursor cursor = db.query(TABLE_EXTRAS, 3446 COLUMNS_EXTRAS_KEY_AND_VALUE, 3447 SELECTION_USERDATA_BY_ACCOUNT, 3448 new String[]{account.name, account.type}, 3449 null, null, null); 3450 try { 3451 while (cursor.moveToNext()) { 3452 final String tmpkey = cursor.getString(0); 3453 final String value = cursor.getString(1); 3454 userDataForAccount.put(tmpkey, value); 3455 } 3456 } finally { 3457 cursor.close(); 3458 } 3459 return userDataForAccount; 3460 } 3461 3462 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( 3463 final SQLiteDatabase db, Account account) { 3464 HashMap<String, String> authTokensForAccount = new HashMap<String, String>(); 3465 Cursor cursor = db.query(TABLE_AUTHTOKENS, 3466 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, 3467 SELECTION_AUTHTOKENS_BY_ACCOUNT, 3468 new String[]{account.name, account.type}, 3469 null, null, null); 3470 try { 3471 while (cursor.moveToNext()) { 3472 final String type = cursor.getString(0); 3473 final String authToken = cursor.getString(1); 3474 authTokensForAccount.put(type, authToken); 3475 } 3476 } finally { 3477 cursor.close(); 3478 } 3479 return authTokensForAccount; 3480 } 3481} 3482