blob: ac2f5b0db588e592d7e15a15f9d75053f8aaff16 [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.accounts;
Fred Quintana60307342009-03-24 22:48:12 -070018
Doug Zongker885cfc232009-10-21 16:52:44 -070019import android.Manifest;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080020import android.accounts.Account;
21import android.accounts.AccountAndUser;
22import android.accounts.AccountAuthenticatorResponse;
23import android.accounts.AccountManager;
24import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070025import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.accounts.GrantCredentialsPermissionActivity;
27import android.accounts.IAccountAuthenticator;
28import android.accounts.IAccountAuthenticatorResponse;
29import android.accounts.IAccountManager;
30import android.accounts.IAccountManagerResponse;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080031import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070032import android.app.ActivityManagerNative;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070033import android.app.AppGlobals;
Doug Zongker885cfc232009-10-21 16:52:44 -070034import android.app.Notification;
35import android.app.NotificationManager;
36import android.app.PendingIntent;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010037import android.app.admin.DevicePolicyManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070038import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070039import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070040import android.content.ContentValues;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
Fred Quintanab839afc2009-10-14 15:57:28 -070044import android.content.ServiceConnection;
Doug Zongker885cfc232009-10-21 16:52:44 -070045import android.content.pm.ApplicationInfo;
46import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070047import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070048import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070049import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070050import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070051import android.content.pm.ResolveInfo;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070052import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070053import android.database.Cursor;
54import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070055import android.database.sqlite.SQLiteDatabase;
56import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070057import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070059import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080060import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070061import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070062import android.os.IBinder;
63import android.os.Looper;
64import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070065import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070066import android.os.Process;
Fred Quintanaa698f422009-04-08 19:14:54 -070067import android.os.RemoteException;
68import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070069import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070070import android.os.UserManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070071import android.text.TextUtils;
72import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070073import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070074import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080075import android.util.SparseArray;
Fred Quintana60307342009-03-24 22:48:12 -070076
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070077import com.android.internal.R;
Amith Yamasani67df64b2012-12-14 12:09:36 -080078import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080079import com.android.internal.util.IndentingPrintWriter;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070080import com.android.server.FgThread;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070081import com.google.android.collect.Lists;
82import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070083
Oscar Montemayora8529f62009-11-18 10:14:20 -080084import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -070085import java.io.FileDescriptor;
86import java.io.PrintWriter;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070087import java.sql.Timestamp;
88import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -070089import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -080090import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -070091import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070092import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070093import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070094import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -080095import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070096import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -080097import java.util.Map;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070098import java.util.concurrent.atomic.AtomicInteger;
99import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700100
Fred Quintana60307342009-03-24 22:48:12 -0700101/**
102 * A system service that provides account, password, and authtoken management for all
103 * accounts on the device. Some of these calls are implemented with the help of the corresponding
104 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
105 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700106 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700107 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700108 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700109public class AccountManagerService
110 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800111 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700112 private static final String TAG = "AccountManagerService";
113
114 private static final int TIMEOUT_DELAY_MS = 1000 * 60;
115 private static final String DATABASE_NAME = "accounts.db";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700116 private static final int DATABASE_VERSION = 8;
117
118 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700119
120 private final Context mContext;
121
Fred Quintana56285a62010-12-02 14:20:51 -0800122 private final PackageManager mPackageManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700123 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800124
Fred Quintana60307342009-03-24 22:48:12 -0700125 private final MessageHandler mMessageHandler;
126
127 // Messages that can be sent on mHandler
128 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700129 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700130
Fred Quintana56285a62010-12-02 14:20:51 -0800131 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700132
133 private static final String TABLE_ACCOUNTS = "accounts";
134 private static final String ACCOUNTS_ID = "_id";
135 private static final String ACCOUNTS_NAME = "name";
136 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700137 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700138 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700139 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800140 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
141 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700142
143 private static final String TABLE_AUTHTOKENS = "authtokens";
144 private static final String AUTHTOKENS_ID = "_id";
145 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
146 private static final String AUTHTOKENS_TYPE = "type";
147 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
148
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700149 private static final String TABLE_GRANTS = "grants";
150 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
151 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
152 private static final String GRANTS_GRANTEE_UID = "uid";
153
Fred Quintana60307342009-03-24 22:48:12 -0700154 private static final String TABLE_EXTRAS = "extras";
155 private static final String EXTRAS_ID = "_id";
156 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
157 private static final String EXTRAS_KEY = "key";
158 private static final String EXTRAS_VALUE = "value";
159
160 private static final String TABLE_META = "meta";
161 private static final String META_KEY = "key";
162 private static final String META_VALUE = "value";
163
Amith Yamasani67df64b2012-12-14 12:09:36 -0800164 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
165
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700166 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
167 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700168 private static final Intent ACCOUNTS_CHANGED_INTENT;
Fred Quintanaa698f422009-04-08 19:14:54 -0700169
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700170 private static final String COUNT_OF_MATCHING_GRANTS = ""
171 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
172 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
173 + " AND " + GRANTS_GRANTEE_UID + "=?"
174 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
175 + " AND " + ACCOUNTS_NAME + "=?"
176 + " AND " + ACCOUNTS_TYPE + "=?";
177
Fred Quintana56285a62010-12-02 14:20:51 -0800178 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
179 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
180 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
181 AUTHTOKENS_AUTHTOKEN};
182
183 private static final String SELECTION_USERDATA_BY_ACCOUNT =
184 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
185 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
186
Fred Quintanaa698f422009-04-08 19:14:54 -0700187 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700188 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
189
Amith Yamasani04e0d262012-02-14 11:50:53 -0800190 static class UserAccounts {
191 private final int userId;
192 private final DatabaseHelper openHelper;
193 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
194 credentialsPermissionNotificationIds =
195 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
196 private final HashMap<Account, Integer> signinRequiredNotificationIds =
197 new HashMap<Account, Integer>();
198 private final Object cacheLock = new Object();
199 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700200 private final HashMap<String, Account[]> accountCache =
201 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800202 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800203 private final HashMap<Account, HashMap<String, String>> userDataCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800204 new HashMap<Account, HashMap<String, String>>();
205 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800206 private final HashMap<Account, HashMap<String, String>> authTokenCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800207 new HashMap<Account, HashMap<String, String>>();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700208 /**
209 * protected by the {@link #cacheLock}
210 *
211 * Caches the previous names associated with an account. Previous names
212 * should be cached because we expect that when an Account is renamed,
213 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
214 * want to know if the accounts they care about have been renamed.
215 *
216 * The previous names are wrapped in an {@link AtomicReference} so that
217 * we can distinguish between those accounts with no previous names and
218 * those whose previous names haven't been cached (yet).
219 */
220 private final HashMap<Account, AtomicReference<String>> previousNameCache =
221 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800222
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700223 private int debugDbInsertionPoint = -1;
224 private SQLiteStatement statementForLogging;
225
Amith Yamasani04e0d262012-02-14 11:50:53 -0800226 UserAccounts(Context context, int userId) {
227 this.userId = userId;
228 synchronized (cacheLock) {
229 openHelper = new DatabaseHelper(context, userId);
230 }
231 }
232 }
233
234 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
235
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700236 private static AtomicReference<AccountManagerService> sThis =
237 new AtomicReference<AccountManagerService>();
Fred Quintana31957f12009-10-21 13:43:10 -0700238 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700239
240 static {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -0700241 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
Fred Quintana7be59642009-08-24 18:29:25 -0700242 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
243 }
244
Fred Quintana56285a62010-12-02 14:20:51 -0800245
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700246 /**
247 * This should only be called by system code. One should only call this after the service
248 * has started.
249 * @return a reference to the AccountManagerService instance
250 * @hide
251 */
252 public static AccountManagerService getSingleton() {
253 return sThis.get();
254 }
Fred Quintana60307342009-03-24 22:48:12 -0700255
Fred Quintana56285a62010-12-02 14:20:51 -0800256 public AccountManagerService(Context context) {
257 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700258 }
259
Fred Quintana56285a62010-12-02 14:20:51 -0800260 public AccountManagerService(Context context, PackageManager packageManager,
261 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700262 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800263 mPackageManager = packageManager;
Fred Quintana60307342009-03-24 22:48:12 -0700264
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700265 mMessageHandler = new MessageHandler(FgThread.get().getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700266
Fred Quintana56285a62010-12-02 14:20:51 -0800267 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800268 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700269
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700270 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800271
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800272 IntentFilter intentFilter = new IntentFilter();
273 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
274 intentFilter.addDataScheme("package");
275 mContext.registerReceiver(new BroadcastReceiver() {
276 @Override
277 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700278 // Don't delete accounts when updating a authenticator's
279 // package.
280 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
281 purgeOldGrantsAll();
282 }
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800283 }
284 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800285
Amith Yamasani13593602012-03-22 16:16:17 -0700286 IntentFilter userFilter = new IntentFilter();
287 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800288 userFilter.addAction(Intent.ACTION_USER_STARTED);
289 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700290 @Override
291 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800292 String action = intent.getAction();
293 if (Intent.ACTION_USER_REMOVED.equals(action)) {
294 onUserRemoved(intent);
295 } else if (Intent.ACTION_USER_STARTED.equals(action)) {
296 onUserStarted(intent);
297 }
Amith Yamasani13593602012-03-22 16:16:17 -0700298 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800299 }, UserHandle.ALL, userFilter, null, null);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800300 }
301
Dianne Hackborn164371f2013-10-01 19:10:13 -0700302 @Override
303 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
304 throws RemoteException {
305 try {
306 return super.onTransact(code, data, reply, flags);
307 } catch (RuntimeException e) {
308 // The account manager only throws security exceptions, so let's
309 // log all others.
310 if (!(e instanceof SecurityException)) {
311 Slog.wtf(TAG, "Account Manager Crash", e);
312 }
313 throw e;
314 }
315 }
316
Kenny Root26ff6622012-07-30 12:58:03 -0700317 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -0700318 }
319
Amith Yamasani258848d2012-08-10 17:06:33 -0700320 private UserManager getUserManager() {
321 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700322 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700323 }
324 return mUserManager;
325 }
326
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700327 /* Caller should lock mUsers */
328 private UserAccounts initUserLocked(int userId) {
329 UserAccounts accounts = mUsers.get(userId);
330 if (accounts == null) {
331 accounts = new UserAccounts(mContext, userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700332 initializeDebugDbSizeAndCompileSqlStatementForLogging(
333 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700334 mUsers.append(userId, accounts);
335 purgeOldGrants(accounts);
336 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800337 }
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700338 return accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800339 }
340
341 private void purgeOldGrantsAll() {
342 synchronized (mUsers) {
343 for (int i = 0; i < mUsers.size(); i++) {
344 purgeOldGrants(mUsers.valueAt(i));
345 }
346 }
347 }
348
349 private void purgeOldGrants(UserAccounts accounts) {
350 synchronized (accounts.cacheLock) {
351 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800352 final Cursor cursor = db.query(TABLE_GRANTS,
353 new String[]{GRANTS_GRANTEE_UID},
354 null, null, GRANTS_GRANTEE_UID, null, null);
355 try {
356 while (cursor.moveToNext()) {
357 final int uid = cursor.getInt(0);
358 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
359 if (packageExists) {
360 continue;
361 }
362 Log.d(TAG, "deleting grants for UID " + uid
363 + " because its package is no longer installed");
364 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
365 new String[]{Integer.toString(uid)});
366 }
367 } finally {
368 cursor.close();
369 }
370 }
371 }
372
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700373 /**
374 * Validate internal set of accounts against installed authenticators for
375 * given user. Clears cached authenticators before validating.
376 */
377 public void validateAccounts(int userId) {
378 final UserAccounts accounts = getUserAccounts(userId);
379
380 // Invalidate user-specific cache to make sure we catch any
381 // removed authenticators.
382 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
383 }
384
385 /**
386 * Validate internal set of accounts against installed authenticators for
387 * given user. Clear cached authenticators before validating when requested.
388 */
389 private void validateAccountsInternal(
390 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
391 if (invalidateAuthenticatorCache) {
392 mAuthenticatorCache.invalidateCache(accounts.userId);
393 }
394
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700395 final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
396 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
397 mAuthenticatorCache.getAllServices(accounts.userId)) {
398 knownAuth.add(service.type);
399 }
400
Amith Yamasani04e0d262012-02-14 11:50:53 -0800401 synchronized (accounts.cacheLock) {
402 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800403 boolean accountDeleted = false;
404 Cursor cursor = db.query(TABLE_ACCOUNTS,
405 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
Marvin Paul48fcd4e2014-12-01 18:26:07 -0800406 null, null, null, null, ACCOUNTS_ID);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800407 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800408 accounts.accountCache.clear();
Fred Quintana56285a62010-12-02 14:20:51 -0800409 final HashMap<String, ArrayList<String>> accountNamesByType =
Amith Yamasanib483a992012-05-22 13:14:25 -0700410 new LinkedHashMap<String, ArrayList<String>>();
Fred Quintana56285a62010-12-02 14:20:51 -0800411 while (cursor.moveToNext()) {
412 final long accountId = cursor.getLong(0);
413 final String accountType = cursor.getString(1);
414 final String accountName = cursor.getString(2);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700415
416 if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700417 Slog.w(TAG, "deleting account " + accountName + " because type "
Fred Quintana56285a62010-12-02 14:20:51 -0800418 + accountType + " no longer has a registered authenticator");
419 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
420 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700421
422 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
423 accountId, accounts);
424
Fred Quintana56285a62010-12-02 14:20:51 -0800425 final Account account = new Account(accountName, accountType);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800426 accounts.userDataCache.remove(account);
427 accounts.authTokenCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -0800428 } else {
429 ArrayList<String> accountNames = accountNamesByType.get(accountType);
430 if (accountNames == null) {
431 accountNames = new ArrayList<String>();
432 accountNamesByType.put(accountType, accountNames);
433 }
434 accountNames.add(accountName);
435 }
436 }
Andy McFadden2f362292012-01-20 14:43:38 -0800437 for (Map.Entry<String, ArrayList<String>> cur
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800438 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800439 final String accountType = cur.getKey();
440 final ArrayList<String> accountNames = cur.getValue();
441 final Account[] accountsForType = new Account[accountNames.size()];
442 int i = 0;
443 for (String accountName : accountNames) {
444 accountsForType[i] = new Account(accountName, accountType);
445 ++i;
446 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800447 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800448 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800449 } finally {
450 cursor.close();
451 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800452 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800453 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800454 }
455 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700456 }
457
Amith Yamasani04e0d262012-02-14 11:50:53 -0800458 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700459 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -0800460 }
461
462 protected UserAccounts getUserAccounts(int userId) {
463 synchronized (mUsers) {
464 UserAccounts accounts = mUsers.get(userId);
465 if (accounts == null) {
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700466 accounts = initUserLocked(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800467 mUsers.append(userId, accounts);
468 }
469 return accounts;
470 }
471 }
472
Amith Yamasani13593602012-03-22 16:16:17 -0700473 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700474 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -0700475 if (userId < 1) return;
476
477 UserAccounts accounts;
478 synchronized (mUsers) {
479 accounts = mUsers.get(userId);
480 mUsers.remove(userId);
481 }
482 if (accounts == null) {
483 File dbFile = new File(getDatabaseName(userId));
484 dbFile.delete();
485 return;
486 }
487
488 synchronized (accounts.cacheLock) {
489 accounts.openHelper.close();
490 File dbFile = new File(getDatabaseName(userId));
491 dbFile.delete();
492 }
493 }
494
Amith Yamasani67df64b2012-12-14 12:09:36 -0800495 private void onUserStarted(Intent intent) {
496 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
497 if (userId < 1) return;
498
499 // Check if there's a shared account that needs to be created as an account
500 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
501 if (sharedAccounts == null || sharedAccounts.length == 0) return;
502 Account[] accounts = getAccountsAsUser(null, userId);
503 for (Account sa : sharedAccounts) {
504 if (ArrayUtils.contains(accounts, sa)) continue;
505 // Account doesn't exist. Copy it now.
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000506 copyAccountToUser(null /*no response*/, sa, UserHandle.USER_OWNER, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800507 }
508 }
509
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700510 @Override
511 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700512 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -0700513 }
514
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800515 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -0700516 public String getPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -0800517 if (Log.isLoggable(TAG, Log.VERBOSE)) {
518 Log.v(TAG, "getPassword: " + account
519 + ", caller's uid " + Binder.getCallingUid()
520 + ", pid " + Binder.getCallingPid());
521 }
Fred Quintana382601f2010-03-25 12:25:10 -0700522 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700523 checkAuthenticateAccountsPermission(account);
524
Amith Yamasani04e0d262012-02-14 11:50:53 -0800525 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700526 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700527 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800528 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700529 } finally {
530 restoreCallingIdentity(identityToken);
531 }
532 }
533
Amith Yamasani04e0d262012-02-14 11:50:53 -0800534 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700535 if (account == null) {
536 return null;
537 }
538
Amith Yamasani04e0d262012-02-14 11:50:53 -0800539 synchronized (accounts.cacheLock) {
540 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800541 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
542 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
543 new String[]{account.name, account.type}, null, null, null);
544 try {
545 if (cursor.moveToNext()) {
546 return cursor.getString(0);
547 }
548 return null;
549 } finally {
550 cursor.close();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700551 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700552 }
553 }
554
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800555 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700556 public String getPreviousName(Account account) {
557 if (Log.isLoggable(TAG, Log.VERBOSE)) {
558 Log.v(TAG, "getPreviousName: " + account
559 + ", caller's uid " + Binder.getCallingUid()
560 + ", pid " + Binder.getCallingPid());
561 }
562 if (account == null) throw new IllegalArgumentException("account is null");
563 UserAccounts accounts = getUserAccountsForCaller();
564 long identityToken = clearCallingIdentity();
565 try {
566 return readPreviousNameInternal(accounts, account);
567 } finally {
568 restoreCallingIdentity(identityToken);
569 }
570 }
571
572 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
573 if (account == null) {
574 return null;
575 }
576 synchronized (accounts.cacheLock) {
577 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
578 if (previousNameRef == null) {
579 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
580 Cursor cursor = db.query(
581 TABLE_ACCOUNTS,
582 new String[]{ ACCOUNTS_PREVIOUS_NAME },
583 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
584 new String[] { account.name, account.type },
585 null,
586 null,
587 null);
588 try {
589 if (cursor.moveToNext()) {
590 String previousName = cursor.getString(0);
591 previousNameRef = new AtomicReference<String>(previousName);
592 accounts.previousNameCache.put(account, previousNameRef);
593 return previousName;
594 } else {
595 return null;
596 }
597 } finally {
598 cursor.close();
599 }
600 } else {
601 return previousNameRef.get();
602 }
603 }
604 }
605
606 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700607 public String getUserData(Account account, String key) {
Fred Quintana56285a62010-12-02 14:20:51 -0800608 if (Log.isLoggable(TAG, Log.VERBOSE)) {
609 Log.v(TAG, "getUserData: " + account
610 + ", key " + key
611 + ", caller's uid " + Binder.getCallingUid()
612 + ", pid " + Binder.getCallingPid());
613 }
Fred Quintana382601f2010-03-25 12:25:10 -0700614 if (account == null) throw new IllegalArgumentException("account is null");
615 if (key == null) throw new IllegalArgumentException("key is null");
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700616 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800617 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700618 long identityToken = clearCallingIdentity();
619 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800620 return readUserDataInternal(accounts, account, key);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700621 } finally {
622 restoreCallingIdentity(identityToken);
623 }
624 }
625
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800626 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100627 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -0800628 if (Log.isLoggable(TAG, Log.VERBOSE)) {
629 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100630 + "for user id " + userId
Fred Quintana56285a62010-12-02 14:20:51 -0800631 + "caller's uid " + Binder.getCallingUid()
632 + ", pid " + Binder.getCallingPid());
633 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100634 // Only allow the system process to read accounts of other users
635 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
636 + " trying get authenticator types for " + userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700637 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700638 try {
Fred Quintana97889762009-06-15 12:29:24 -0700639 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700640 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
Fred Quintana97889762009-06-15 12:29:24 -0700641 AuthenticatorDescription[] types =
642 new AuthenticatorDescription[authenticatorCollection.size()];
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700643 int i = 0;
Fred Quintana97889762009-06-15 12:29:24 -0700644 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
Fred Quintana718d8a22009-04-29 17:53:20 -0700645 : authenticatorCollection) {
646 types[i] = authenticator.type;
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700647 i++;
648 }
649 return types;
650 } finally {
651 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700652 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700653 }
654
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100655 private void enforceCrossUserPermission(int userId, String errorMessage) {
656 if (userId != UserHandle.getCallingUserId()
657 && Binder.getCallingUid() != Process.myUid()
658 && mContext.checkCallingOrSelfPermission(
659 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
660 != PackageManager.PERMISSION_GRANTED) {
661 throw new SecurityException(errorMessage);
662 }
663 }
664
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700665 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -0700666 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Fred Quintana56285a62010-12-02 14:20:51 -0800667 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700668 Log.v(TAG, "addAccountExplicitly: " + account
Fred Quintana56285a62010-12-02 14:20:51 -0800669 + ", caller's uid " + Binder.getCallingUid()
670 + ", pid " + Binder.getCallingPid());
671 }
Fred Quintana382601f2010-03-25 12:25:10 -0700672 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700673 checkAuthenticateAccountsPermission(account);
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700674 /*
675 * Child users are not allowed to add accounts. Only the accounts that are
676 * shared by the parent profile can be added to child profile.
677 *
678 * TODO: Only allow accounts that were shared to be added by
679 * a limited user.
680 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700681
Amith Yamasani04e0d262012-02-14 11:50:53 -0800682 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana60307342009-03-24 22:48:12 -0700683 // fails if the account already exists
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700684 int uid = getCallingUid();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700685 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700686 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700687 return addAccountInternal(accounts, account, password, extras, false, uid);
Fred Quintana60307342009-03-24 22:48:12 -0700688 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700689 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700690 }
691 }
692
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000693 @Override
694 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
695 int userFrom, int userTo) {
696 enforceCrossUserPermission(UserHandle.USER_ALL, "Calling copyAccountToUser requires "
697 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800698 final UserAccounts fromAccounts = getUserAccounts(userFrom);
699 final UserAccounts toAccounts = getUserAccounts(userTo);
700 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000701 if (response != null) {
702 Bundle result = new Bundle();
703 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
704 try {
705 response.onResult(result);
706 } catch (RemoteException e) {
707 Slog.w(TAG, "Failed to report error back to the client." + e);
708 }
709 }
710 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800711 }
712
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000713 Slog.d(TAG, "Copying account " + account.name
714 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800715 long identityToken = clearCallingIdentity();
716 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000717 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800718 false /* stripAuthTokenFromResult */, account.name,
719 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700720 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800721 protected String toDebugString(long now) {
722 return super.toDebugString(now) + ", getAccountCredentialsForClone"
723 + ", " + account.type;
724 }
725
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700726 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800727 public void run() throws RemoteException {
728 mAuthenticator.getAccountCredentialsForCloning(this, account);
729 }
730
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700731 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800732 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000733 if (result != null
734 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
735 // Create a Session for the target user and pass in the bundle
736 completeCloningAccount(response, result, account, toAccounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800737 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800738 super.onResult(result);
739 }
740 }
741 }.bind();
742 } finally {
743 restoreCallingIdentity(identityToken);
744 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800745 }
746
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800747 @Override
748 public boolean accountAuthenticated(final Account account) {
749 if (account == null) {
750 throw new IllegalArgumentException("account is null");
751 }
752 checkAuthenticateAccountsPermission(account);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800753 int userId = Binder.getCallingUserHandle().getIdentifier();
754 if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
755 return false;
756 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -0700757 return updateLastAuthenticatedTime(account);
758 }
759
760 private boolean updateLastAuthenticatedTime(Account account) {
761 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800762 synchronized (accounts.cacheLock) {
763 final ContentValues values = new ContentValues();
764 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
765 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
766 int i = db.update(
767 TABLE_ACCOUNTS,
768 values,
769 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
770 new String[] {
771 account.name, account.type
772 });
773 if (i > 0) {
774 return true;
775 }
776 }
777 return false;
778 }
779
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000780 private void completeCloningAccount(IAccountManagerResponse response,
781 final Bundle accountCredentials, final Account account, final UserAccounts targetUser) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800782 long id = clearCallingIdentity();
783 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000784 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800785 false /* stripAuthTokenFromResult */, account.name,
786 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700787 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800788 protected String toDebugString(long now) {
789 return super.toDebugString(now) + ", getAccountCredentialsForClone"
790 + ", " + account.type;
791 }
792
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700793 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800794 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700795 // Confirm that the owner's account still exists before this step.
796 UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER);
797 synchronized (owner.cacheLock) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000798 for (Account acc : getAccounts(UserHandle.USER_OWNER)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700799 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000800 mAuthenticator.addAccountFromCredentials(
801 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700802 break;
803 }
804 }
805 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800806 }
807
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700808 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800809 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000810 // TODO: Anything to do if if succedded?
811 // TODO: If it failed: Show error notification? Should we remove the shadow
812 // account to avoid retries?
813 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800814 }
815
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700816 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800817 public void onError(int errorCode, String errorMessage) {
818 super.onError(errorCode, errorMessage);
819 // TODO: Show error notification to user
820 // TODO: Should we remove the shadow account so that it doesn't keep trying?
821 }
822
823 }.bind();
824 } finally {
825 restoreCallingIdentity(id);
826 }
827 }
828
Amith Yamasani04e0d262012-02-14 11:50:53 -0800829 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700830 Bundle extras, boolean restricted, int callingUid) {
Fred Quintana743dfad2010-07-15 10:59:25 -0700831 if (account == null) {
832 return false;
833 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800834 synchronized (accounts.cacheLock) {
835 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800836 db.beginTransaction();
837 try {
838 long numMatches = DatabaseUtils.longForQuery(db,
839 "select count(*) from " + TABLE_ACCOUNTS
840 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
841 new String[]{account.name, account.type});
842 if (numMatches > 0) {
843 Log.w(TAG, "insertAccountIntoDatabase: " + account
844 + ", skipping since the account already exists");
845 return false;
846 }
847 ContentValues values = new ContentValues();
848 values.put(ACCOUNTS_NAME, account.name);
849 values.put(ACCOUNTS_TYPE, account.type);
850 values.put(ACCOUNTS_PASSWORD, password);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800851 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800852 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
853 if (accountId < 0) {
854 Log.w(TAG, "insertAccountIntoDatabase: " + account
855 + ", skipping the DB insert failed");
856 return false;
857 }
858 if (extras != null) {
859 for (String key : extras.keySet()) {
860 final String value = extras.getString(key);
861 if (insertExtraLocked(db, accountId, key, value) < 0) {
862 Log.w(TAG, "insertAccountIntoDatabase: " + account
863 + ", skipping since insertExtra failed for key " + key);
864 return false;
865 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700866 }
867 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800868 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700869
870 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
871 accounts, callingUid);
872
Amith Yamasani04e0d262012-02-14 11:50:53 -0800873 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800874 } finally {
875 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700876 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800877 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700878 }
879 if (accounts.userId == UserHandle.USER_OWNER) {
880 addAccountToLimitedUsers(account);
881 }
882 return true;
883 }
884
885 /**
886 * Adds the account to all limited users as shared accounts. If the user is currently
887 * running, then clone the account too.
888 * @param account the account to share with limited users
889 */
890 private void addAccountToLimitedUsers(Account account) {
Mita Yunf4c240e2013-04-01 21:12:43 -0700891 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -0700892 for (UserInfo user : users) {
893 if (user.isRestricted()) {
894 addSharedAccountAsUser(account, user.id);
895 try {
896 if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) {
897 mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
898 MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id,
899 account));
900 }
901 } catch (RemoteException re) {
902 // Shouldn't happen
903 }
904 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700905 }
906 }
907
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800908 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -0700909 ContentValues values = new ContentValues();
910 values.put(EXTRAS_KEY, key);
911 values.put(EXTRAS_ACCOUNTS_ID, accountId);
912 values.put(EXTRAS_VALUE, value);
913 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
914 }
915
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800916 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -0800917 public void hasFeatures(IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800918 Account account, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -0800919 if (Log.isLoggable(TAG, Log.VERBOSE)) {
920 Log.v(TAG, "hasFeatures: " + account
921 + ", response " + response
922 + ", features " + stringArrayToString(features)
923 + ", caller's uid " + Binder.getCallingUid()
924 + ", pid " + Binder.getCallingPid());
925 }
Fred Quintana382601f2010-03-25 12:25:10 -0700926 if (response == null) throw new IllegalArgumentException("response is null");
927 if (account == null) throw new IllegalArgumentException("account is null");
928 if (features == null) throw new IllegalArgumentException("features is null");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800929 checkReadAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800930 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800931 long identityToken = clearCallingIdentity();
932 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800933 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800934 } finally {
935 restoreCallingIdentity(identityToken);
936 }
937 }
938
939 private class TestFeaturesSession extends Session {
940 private final String[] mFeatures;
941 private final Account mAccount;
942
Amith Yamasani04e0d262012-02-14 11:50:53 -0800943 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800944 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800945 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800946 true /* stripAuthTokenFromResult */, account.name,
947 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800948 mFeatures = features;
949 mAccount = account;
950 }
951
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700952 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800953 public void run() throws RemoteException {
954 try {
955 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
956 } catch (RemoteException e) {
957 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
958 }
959 }
960
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700961 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800962 public void onResult(Bundle result) {
963 IAccountManagerResponse response = getResponseAndClose();
964 if (response != null) {
965 try {
966 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -0700967 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800968 return;
969 }
Fred Quintana56285a62010-12-02 14:20:51 -0800970 if (Log.isLoggable(TAG, Log.VERBOSE)) {
971 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
972 + response);
973 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800974 final Bundle newResult = new Bundle();
975 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
976 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
977 response.onResult(newResult);
978 } catch (RemoteException e) {
979 // if the caller is dead then there is no one to care about remote exceptions
980 if (Log.isLoggable(TAG, Log.VERBOSE)) {
981 Log.v(TAG, "failure while notifying response", e);
982 }
983 }
984 }
985 }
986
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700987 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800988 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -0800989 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800990 + ", " + mAccount
991 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
992 }
993 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800994
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800995 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700996 public void renameAccount(
997 IAccountManagerResponse response, Account accountToRename, String newName) {
998 if (Log.isLoggable(TAG, Log.VERBOSE)) {
999 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
1000 + ", caller's uid " + Binder.getCallingUid()
1001 + ", pid " + Binder.getCallingPid());
1002 }
1003 if (accountToRename == null) throw new IllegalArgumentException("account is null");
1004 checkAuthenticateAccountsPermission(accountToRename);
1005 UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001006
1007 int callingUid = getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001008 long identityToken = clearCallingIdentity();
1009 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001010 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName,
1011 callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001012 Bundle result = new Bundle();
1013 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1014 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1015 try {
1016 response.onResult(result);
1017 } catch (RemoteException e) {
1018 Log.w(TAG, e.getMessage());
1019 }
1020 } finally {
1021 restoreCallingIdentity(identityToken);
1022 }
1023 }
1024
1025 private Account renameAccountInternal(
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001026 UserAccounts accounts, Account accountToRename, String newName, int callingUid) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001027 Account resultAccount = null;
1028 /*
1029 * Cancel existing notifications. Let authenticators
1030 * re-post notifications as required. But we don't know if
1031 * the authenticators have bound their notifications to
1032 * now stale account name data.
1033 *
1034 * With a rename api, we might not need to do this anymore but it
1035 * shouldn't hurt.
1036 */
1037 cancelNotification(
1038 getSigninRequiredNotificationId(accounts, accountToRename),
1039 new UserHandle(accounts.userId));
1040 synchronized(accounts.credentialsPermissionNotificationIds) {
1041 for (Pair<Pair<Account, String>, Integer> pair:
1042 accounts.credentialsPermissionNotificationIds.keySet()) {
1043 if (accountToRename.equals(pair.first.first)) {
1044 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1045 cancelNotification(id, new UserHandle(accounts.userId));
1046 }
1047 }
1048 }
1049 synchronized (accounts.cacheLock) {
1050 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1051 db.beginTransaction();
1052 boolean isSuccessful = false;
1053 Account renamedAccount = new Account(newName, accountToRename.type);
1054 try {
1055 final ContentValues values = new ContentValues();
1056 values.put(ACCOUNTS_NAME, newName);
1057 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1058 final long accountId = getAccountIdLocked(db, accountToRename);
1059 if (accountId >= 0) {
1060 final String[] argsAccountId = { String.valueOf(accountId) };
1061 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1062 db.setTransactionSuccessful();
1063 isSuccessful = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001064 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1065 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001066 }
1067 } finally {
1068 db.endTransaction();
1069 if (isSuccessful) {
1070 /*
1071 * Database transaction was successful. Clean up cached
1072 * data associated with the account in the user profile.
1073 */
1074 insertAccountIntoCacheLocked(accounts, renamedAccount);
1075 /*
1076 * Extract the data and token caches before removing the
1077 * old account to preserve the user data associated with
1078 * the account.
1079 */
1080 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1081 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1082 removeAccountFromCacheLocked(accounts, accountToRename);
1083 /*
1084 * Update the cached data associated with the renamed
1085 * account.
1086 */
1087 accounts.userDataCache.put(renamedAccount, tmpData);
1088 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1089 accounts.previousNameCache.put(
1090 renamedAccount,
1091 new AtomicReference<String>(accountToRename.name));
1092 resultAccount = renamedAccount;
1093
1094 if (accounts.userId == UserHandle.USER_OWNER) {
1095 /*
1096 * Owner's account was renamed, rename the account for
1097 * those users with which the account was shared.
1098 */
1099 List<UserInfo> users = mUserManager.getUsers(true);
1100 for (UserInfo user : users) {
1101 if (!user.isPrimary() && user.isRestricted()) {
1102 renameSharedAccountAsUser(accountToRename, newName, user.id);
1103 }
1104 }
1105 }
1106 sendAccountsChangedBroadcast(accounts.userId);
1107 }
1108 }
1109 }
1110 return resultAccount;
1111 }
1112
1113 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001114 public void removeAccount(IAccountManagerResponse response, Account account,
1115 boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08001116 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1117 Log.v(TAG, "removeAccount: " + account
1118 + ", response " + response
1119 + ", caller's uid " + Binder.getCallingUid()
1120 + ", pid " + Binder.getCallingPid());
1121 }
Fred Quintana382601f2010-03-25 12:25:10 -07001122 if (response == null) throw new IllegalArgumentException("response is null");
1123 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001124 checkManageAccountsPermission();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001125 UserHandle user = Binder.getCallingUserHandle();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001126 UserAccounts accounts = getUserAccountsForCaller();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001127 int userId = Binder.getCallingUserHandle().getIdentifier();
1128 if (!canUserModifyAccounts(userId)) {
Amith Yamasanie4cf7342012-12-17 11:12:09 -08001129 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001130 // TODO: This should be ERROR_CODE_USER_RESTRICTED instead. See http://b/16322768
Amith Yamasanie4cf7342012-12-17 11:12:09 -08001131 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
1132 "User cannot modify accounts");
1133 } catch (RemoteException re) {
1134 }
Amith Yamasani5934dbc2014-02-20 15:23:36 -08001135 return;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08001136 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001137 if (!canUserModifyAccountsForType(userId, account.type)) {
1138 try {
1139 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1140 "User cannot modify accounts of this type (policy).");
1141 } catch (RemoteException re) {
1142 }
1143 return;
1144 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08001145
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001146 long identityToken = clearCallingIdentity();
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001147
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001148 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001149 synchronized (accounts.credentialsPermissionNotificationIds) {
1150 for (Pair<Pair<Account, String>, Integer> pair:
1151 accounts.credentialsPermissionNotificationIds.keySet()) {
1152 if (account.equals(pair.first.first)) {
1153 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1154 cancelNotification(id, user);
1155 }
1156 }
1157 }
1158
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001159 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1160
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001161 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001162 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001163 } finally {
1164 restoreCallingIdentity(identityToken);
1165 }
1166 }
1167
1168 @Override
1169 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001170 boolean expectActivityLaunch, int userId) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001171 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1172 Log.v(TAG, "removeAccount: " + account
1173 + ", response " + response
1174 + ", caller's uid " + Binder.getCallingUid()
1175 + ", pid " + Binder.getCallingPid()
1176 + ", for user id " + userId);
1177 }
1178 if (response == null) throw new IllegalArgumentException("response is null");
1179 if (account == null) throw new IllegalArgumentException("account is null");
1180
1181 // Only allow the system process to modify accounts of other users
1182 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
1183 + " trying to remove account for " + userId);
1184 checkManageAccountsPermission();
1185
1186 UserAccounts accounts = getUserAccounts(userId);
1187 if (!canUserModifyAccounts(userId)) {
1188 try {
1189 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1190 "User cannot modify accounts");
1191 } catch (RemoteException re) {
1192 }
1193 return;
1194 }
1195 if (!canUserModifyAccountsForType(userId, account.type)) {
1196 try {
1197 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1198 "User cannot modify accounts of this type (policy).");
1199 } catch (RemoteException re) {
1200 }
1201 return;
1202 }
1203
1204 UserHandle user = new UserHandle(userId);
1205 long identityToken = clearCallingIdentity();
1206
1207 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001208 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001209 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08001210 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001211 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001212 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001213 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001214 }
1215 }
1216 }
1217
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001218 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1219
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001220 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001221 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1222 } finally {
1223 restoreCallingIdentity(identityToken);
1224 }
1225 }
1226
1227 @Override
1228 public boolean removeAccountExplicitly(Account account) {
1229 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1230 Log.v(TAG, "removeAccountExplicitly: " + account
1231 + ", caller's uid " + Binder.getCallingUid()
1232 + ", pid " + Binder.getCallingPid());
1233 }
1234 if (account == null) throw new IllegalArgumentException("account is null");
1235 checkAuthenticateAccountsPermission(account);
1236
1237 UserAccounts accounts = getUserAccountsForCaller();
1238 int userId = Binder.getCallingUserHandle().getIdentifier();
1239 if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
1240 return false;
1241 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001242
1243 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1244
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001245 long identityToken = clearCallingIdentity();
1246 try {
1247 return removeAccountInternal(accounts, account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001248 } finally {
1249 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001250 }
Fred Quintana60307342009-03-24 22:48:12 -07001251 }
1252
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001253 private class RemoveAccountSession extends Session {
1254 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001255 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001256 Account account, boolean expectActivityLaunch) {
1257 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001258 true /* stripAuthTokenFromResult */, account.name,
1259 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001260 mAccount = account;
1261 }
1262
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001263 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001264 protected String toDebugString(long now) {
1265 return super.toDebugString(now) + ", removeAccount"
1266 + ", account " + mAccount;
1267 }
1268
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001269 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001270 public void run() throws RemoteException {
1271 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1272 }
1273
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001274 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001275 public void onResult(Bundle result) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001276 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1277 && !result.containsKey(AccountManager.KEY_INTENT)) {
1278 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001279 if (removalAllowed) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001280 removeAccountInternal(mAccounts, mAccount);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001281 }
1282 IAccountManagerResponse response = getResponseAndClose();
1283 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001284 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1285 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1286 + response);
1287 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001288 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001289 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001290 try {
1291 response.onResult(result2);
1292 } catch (RemoteException e) {
1293 // ignore
1294 }
1295 }
1296 }
1297 super.onResult(result);
1298 }
1299 }
1300
Amith Yamasani04e0d262012-02-14 11:50:53 -08001301 /* For testing */
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001302 protected void removeAccountInternal(Account account) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001303 removeAccountInternal(getUserAccountsForCaller(), account);
1304 }
1305
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001306 private boolean removeAccountInternal(UserAccounts accounts, Account account) {
1307 int deleted;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001308 synchronized (accounts.cacheLock) {
1309 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001310 final long accountId = getAccountIdLocked(db, account);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001311 deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
1312 + "=?",
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001313 new String[]{account.name, account.type});
Amith Yamasani04e0d262012-02-14 11:50:53 -08001314 removeAccountFromCacheLocked(accounts, account);
1315 sendAccountsChangedBroadcast(accounts.userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001316
1317 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001318 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001319 if (accounts.userId == UserHandle.USER_OWNER) {
1320 // Owner's account was removed, remove from any users that are sharing
1321 // this account.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001322 int callingUid = getCallingUid();
Amith Yamasani67df64b2012-12-14 12:09:36 -08001323 long id = Binder.clearCallingIdentity();
1324 try {
1325 List<UserInfo> users = mUserManager.getUsers(true);
1326 for (UserInfo user : users) {
1327 if (!user.isPrimary() && user.isRestricted()) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001328 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001329 }
1330 }
1331 } finally {
1332 Binder.restoreCallingIdentity(id);
1333 }
1334 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001335 return (deleted > 0);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001336 }
1337
Maggie Benthalla12fccf2013-03-14 18:02:12 -04001338 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001339 public void invalidateAuthToken(String accountType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -08001340 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1341 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
1342 + ", caller's uid " + Binder.getCallingUid()
1343 + ", pid " + Binder.getCallingPid());
1344 }
Fred Quintana382601f2010-03-25 12:25:10 -07001345 if (accountType == null) throw new IllegalArgumentException("accountType is null");
1346 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Fred Quintanab38eb142010-02-24 13:40:54 -08001347 checkManageAccountsOrUseCredentialsPermissions();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001348 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001349 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001350 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001351 synchronized (accounts.cacheLock) {
1352 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001353 db.beginTransaction();
1354 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001355 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001356 db.setTransactionSuccessful();
1357 } finally {
1358 db.endTransaction();
1359 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001360 }
Fred Quintana60307342009-03-24 22:48:12 -07001361 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001362 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001363 }
1364 }
1365
Amith Yamasani04e0d262012-02-14 11:50:53 -08001366 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1367 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001368 if (authToken == null || accountType == null) {
1369 return;
1370 }
Fred Quintana33269202009-04-20 16:05:10 -07001371 Cursor cursor = db.rawQuery(
1372 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1373 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1374 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1375 + " FROM " + TABLE_ACCOUNTS
1376 + " JOIN " + TABLE_AUTHTOKENS
1377 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1378 + " = " + AUTHTOKENS_ACCOUNTS_ID
1379 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
1380 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1381 new String[]{authToken, accountType});
1382 try {
1383 while (cursor.moveToNext()) {
1384 long authTokenId = cursor.getLong(0);
1385 String accountName = cursor.getString(1);
1386 String authTokenType = cursor.getString(2);
1387 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001388 writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType),
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001389 authTokenType, null);
Fred Quintana60307342009-03-24 22:48:12 -07001390 }
Fred Quintana33269202009-04-20 16:05:10 -07001391 } finally {
1392 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07001393 }
1394 }
1395
Amith Yamasani04e0d262012-02-14 11:50:53 -08001396 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1397 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07001398 if (account == null || type == null) {
1399 return false;
1400 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001401 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1402 new UserHandle(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001403 synchronized (accounts.cacheLock) {
1404 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001405 db.beginTransaction();
1406 try {
1407 long accountId = getAccountIdLocked(db, account);
1408 if (accountId < 0) {
1409 return false;
1410 }
1411 db.delete(TABLE_AUTHTOKENS,
1412 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1413 new String[]{type});
1414 ContentValues values = new ContentValues();
1415 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1416 values.put(AUTHTOKENS_TYPE, type);
1417 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1418 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1419 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001420 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001421 return true;
1422 }
Fred Quintana33269202009-04-20 16:05:10 -07001423 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001424 } finally {
1425 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07001426 }
Fred Quintana60307342009-03-24 22:48:12 -07001427 }
1428 }
1429
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001430 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001431 public String peekAuthToken(Account account, String authTokenType) {
Fred Quintana56285a62010-12-02 14:20:51 -08001432 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1433 Log.v(TAG, "peekAuthToken: " + account
1434 + ", authTokenType " + authTokenType
1435 + ", caller's uid " + Binder.getCallingUid()
1436 + ", pid " + Binder.getCallingPid());
1437 }
Fred Quintana382601f2010-03-25 12:25:10 -07001438 if (account == null) throw new IllegalArgumentException("account is null");
1439 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001440 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001441 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001442 long identityToken = clearCallingIdentity();
1443 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001444 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001445 } finally {
1446 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001447 }
Fred Quintana60307342009-03-24 22:48:12 -07001448 }
1449
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001450 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001451 public void setAuthToken(Account account, String authTokenType, String authToken) {
Fred Quintana56285a62010-12-02 14:20:51 -08001452 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1453 Log.v(TAG, "setAuthToken: " + account
1454 + ", authTokenType " + authTokenType
1455 + ", caller's uid " + Binder.getCallingUid()
1456 + ", pid " + Binder.getCallingPid());
1457 }
Fred Quintana382601f2010-03-25 12:25:10 -07001458 if (account == null) throw new IllegalArgumentException("account is null");
1459 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001460 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001461 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001462 long identityToken = clearCallingIdentity();
1463 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001464 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001465 } finally {
1466 restoreCallingIdentity(identityToken);
1467 }
Fred Quintana60307342009-03-24 22:48:12 -07001468 }
1469
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001470 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001471 public void setPassword(Account account, String password) {
Fred Quintana56285a62010-12-02 14:20:51 -08001472 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1473 Log.v(TAG, "setAuthToken: " + account
1474 + ", caller's uid " + Binder.getCallingUid()
1475 + ", pid " + Binder.getCallingPid());
1476 }
Fred Quintana382601f2010-03-25 12:25:10 -07001477 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001478 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001479 UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001480 int callingUid = getCallingUid();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001481 long identityToken = clearCallingIdentity();
1482 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001483 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001484 } finally {
1485 restoreCallingIdentity(identityToken);
1486 }
Fred Quintana60307342009-03-24 22:48:12 -07001487 }
1488
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001489 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1490 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07001491 if (account == null) {
1492 return;
1493 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001494 synchronized (accounts.cacheLock) {
1495 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001496 db.beginTransaction();
1497 try {
1498 final ContentValues values = new ContentValues();
1499 values.put(ACCOUNTS_PASSWORD, password);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001500 long time = 0;
1501 // Only set current time, if it is a valid password. For clear password case, it
1502 // should not be set.
1503 if (password != null) {
1504 time = System.currentTimeMillis();
1505 }
1506 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, time);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001507 final long accountId = getAccountIdLocked(db, account);
1508 if (accountId >= 0) {
1509 final String[] argsAccountId = {String.valueOf(accountId)};
1510 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1511 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001512 accounts.authTokenCache.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001513 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001514
1515 String action = (password == null || password.length() == 0) ?
1516 DebugDbHelper.ACTION_CLEAR_PASSWORD
1517 : DebugDbHelper.ACTION_SET_PASSWORD;
1518 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08001519 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001520 } finally {
1521 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001522 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001523 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001524 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001525 }
1526
Amith Yamasani04e0d262012-02-14 11:50:53 -08001527 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08001528 Log.i(TAG, "the accounts changed, sending broadcast of "
1529 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001530 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07001531 }
1532
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001533 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001534 public void clearPassword(Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08001535 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1536 Log.v(TAG, "clearPassword: " + account
1537 + ", caller's uid " + Binder.getCallingUid()
1538 + ", pid " + Binder.getCallingPid());
1539 }
Fred Quintana382601f2010-03-25 12:25:10 -07001540 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001541 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001542 UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001543
1544 int callingUid = getCallingUid();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001545 long identityToken = clearCallingIdentity();
1546 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001547 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001548 } finally {
1549 restoreCallingIdentity(identityToken);
1550 }
Fred Quintana60307342009-03-24 22:48:12 -07001551 }
1552
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001553 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001554 public void setUserData(Account account, String key, String value) {
Fred Quintana56285a62010-12-02 14:20:51 -08001555 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1556 Log.v(TAG, "setUserData: " + account
1557 + ", key " + key
1558 + ", caller's uid " + Binder.getCallingUid()
1559 + ", pid " + Binder.getCallingPid());
1560 }
Fred Quintana382601f2010-03-25 12:25:10 -07001561 if (key == null) throw new IllegalArgumentException("key is null");
1562 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001563 checkAuthenticateAccountsPermission(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001564 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001565 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001566 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001567 setUserdataInternal(accounts, account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -07001568 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001569 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001570 }
1571 }
1572
Amith Yamasani04e0d262012-02-14 11:50:53 -08001573 private void setUserdataInternal(UserAccounts accounts, Account account, String key,
1574 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07001575 if (account == null || key == null) {
1576 return;
1577 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001578 synchronized (accounts.cacheLock) {
1579 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001580 db.beginTransaction();
1581 try {
1582 long accountId = getAccountIdLocked(db, account);
1583 if (accountId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001584 return;
1585 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001586 long extrasId = getExtrasIdLocked(db, accountId, key);
1587 if (extrasId < 0 ) {
1588 extrasId = insertExtraLocked(db, accountId, key, value);
1589 if (extrasId < 0) {
1590 return;
1591 }
1592 } else {
1593 ContentValues values = new ContentValues();
1594 values.put(EXTRAS_VALUE, value);
1595 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1596 return;
1597 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001598
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001599 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001600 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001601 db.setTransactionSuccessful();
1602 } finally {
1603 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001604 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001605 }
1606 }
1607
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001608 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08001609 if (result == null) {
1610 Log.e(TAG, "the result is unexpectedly null", new Exception());
1611 }
1612 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1613 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1614 + response);
1615 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001616 try {
1617 response.onResult(result);
1618 } catch (RemoteException e) {
1619 // if the caller is dead then there is no one to care about remote
1620 // exceptions
1621 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1622 Log.v(TAG, "failure while notifying response", e);
1623 }
1624 }
1625 }
1626
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001627 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07001628 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1629 final String authTokenType)
1630 throws RemoteException {
1631 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08001632 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1633
Fred Quintanad9640ec2012-05-23 12:37:00 -07001634 final int callingUid = getCallingUid();
1635 clearCallingIdentity();
Amith Yamasani27db4682013-03-30 17:07:47 -07001636 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07001637 throw new SecurityException("can only call from system");
1638 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001639 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
Costin Manolache5f383ad92010-12-02 16:44:46 -08001640 long identityToken = clearCallingIdentity();
1641 try {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001642 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
1643 false /* stripAuthTokenFromResult */, null /* accountName */,
1644 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001645 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001646 protected String toDebugString(long now) {
1647 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07001648 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08001649 + ", authTokenType " + authTokenType;
1650 }
1651
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001652 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001653 public void run() throws RemoteException {
1654 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1655 }
1656
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001657 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001658 public void onResult(Bundle result) {
1659 if (result != null) {
1660 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1661 Bundle bundle = new Bundle();
1662 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1663 super.onResult(bundle);
1664 return;
1665 } else {
1666 super.onResult(result);
1667 }
1668 }
1669 }.bind();
1670 } finally {
1671 restoreCallingIdentity(identityToken);
1672 }
1673 }
1674
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001675 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001676 public void getAuthToken(IAccountManagerResponse response, final Account account,
1677 final String authTokenType, final boolean notifyOnAuthFailure,
Costin Manolachec6684f92011-01-14 11:25:39 -08001678 final boolean expectActivityLaunch, Bundle loginOptionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08001679 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1680 Log.v(TAG, "getAuthToken: " + account
1681 + ", response " + response
1682 + ", authTokenType " + authTokenType
1683 + ", notifyOnAuthFailure " + notifyOnAuthFailure
1684 + ", expectActivityLaunch " + expectActivityLaunch
1685 + ", caller's uid " + Binder.getCallingUid()
1686 + ", pid " + Binder.getCallingPid());
1687 }
Fred Quintana382601f2010-03-25 12:25:10 -07001688 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001689 try {
1690 if (account == null) {
1691 Slog.w(TAG, "getAuthToken called with null account");
1692 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
1693 return;
1694 }
1695 if (authTokenType == null) {
1696 Slog.w(TAG, "getAuthToken called with null authTokenType");
1697 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
1698 return;
1699 }
1700 } catch (RemoteException e) {
1701 Slog.w(TAG, "Failed to report error back to the client." + e);
1702 return;
1703 }
1704
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001705 checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
Dianne Hackborn41203752012-08-31 14:05:51 -07001706 final UserAccounts accounts = getUserAccountsForCaller();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001707 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
1708 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1709 AuthenticatorDescription.newKey(account.type), accounts.userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08001710 final boolean customTokens =
1711 authenticatorInfo != null && authenticatorInfo.type.customTokens;
1712
1713 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001714 final int callerUid = Binder.getCallingUid();
Costin Manolachea40c6302010-12-13 14:50:45 -08001715 final boolean permissionGranted = customTokens ||
1716 permissionIsGranted(account, authTokenType, callerUid);
1717
Costin Manolachec6684f92011-01-14 11:25:39 -08001718 final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
1719 loginOptionsIn;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001720 // let authenticator know the identity of the caller
1721 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1722 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
1723 if (notifyOnAuthFailure) {
1724 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08001725 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001726
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001727 long identityToken = clearCallingIdentity();
1728 try {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001729 // if the caller has permission, do the peek. otherwise go the more expensive
1730 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08001731 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001732 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001733 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001734 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001735 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1736 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1737 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001738 onResult(response, result);
1739 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001740 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001741 }
1742
Amith Yamasani04e0d262012-02-14 11:50:53 -08001743 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001744 false /* stripAuthTokenFromResult */, account.name,
1745 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001746 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001747 protected String toDebugString(long now) {
1748 if (loginOptions != null) loginOptions.keySet();
1749 return super.toDebugString(now) + ", getAuthToken"
1750 + ", " + account
1751 + ", authTokenType " + authTokenType
1752 + ", loginOptions " + loginOptions
1753 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
1754 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001755
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001756 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001757 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001758 // If the caller doesn't have permission then create and return the
1759 // "grant permission" intent instead of the "getAuthToken" intent.
1760 if (!permissionGranted) {
1761 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1762 } else {
1763 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
1764 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001765 }
1766
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001767 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001768 public void onResult(Bundle result) {
1769 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001770 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001771 Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
1772 new AccountAuthenticatorResponse(this),
1773 authTokenType,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001774 result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001775 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001776 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001777 onResult(bundle);
1778 return;
1779 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001780 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001781 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001782 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1783 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001784 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001785 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001786 "the type and name should not be empty");
1787 return;
1788 }
Costin Manolachea40c6302010-12-13 14:50:45 -08001789 if (!customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001790 saveAuthTokenToDatabase(mAccounts, new Account(name, type),
Costin Manolachea40c6302010-12-13 14:50:45 -08001791 authTokenType, authToken);
1792 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001793 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001794
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001795 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08001796 if (intent != null && notifyOnAuthFailure && !customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001797 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001798 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Dianne Hackborn41203752012-08-31 14:05:51 -07001799 intent, accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001800 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001801 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001802 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07001803 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001804 }.bind();
1805 } finally {
1806 restoreCallingIdentity(identityToken);
1807 }
Fred Quintana60307342009-03-24 22:48:12 -07001808 }
1809
Dianne Hackborn41203752012-08-31 14:05:51 -07001810 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
1811 int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001812 int uid = intent.getIntExtra(
1813 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
1814 String authTokenType = intent.getStringExtra(
1815 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
1816 String authTokenLabel = intent.getStringExtra(
1817 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);
1818
1819 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
1820 0 /* when */);
Eric Fischeree452ee2009-08-31 17:58:06 -07001821 final String titleAndSubtitle =
1822 mContext.getString(R.string.permission_request_notification_with_subtitle,
1823 account.name);
1824 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07001825 String title = titleAndSubtitle;
1826 String subtitle = "";
1827 if (index > 0) {
1828 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04001829 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07001830 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001831 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01001832 Context contextForUser = getContextForUser(user);
Alan Viverette4a357cd2015-03-18 18:37:18 -07001833 n.color = contextForUser.getColor(
Selim Cinek255dd042014-08-19 22:29:02 +02001834 com.android.internal.R.color.system_notification_accent_color);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01001835 n.setLatestEventInfo(contextForUser, title, subtitle,
Dianne Hackborn41203752012-08-31 14:05:51 -07001836 PendingIntent.getActivityAsUser(mContext, 0, intent,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001837 PendingIntent.FLAG_CANCEL_CURRENT, null, user));
1838 installNotification(getCredentialPermissionNotificationId(
1839 account, authTokenType, uid), n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001840 }
1841
Costin Manolache5f383ad92010-12-02 16:44:46 -08001842 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
1843 AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001844
1845 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07001846 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08001847 // Since it was set in Eclair+ we can't change it without breaking apps using
1848 // the intent from a non-Activity context.
1849 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001850 intent.addCategory(
1851 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08001852
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001853 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001854 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
1855 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001856 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001857
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001858 return intent;
1859 }
1860
1861 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
1862 int uid) {
1863 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001864 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001865 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001866 final Pair<Pair<Account, String>, Integer> key =
1867 new Pair<Pair<Account, String>, Integer>(
1868 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001869 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001870 if (id == null) {
1871 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001872 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001873 }
1874 }
1875 return id;
1876 }
1877
Amith Yamasani04e0d262012-02-14 11:50:53 -08001878 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001879 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001880 synchronized (accounts.signinRequiredNotificationIds) {
1881 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001882 if (id == null) {
1883 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001884 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001885 }
1886 }
1887 return id;
1888 }
1889
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001890 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001891 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07001892 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001893 final boolean expectActivityLaunch, final Bundle optionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08001894 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1895 Log.v(TAG, "addAccount: accountType " + accountType
1896 + ", response " + response
1897 + ", authTokenType " + authTokenType
1898 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
1899 + ", expectActivityLaunch " + expectActivityLaunch
1900 + ", caller's uid " + Binder.getCallingUid()
1901 + ", pid " + Binder.getCallingPid());
1902 }
Fred Quintana382601f2010-03-25 12:25:10 -07001903 if (response == null) throw new IllegalArgumentException("response is null");
1904 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001905 checkManageAccountsPermission();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001906
Amith Yamasani71e6c692013-03-24 17:39:28 -07001907 // Is user disallowed from modifying accounts?
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001908 int userId = Binder.getCallingUserHandle().getIdentifier();
1909 if (!canUserModifyAccounts(userId)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08001910 try {
1911 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1912 "User is not allowed to add an account!");
1913 } catch (RemoteException re) {
1914 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07001915 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001916 return;
1917 }
1918 if (!canUserModifyAccountsForType(userId, accountType)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07001919 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001920 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1921 "User cannot modify accounts of this type (policy).");
1922 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07001923 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07001924 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1925 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08001926 return;
1927 }
1928
Amith Yamasani04e0d262012-02-14 11:50:53 -08001929 UserAccounts accounts = getUserAccountsForCaller();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001930 final int pid = Binder.getCallingPid();
1931 final int uid = Binder.getCallingUid();
1932 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
1933 options.putInt(AccountManager.KEY_CALLER_UID, uid);
1934 options.putInt(AccountManager.KEY_CALLER_PID, pid);
1935
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001936 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS);
1937
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001938 long identityToken = clearCallingIdentity();
1939 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001940 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001941 true /* stripAuthTokenFromResult */, null /* accountName */,
1942 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001943 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001944 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07001945 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07001946 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001947 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001948
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001949 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001950 protected String toDebugString(long now) {
1951 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07001952 + ", accountType " + accountType
1953 + ", requiredFeatures "
1954 + (requiredFeatures != null
1955 ? TextUtils.join(",", requiredFeatures)
1956 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001957 }
1958 }.bind();
1959 } finally {
1960 restoreCallingIdentity(identityToken);
1961 }
Fred Quintana60307342009-03-24 22:48:12 -07001962 }
1963
Amith Yamasani2c7bc262012-11-05 16:46:02 -08001964 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001965 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
1966 final String authTokenType, final String[] requiredFeatures,
1967 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
1968 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1969 Log.v(TAG, "addAccount: accountType " + accountType
1970 + ", response " + response
1971 + ", authTokenType " + authTokenType
1972 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
1973 + ", expectActivityLaunch " + expectActivityLaunch
1974 + ", caller's uid " + Binder.getCallingUid()
1975 + ", pid " + Binder.getCallingPid()
1976 + ", for user id " + userId);
1977 }
1978 if (response == null) throw new IllegalArgumentException("response is null");
1979 if (accountType == null) throw new IllegalArgumentException("accountType is null");
1980 checkManageAccountsPermission();
1981
1982 // Only allow the system process to add accounts of other users
1983 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
1984 + " trying to add account for " + userId);
1985
1986 // Is user disallowed from modifying accounts?
1987 if (!canUserModifyAccounts(userId)) {
1988 try {
1989 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1990 "User is not allowed to add an account!");
1991 } catch (RemoteException re) {
1992 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07001993 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001994 return;
1995 }
1996 if (!canUserModifyAccountsForType(userId, accountType)) {
1997 try {
1998 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1999 "User cannot modify accounts of this type (policy).");
2000 } catch (RemoteException re) {
2001 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002002 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2003 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002004 return;
2005 }
2006
2007 UserAccounts accounts = getUserAccounts(userId);
2008 final int pid = Binder.getCallingPid();
2009 final int uid = Binder.getCallingUid();
2010 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2011 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2012 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2013
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002014 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS);
2015
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002016 long identityToken = clearCallingIdentity();
2017 try {
2018 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002019 true /* stripAuthTokenFromResult */, null /* accountName */,
2020 false /* authDetailsRequired */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002021 @Override
2022 public void run() throws RemoteException {
2023 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2024 options);
2025 }
2026
2027 @Override
2028 protected String toDebugString(long now) {
2029 return super.toDebugString(now) + ", addAccount"
2030 + ", accountType " + accountType
2031 + ", requiredFeatures "
2032 + (requiredFeatures != null
2033 ? TextUtils.join(",", requiredFeatures)
2034 : null);
2035 }
2036 }.bind();
2037 } finally {
2038 restoreCallingIdentity(identityToken);
2039 }
2040 }
2041
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002042 private void showCantAddAccount(int errorCode, int userId) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002043 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2044 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2045 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2046 long identityToken = clearCallingIdentity();
2047 try {
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002048 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002049 } finally {
2050 restoreCallingIdentity(identityToken);
2051 }
2052 }
2053
2054 @Override
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002055 public void confirmCredentialsAsUser(IAccountManagerResponse response,
2056 final Account account, final Bundle options, final boolean expectActivityLaunch,
2057 int userId) {
2058 // Only allow the system process to read accounts of other users
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01002059 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002060 + " trying to confirm account credentials for " + userId);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002061
Fred Quintana56285a62010-12-02 14:20:51 -08002062 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2063 Log.v(TAG, "confirmCredentials: " + account
2064 + ", response " + response
2065 + ", expectActivityLaunch " + expectActivityLaunch
2066 + ", caller's uid " + Binder.getCallingUid()
2067 + ", pid " + Binder.getCallingPid());
2068 }
Fred Quintana382601f2010-03-25 12:25:10 -07002069 if (response == null) throw new IllegalArgumentException("response is null");
2070 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002071 checkManageAccountsPermission();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002072 UserAccounts accounts = getUserAccounts(userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002073 long identityToken = clearCallingIdentity();
2074 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002075 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002076 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002077 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002078 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002079 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002080 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002081 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002082 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002083 protected String toDebugString(long now) {
2084 return super.toDebugString(now) + ", confirmCredentials"
2085 + ", " + account;
2086 }
2087 }.bind();
2088 } finally {
2089 restoreCallingIdentity(identityToken);
2090 }
Fred Quintana60307342009-03-24 22:48:12 -07002091 }
2092
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002093 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002094 public void updateCredentials(IAccountManagerResponse response, final Account account,
2095 final String authTokenType, final boolean expectActivityLaunch,
2096 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08002097 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2098 Log.v(TAG, "updateCredentials: " + account
2099 + ", response " + response
2100 + ", authTokenType " + authTokenType
2101 + ", expectActivityLaunch " + expectActivityLaunch
2102 + ", caller's uid " + Binder.getCallingUid()
2103 + ", pid " + Binder.getCallingPid());
2104 }
Fred Quintana382601f2010-03-25 12:25:10 -07002105 if (response == null) throw new IllegalArgumentException("response is null");
2106 if (account == null) throw new IllegalArgumentException("account is null");
2107 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002108 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002109 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002110 long identityToken = clearCallingIdentity();
2111 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002112 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002113 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002114 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002115 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002116 public void run() throws RemoteException {
2117 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
2118 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002119 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002120 protected String toDebugString(long now) {
2121 if (loginOptions != null) loginOptions.keySet();
2122 return super.toDebugString(now) + ", updateCredentials"
2123 + ", " + account
2124 + ", authTokenType " + authTokenType
2125 + ", loginOptions " + loginOptions;
2126 }
2127 }.bind();
2128 } finally {
2129 restoreCallingIdentity(identityToken);
2130 }
Fred Quintana60307342009-03-24 22:48:12 -07002131 }
2132
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002133 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002134 public void editProperties(IAccountManagerResponse response, final String accountType,
2135 final boolean expectActivityLaunch) {
Fred Quintana56285a62010-12-02 14:20:51 -08002136 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2137 Log.v(TAG, "editProperties: accountType " + accountType
2138 + ", response " + response
2139 + ", expectActivityLaunch " + expectActivityLaunch
2140 + ", caller's uid " + Binder.getCallingUid()
2141 + ", pid " + Binder.getCallingPid());
2142 }
Fred Quintana382601f2010-03-25 12:25:10 -07002143 if (response == null) throw new IllegalArgumentException("response is null");
2144 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002145 checkManageAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002146 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002147 long identityToken = clearCallingIdentity();
2148 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002149 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002150 true /* stripAuthTokenFromResult */, null /* accountName */,
2151 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002152 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002153 public void run() throws RemoteException {
2154 mAuthenticator.editProperties(this, mAccountType);
2155 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002156 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002157 protected String toDebugString(long now) {
2158 return super.toDebugString(now) + ", editProperties"
2159 + ", accountType " + accountType;
2160 }
2161 }.bind();
2162 } finally {
2163 restoreCallingIdentity(identityToken);
2164 }
Fred Quintana60307342009-03-24 22:48:12 -07002165 }
2166
Fred Quintana33269202009-04-20 16:05:10 -07002167 private class GetAccountsByTypeAndFeatureSession extends Session {
2168 private final String[] mFeatures;
2169 private volatile Account[] mAccountsOfType = null;
2170 private volatile ArrayList<Account> mAccountsWithFeatures = null;
2171 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002172 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07002173
Amith Yamasani04e0d262012-02-14 11:50:53 -08002174 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002175 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002176 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002177 true /* stripAuthTokenFromResult */, null /* accountName */,
2178 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002179 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07002180 mFeatures = features;
2181 }
2182
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002183 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002184 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002185 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002186 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
2187 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002188 }
Fred Quintana33269202009-04-20 16:05:10 -07002189 // check whether each account matches the requested features
2190 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
2191 mCurrentAccount = 0;
2192
2193 checkAccount();
2194 }
2195
2196 public void checkAccount() {
2197 if (mCurrentAccount >= mAccountsOfType.length) {
2198 sendResult();
2199 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002200 }
Fred Quintana33269202009-04-20 16:05:10 -07002201
Fred Quintana29e94b82010-03-10 12:11:51 -08002202 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
2203 if (accountAuthenticator == null) {
2204 // It is possible that the authenticator has died, which is indicated by
2205 // mAuthenticator being set to null. If this happens then just abort.
2206 // There is no need to send back a result or error in this case since
2207 // that already happened when mAuthenticator was cleared.
2208 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2209 Log.v(TAG, "checkAccount: aborting session since we are no longer"
2210 + " connected to the authenticator, " + toDebugString());
2211 }
2212 return;
2213 }
Fred Quintana33269202009-04-20 16:05:10 -07002214 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08002215 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07002216 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002217 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07002218 }
2219 }
2220
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002221 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002222 public void onResult(Bundle result) {
2223 mNumResults++;
2224 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002225 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07002226 return;
2227 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002228 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07002229 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
2230 }
2231 mCurrentAccount++;
2232 checkAccount();
2233 }
2234
2235 public void sendResult() {
2236 IAccountManagerResponse response = getResponseAndClose();
2237 if (response != null) {
2238 try {
2239 Account[] accounts = new Account[mAccountsWithFeatures.size()];
2240 for (int i = 0; i < accounts.length; i++) {
2241 accounts[i] = mAccountsWithFeatures.get(i);
2242 }
Fred Quintana56285a62010-12-02 14:20:51 -08002243 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2244 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2245 + response);
2246 }
Fred Quintana33269202009-04-20 16:05:10 -07002247 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002248 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07002249 response.onResult(result);
2250 } catch (RemoteException e) {
2251 // if the caller is dead then there is no one to care about remote exceptions
2252 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2253 Log.v(TAG, "failure while notifying response", e);
2254 }
2255 }
2256 }
2257 }
2258
2259
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002260 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002261 protected String toDebugString(long now) {
2262 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
2263 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
2264 }
2265 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002266
Amith Yamasani04e0d262012-02-14 11:50:53 -08002267 /**
2268 * Returns the accounts for a specific user
2269 * @hide
2270 */
2271 public Account[] getAccounts(int userId) {
2272 checkReadAccountsPermission();
2273 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002274 int callingUid = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002275 long identityToken = clearCallingIdentity();
2276 try {
2277 synchronized (accounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002278 return getAccountsFromCacheLocked(accounts, null, callingUid, null);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002279 }
2280 } finally {
2281 restoreCallingIdentity(identityToken);
2282 }
2283 }
2284
Amith Yamasanif29f2362012-04-05 18:29:52 -07002285 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002286 * Returns accounts for all running users.
2287 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07002288 * @hide
2289 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002290 public AccountAndUser[] getRunningAccounts() {
2291 final int[] runningUserIds;
2292 try {
2293 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
2294 } catch (RemoteException e) {
2295 // Running in system_server; should never happen
2296 throw new RuntimeException(e);
2297 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07002298 return getAccounts(runningUserIds);
2299 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07002300
Jeff Sharkey6eb96202012-10-10 13:13:54 -07002301 /** {@hide} */
2302 public AccountAndUser[] getAllAccounts() {
2303 final List<UserInfo> users = getUserManager().getUsers();
2304 final int[] userIds = new int[users.size()];
2305 for (int i = 0; i < userIds.length; i++) {
2306 userIds[i] = users.get(i).id;
2307 }
2308 return getAccounts(userIds);
2309 }
2310
2311 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002312 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07002313 for (int userId : userIds) {
2314 UserAccounts userAccounts = getUserAccounts(userId);
2315 if (userAccounts == null) continue;
2316 synchronized (userAccounts.cacheLock) {
2317 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
2318 Binder.getCallingUid(), null);
2319 for (int a = 0; a < accounts.length; a++) {
2320 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07002321 }
2322 }
2323 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002324
2325 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
2326 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07002327 }
2328
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002329 @Override
2330 public Account[] getAccountsAsUser(String type, int userId) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002331 return getAccountsAsUser(type, userId, null, -1);
2332 }
2333
2334 private Account[] getAccountsAsUser(String type, int userId, String callingPackage,
2335 int packageUid) {
2336 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002337 // Only allow the system process to read accounts of other users
2338 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07002339 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08002340 && mContext.checkCallingOrSelfPermission(
2341 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
2342 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002343 throw new SecurityException("User " + UserHandle.getCallingUserId()
2344 + " trying to get account for " + userId);
2345 }
2346
Fred Quintana56285a62010-12-02 14:20:51 -08002347 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2348 Log.v(TAG, "getAccounts: accountType " + type
2349 + ", caller's uid " + Binder.getCallingUid()
2350 + ", pid " + Binder.getCallingPid());
2351 }
Amith Yamasani27db4682013-03-30 17:07:47 -07002352 // If the original calling app was using the framework account chooser activity, we'll
2353 // be passed in the original caller's uid here, which is what should be used for filtering.
2354 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
2355 callingUid = packageUid;
2356 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002357 checkReadAccountsPermission();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002358 UserAccounts accounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002359 long identityToken = clearCallingIdentity();
2360 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002361 synchronized (accounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002362 return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002363 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002364 } finally {
2365 restoreCallingIdentity(identityToken);
2366 }
2367 }
2368
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002369 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08002370 public boolean addSharedAccountAsUser(Account account, int userId) {
2371 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002372 UserAccounts accounts = getUserAccounts(userId);
2373 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Amith Yamasani67df64b2012-12-14 12:09:36 -08002374 ContentValues values = new ContentValues();
2375 values.put(ACCOUNTS_NAME, account.name);
2376 values.put(ACCOUNTS_TYPE, account.type);
2377 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
2378 new String[] {account.name, account.type});
2379 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
2380 if (accountId < 0) {
2381 Log.w(TAG, "insertAccountIntoDatabase: " + account
2382 + ", skipping the DB insert failed");
2383 return false;
2384 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002385 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002386 return true;
2387 }
2388
2389 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002390 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
2391 userId = handleIncomingUser(userId);
2392 UserAccounts accounts = getUserAccounts(userId);
2393 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002394 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002395 final ContentValues values = new ContentValues();
2396 values.put(ACCOUNTS_NAME, newName);
2397 values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
2398 int r = db.update(
2399 TABLE_SHARED_ACCOUNTS,
2400 values,
2401 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
2402 new String[] { account.name, account.type });
2403 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002404 int callingUid = getCallingUid();
2405 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
2406 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002407 // Recursively rename the account.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002408 renameAccountInternal(accounts, account, newName, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002409 }
2410 return r > 0;
2411 }
2412
2413 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08002414 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002415 return removeSharedAccountAsUser(account, userId, getCallingUid());
2416 }
2417
2418 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08002419 userId = handleIncomingUser(userId);
2420 UserAccounts accounts = getUserAccounts(userId);
2421 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002422 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002423 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
2424 new String[] {account.name, account.type});
2425 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002426 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
2427 sharedTableAccountId, accounts, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002428 removeAccountInternal(accounts, account);
2429 }
2430 return r > 0;
2431 }
2432
2433 @Override
2434 public Account[] getSharedAccountsAsUser(int userId) {
2435 userId = handleIncomingUser(userId);
2436 UserAccounts accounts = getUserAccounts(userId);
2437 ArrayList<Account> accountList = new ArrayList<Account>();
2438 Cursor cursor = null;
2439 try {
2440 cursor = accounts.openHelper.getReadableDatabase()
2441 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
2442 null, null, null, null, null);
2443 if (cursor != null && cursor.moveToFirst()) {
2444 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
2445 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
2446 do {
2447 accountList.add(new Account(cursor.getString(nameIndex),
2448 cursor.getString(typeIndex)));
2449 } while (cursor.moveToNext());
2450 }
2451 } finally {
2452 if (cursor != null) {
2453 cursor.close();
2454 }
2455 }
2456 Account[] accountArray = new Account[accountList.size()];
2457 accountList.toArray(accountArray);
2458 return accountArray;
2459 }
2460
2461 @Override
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002462 public Account[] getAccounts(String type) {
2463 return getAccountsAsUser(type, UserHandle.getCallingUserId());
2464 }
2465
Amith Yamasani27db4682013-03-30 17:07:47 -07002466 @Override
2467 public Account[] getAccountsForPackage(String packageName, int uid) {
2468 int callingUid = Binder.getCallingUid();
2469 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
2470 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
2471 + callingUid + " with uid=" + uid);
2472 }
2473 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid);
2474 }
2475
Amith Yamasani3b458ad2013-04-18 18:40:07 -07002476 @Override
2477 public Account[] getAccountsByTypeForPackage(String type, String packageName) {
2478 checkBinderPermission(android.Manifest.permission.INTERACT_ACROSS_USERS);
2479 int packageUid = -1;
2480 try {
2481 packageUid = AppGlobals.getPackageManager().getPackageUid(
2482 packageName, UserHandle.getCallingUserId());
2483 } catch (RemoteException re) {
2484 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
2485 return new Account[0];
2486 }
2487 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid);
2488 }
2489
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002490 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002491 public void getAccountsByFeatures(IAccountManagerResponse response,
Fred Quintana33269202009-04-20 16:05:10 -07002492 String type, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -08002493 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2494 Log.v(TAG, "getAccounts: accountType " + type
2495 + ", response " + response
2496 + ", features " + stringArrayToString(features)
2497 + ", caller's uid " + Binder.getCallingUid()
2498 + ", pid " + Binder.getCallingPid());
2499 }
Fred Quintana382601f2010-03-25 12:25:10 -07002500 if (response == null) throw new IllegalArgumentException("response is null");
2501 if (type == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002502 checkReadAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002503 UserAccounts userAccounts = getUserAccountsForCaller();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002504 int callingUid = Binder.getCallingUid();
Fred Quintana33269202009-04-20 16:05:10 -07002505 long identityToken = clearCallingIdentity();
2506 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002507 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002508 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002509 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002510 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002511 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002512 Bundle result = new Bundle();
2513 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
2514 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002515 return;
2516 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002517 new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features,
2518 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07002519 } finally {
2520 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002521 }
2522 }
2523
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002524 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
2525 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
2526 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
2527 try {
2528 if (cursor.moveToNext()) {
2529 return cursor.getLong(0);
2530 }
2531 return -1;
2532 } finally {
2533 cursor.close();
2534 }
2535 }
2536
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002537 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07002538 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002539 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07002540 try {
2541 if (cursor.moveToNext()) {
2542 return cursor.getLong(0);
2543 }
2544 return -1;
2545 } finally {
2546 cursor.close();
2547 }
2548 }
2549
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002550 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07002551 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
2552 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
2553 new String[]{key}, null, null, null);
2554 try {
2555 if (cursor.moveToNext()) {
2556 return cursor.getLong(0);
2557 }
2558 return -1;
2559 } finally {
2560 cursor.close();
2561 }
2562 }
2563
Fred Quintanaa698f422009-04-08 19:14:54 -07002564 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07002565 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07002566 IAccountManagerResponse mResponse;
2567 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07002568 final boolean mExpectActivityLaunch;
2569 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002570 final String mAccountName;
2571 // Indicates if we need to add auth details(like last credential time)
2572 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002573 // If set, we need to update the last authenticated time. This is
2574 // currently
2575 // used on
2576 // successful confirming credentials.
2577 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07002578
Fred Quintana33269202009-04-20 16:05:10 -07002579 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07002580 private int mNumRequestContinued = 0;
2581 private int mNumErrors = 0;
2582
Fred Quintana60307342009-03-24 22:48:12 -07002583 IAccountAuthenticator mAuthenticator = null;
2584
Fred Quintana8570f742010-02-18 10:32:54 -08002585 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002586 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08002587
Amith Yamasani04e0d262012-02-14 11:50:53 -08002588 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002589 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
2590 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002591 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
2592 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
2593 }
2594
2595 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
2596 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
2597 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07002598 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08002599 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07002600 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08002601 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08002602 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07002603 mResponse = response;
2604 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07002605 mExpectActivityLaunch = expectActivityLaunch;
2606 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002607 mAccountName = accountName;
2608 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002609 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002610
Fred Quintanaa698f422009-04-08 19:14:54 -07002611 synchronized (mSessions) {
2612 mSessions.put(toString(), this);
2613 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002614 if (response != null) {
2615 try {
2616 response.asBinder().linkToDeath(this, 0 /* flags */);
2617 } catch (RemoteException e) {
2618 mResponse = null;
2619 binderDied();
2620 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002621 }
Fred Quintana60307342009-03-24 22:48:12 -07002622 }
2623
Fred Quintanaa698f422009-04-08 19:14:54 -07002624 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07002625 if (mResponse == null) {
2626 // this session has already been closed
2627 return null;
2628 }
Fred Quintana60307342009-03-24 22:48:12 -07002629 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07002630 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07002631 return response;
2632 }
2633
Fred Quintanaa698f422009-04-08 19:14:54 -07002634 private void close() {
2635 synchronized (mSessions) {
2636 if (mSessions.remove(toString()) == null) {
2637 // the session was already closed, so bail out now
2638 return;
2639 }
2640 }
2641 if (mResponse != null) {
2642 // stop listening for response deaths
2643 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
2644
2645 // clear this so that we don't accidentally send any further results
2646 mResponse = null;
2647 }
2648 cancelTimeout();
2649 unbind();
2650 }
2651
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002652 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002653 public void binderDied() {
2654 mResponse = null;
2655 close();
2656 }
2657
2658 protected String toDebugString() {
2659 return toDebugString(SystemClock.elapsedRealtime());
2660 }
2661
2662 protected String toDebugString(long now) {
2663 return "Session: expectLaunch " + mExpectActivityLaunch
2664 + ", connected " + (mAuthenticator != null)
2665 + ", stats (" + mNumResults + "/" + mNumRequestContinued
2666 + "/" + mNumErrors + ")"
2667 + ", lifetime " + ((now - mCreationTime) / 1000.0);
2668 }
2669
Fred Quintana60307342009-03-24 22:48:12 -07002670 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07002671 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2672 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
2673 }
Fred Quintanab839afc2009-10-14 15:57:28 -07002674 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07002675 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002676 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07002677 }
2678 }
2679
2680 private void unbind() {
2681 if (mAuthenticator != null) {
2682 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07002683 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07002684 }
2685 }
2686
2687 public void scheduleTimeout() {
2688 mMessageHandler.sendMessageDelayed(
2689 mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
2690 }
2691
2692 public void cancelTimeout() {
2693 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
2694 }
2695
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002696 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07002697 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07002698 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07002699 try {
2700 run();
2701 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002702 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07002703 "remote exception");
2704 }
Fred Quintana60307342009-03-24 22:48:12 -07002705 }
2706
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002707 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07002708 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07002709 mAuthenticator = null;
2710 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07002711 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07002712 try {
2713 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
2714 "disconnected");
2715 } catch (RemoteException e) {
2716 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2717 Log.v(TAG, "Session.onServiceDisconnected: "
2718 + "caught RemoteException while responding", e);
2719 }
2720 }
Fred Quintana60307342009-03-24 22:48:12 -07002721 }
2722 }
2723
Fred Quintanab839afc2009-10-14 15:57:28 -07002724 public abstract void run() throws RemoteException;
2725
Fred Quintana60307342009-03-24 22:48:12 -07002726 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07002727 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07002728 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07002729 try {
2730 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
2731 "timeout");
2732 } catch (RemoteException e) {
2733 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2734 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
2735 e);
2736 }
2737 }
Fred Quintana60307342009-03-24 22:48:12 -07002738 }
2739 }
2740
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002741 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002742 public void onResult(Bundle result) {
2743 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002744 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002745 if (result != null) {
2746 boolean isSuccessfulConfirmCreds = result.getBoolean(
2747 AccountManager.KEY_BOOLEAN_RESULT, false);
2748 boolean isSuccessfulUpdateCreds =
2749 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
2750 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
2751 // We should only update lastAuthenticated time, if
2752 // mUpdateLastAuthenticatedTime is true and the confirmRequest
2753 // or updateRequest was successful
2754 boolean needUpdate = mUpdateLastAuthenticatedTime
2755 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCreds);
2756 if (needUpdate || mAuthDetailsRequired) {
2757 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
2758 if (needUpdate && accountPresent) {
2759 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
2760 }
2761 if (mAuthDetailsRequired) {
2762 long lastAuthenticatedTime = -1;
2763 if (accountPresent) {
2764 lastAuthenticatedTime = DatabaseUtils.longForQuery(
2765 mAccounts.openHelper.getReadableDatabase(),
2766 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
2767 + " from " +
2768 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
2769 + ACCOUNTS_TYPE + "=?",
2770 new String[] {
2771 mAccountName, mAccountType
2772 });
2773 }
2774 result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
2775 lastAuthenticatedTime);
2776 }
2777 }
2778 if (mAuthDetailsRequired) {
2779 long lastAuthenticatedTime = -1;
2780 if (isAccountPresentForCaller(mAccountName, mAccountType)) {
2781 lastAuthenticatedTime = DatabaseUtils.longForQuery(
2782 mAccounts.openHelper.getReadableDatabase(),
2783 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from "
2784 +
2785 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
2786 + ACCOUNTS_TYPE + "=?",
2787 new String[] {
2788 mAccountName, mAccountType
2789 });
2790 }
2791 result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
2792 lastAuthenticatedTime);
2793 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002794 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002795 if (result != null
2796 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2797 /*
2798 * The Authenticator API allows third party authenticators to
2799 * supply arbitrary intents to other apps that they can run,
2800 * this can be very bad when those apps are in the system like
2801 * the System Settings.
2802 */
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002803 int authenticatorUid = Binder.getCallingUid();
Carlos Valdivia416747a2013-10-14 17:22:42 -07002804 long bid = Binder.clearCallingIdentity();
2805 try {
2806 PackageManager pm = mContext.getPackageManager();
2807 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
2808 int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
2809 if (PackageManager.SIGNATURE_MATCH !=
2810 pm.checkSignatures(authenticatorUid, targetUid)) {
2811 throw new SecurityException(
2812 "Activity to be started with KEY_INTENT must " +
2813 "share Authenticator's signatures");
2814 }
2815 } finally {
2816 Binder.restoreCallingIdentity(bid);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002817 }
2818 }
2819 if (result != null
2820 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002821 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2822 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002823 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2824 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002825 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
2826 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002827 }
Fred Quintana60307342009-03-24 22:48:12 -07002828 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002829 IAccountManagerResponse response;
2830 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002831 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07002832 response = mResponse;
2833 } else {
2834 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07002835 }
Fred Quintana60307342009-03-24 22:48:12 -07002836 if (response != null) {
2837 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07002838 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002839 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2840 Log.v(TAG, getClass().getSimpleName()
2841 + " calling onError() on response " + response);
2842 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002843 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07002844 "null bundle returned");
2845 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08002846 if (mStripAuthTokenFromResult) {
2847 result.remove(AccountManager.KEY_AUTHTOKEN);
2848 }
Fred Quintana56285a62010-12-02 14:20:51 -08002849 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2850 Log.v(TAG, getClass().getSimpleName()
2851 + " calling onResult() on response " + response);
2852 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002853 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
2854 (intent == null)) {
2855 // All AccountManager error codes are greater than 0
2856 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
2857 result.getString(AccountManager.KEY_ERROR_MESSAGE));
2858 } else {
2859 response.onResult(result);
2860 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002861 }
Fred Quintana60307342009-03-24 22:48:12 -07002862 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07002863 // if the caller is dead then there is no one to care about remote exceptions
2864 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2865 Log.v(TAG, "failure while notifying response", e);
2866 }
Fred Quintana60307342009-03-24 22:48:12 -07002867 }
2868 }
2869 }
Fred Quintana60307342009-03-24 22:48:12 -07002870
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002871 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002872 public void onRequestContinued() {
2873 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07002874 }
2875
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002876 @Override
Fred Quintana60307342009-03-24 22:48:12 -07002877 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07002878 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07002879 IAccountManagerResponse response = getResponseAndClose();
2880 if (response != null) {
2881 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08002882 Log.v(TAG, getClass().getSimpleName()
2883 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07002884 }
2885 try {
2886 response.onError(errorCode, errorMessage);
2887 } catch (RemoteException e) {
2888 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2889 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
2890 }
2891 }
2892 } else {
2893 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2894 Log.v(TAG, "Session.onError: already closed");
2895 }
Fred Quintana60307342009-03-24 22:48:12 -07002896 }
2897 }
Fred Quintanab839afc2009-10-14 15:57:28 -07002898
2899 /**
2900 * find the component name for the authenticator and initiate a bind
2901 * if no authenticator or the bind fails then return false, otherwise return true
2902 */
2903 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002904 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
2905 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2906 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07002907 if (authenticatorInfo == null) {
2908 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2909 Log.v(TAG, "there is no authenticator for " + authenticatorType
2910 + ", bailing out");
2911 }
2912 return false;
2913 }
2914
2915 Intent intent = new Intent();
2916 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
2917 intent.setComponent(authenticatorInfo.componentName);
2918 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2919 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
2920 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08002921 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
2922 new UserHandle(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07002923 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2924 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
2925 }
2926 return false;
2927 }
2928
2929
2930 return true;
2931 }
Fred Quintana60307342009-03-24 22:48:12 -07002932 }
2933
2934 private class MessageHandler extends Handler {
2935 MessageHandler(Looper looper) {
2936 super(looper);
2937 }
Costin Manolache3348f142009-09-29 18:58:36 -07002938
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002939 @Override
Fred Quintana60307342009-03-24 22:48:12 -07002940 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07002941 switch (msg.what) {
2942 case MESSAGE_TIMED_OUT:
2943 Session session = (Session)msg.obj;
2944 session.onTimedOut();
2945 break;
2946
Amith Yamasani5be347b2013-03-31 17:44:31 -07002947 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00002948 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07002949 break;
2950
Fred Quintana60307342009-03-24 22:48:12 -07002951 default:
2952 throw new IllegalStateException("unhandled message: " + msg.what);
2953 }
2954 }
2955 }
2956
Amith Yamasani04e0d262012-02-14 11:50:53 -08002957 private static String getDatabaseName(int userId) {
2958 File systemDir = Environment.getSystemSecureDirectory();
Amith Yamasani61f57372012-08-31 12:12:28 -07002959 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002960 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07002961 // Migrate old file, if it exists, to the new location.
2962 // Make sure the new file doesn't already exist. A dummy file could have been
2963 // accidentally created in the old location, causing the new one to become corrupted
2964 // as well.
Amith Yamasani04e0d262012-02-14 11:50:53 -08002965 File oldFile = new File(systemDir, DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07002966 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07002967 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07002968 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07002969 if (!userDir.exists()) {
2970 if (!userDir.mkdirs()) {
2971 throw new IllegalStateException("User dir cannot be created: " + userDir);
2972 }
2973 }
2974 if (!oldFile.renameTo(databaseFile)) {
2975 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
2976 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002977 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08002978 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002979 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08002980 }
2981
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002982 private static class DebugDbHelper{
2983 private DebugDbHelper() {
2984 }
2985
2986 private static String TABLE_DEBUG = "debug_table";
2987
2988 // Columns for the table
2989 private static String ACTION_TYPE = "action_type";
2990 private static String TIMESTAMP = "time";
2991 private static String CALLER_UID = "caller_uid";
2992 private static String TABLE_NAME = "table_name";
2993 private static String KEY = "primary_key";
2994
2995 // These actions correspond to the occurrence of real actions. Since
2996 // these are called by the authenticators, the uid associated will be
2997 // of the authenticator.
2998 private static String ACTION_SET_PASSWORD = "action_set_password";
2999 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
3000 private static String ACTION_ACCOUNT_ADD = "action_account_add";
3001 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
3002 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
3003 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
3004
3005 // These actions don't necessarily correspond to any action on
3006 // accountDb taking place. As an example, there might be a request for
3007 // addingAccount, which might not lead to addition of account on grounds
3008 // of bad authentication. We will still be logging it to keep track of
3009 // who called.
3010 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
3011 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
3012 private static String ACTION_CALLED_ACCOUNT_RENAME = "action_called_account_rename";
3013
3014 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3015
3016 private static String UPDATE_WHERE_CLAUSE = KEY + "=?";
3017
3018 private static void createDebugTable(SQLiteDatabase db) {
3019 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
3020 + ACCOUNTS_ID + " INTEGER,"
3021 + ACTION_TYPE + " TEXT NOT NULL, "
3022 + TIMESTAMP + " DATETIME,"
3023 + CALLER_UID + " INTEGER NOT NULL,"
3024 + TABLE_NAME + " TEXT NOT NULL,"
3025 + KEY + " INTEGER PRIMARY KEY)");
3026 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
3027 }
3028 }
3029
3030 private void logRecord(UserAccounts accounts, String action, String tableName) {
3031 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3032 logRecord(db, action, tableName, -1, accounts);
3033 }
3034
3035 /*
3036 * This function receives an opened writable database.
3037 */
3038 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3039 UserAccounts userAccount) {
3040 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
3041 }
3042
3043 /*
3044 * This function receives an opened writable database.
3045 */
3046 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3047 UserAccounts userAccount, int callingUid) {
3048 SQLiteStatement logStatement = userAccount.statementForLogging;
3049 logStatement.bindLong(1, accountId);
3050 logStatement.bindString(2, action);
3051 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
3052 logStatement.bindLong(4, callingUid);
3053 logStatement.bindString(5, tableName);
3054 logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
3055 logStatement.execute();
3056 logStatement.clearBindings();
3057 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
3058 % MAX_DEBUG_DB_SIZE;
3059 }
3060
3061 /*
3062 * This should only be called once to compile the sql statement for logging
3063 * and to find the insertion point.
3064 */
3065 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
3066 UserAccounts userAccount) {
3067 // Initialize the count if not done earlier.
3068 int size = (int) getDebugTableRowCount(db);
3069 if (size >= MAX_DEBUG_DB_SIZE) {
3070 // Table is full, and we need to find the point where to insert.
3071 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
3072 } else {
3073 userAccount.debugDbInsertionPoint = size;
3074 }
3075 compileSqlStatementForLogging(db, userAccount);
3076 }
3077
3078 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
3079 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
3080 + " VALUES (?,?,?,?,?,?)";
3081 userAccount.statementForLogging = db.compileStatement(sql);
3082 }
3083
3084 private long getDebugTableRowCount(SQLiteDatabase db) {
3085 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
3086 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3087 }
3088
3089 /*
3090 * Finds the row key where the next insertion should take place. This should
3091 * be invoked only if the table has reached its full capacity.
3092 */
3093 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
3094 // This query finds the smallest timestamp value (and if 2 records have
3095 // same timestamp, the choose the lower id).
3096 String queryCountDebugDbRows = new StringBuilder()
3097 .append("SELECT ").append(DebugDbHelper.KEY)
3098 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
3099 .append(" ORDER BY ")
3100 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
3101 .append(" LIMIT 1")
3102 .toString();
3103 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3104 }
3105
Amith Yamasani04e0d262012-02-14 11:50:53 -08003106 static class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08003107
Amith Yamasani04e0d262012-02-14 11:50:53 -08003108 public DatabaseHelper(Context context, int userId) {
3109 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
Fred Quintana60307342009-03-24 22:48:12 -07003110 }
3111
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003112 /**
3113 * This call needs to be made while the mCacheLock is held. The way to
3114 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
3115 * @param db The database.
3116 */
Fred Quintana60307342009-03-24 22:48:12 -07003117 @Override
3118 public void onCreate(SQLiteDatabase db) {
3119 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
3120 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3121 + ACCOUNTS_NAME + " TEXT NOT NULL, "
3122 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
3123 + ACCOUNTS_PASSWORD + " TEXT, "
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003124 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003125 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
Fred Quintana60307342009-03-24 22:48:12 -07003126 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
3127
3128 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
3129 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3130 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
3131 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
3132 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
3133 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
3134
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003135 createGrantsTable(db);
3136
Fred Quintana60307342009-03-24 22:48:12 -07003137 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
3138 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3139 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
3140 + EXTRAS_KEY + " TEXT NOT NULL, "
3141 + EXTRAS_VALUE + " TEXT, "
3142 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
3143
3144 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
3145 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
3146 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07003147
Amith Yamasani67df64b2012-12-14 12:09:36 -08003148 createSharedAccountsTable(db);
3149
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003150 createAccountsDeletionTrigger(db);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003151
3152 DebugDbHelper.createDebugTable(db);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003153 }
3154
Amith Yamasani67df64b2012-12-14 12:09:36 -08003155 private void createSharedAccountsTable(SQLiteDatabase db) {
3156 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
3157 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3158 + ACCOUNTS_NAME + " TEXT NOT NULL, "
3159 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
3160 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
3161 }
3162
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003163 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
3164 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
3165 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
3166 }
3167
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003168 private void addOldAccountNameColumn(SQLiteDatabase db) {
3169 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
3170 }
3171
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003172 private void addDebugTable(SQLiteDatabase db) {
3173 DebugDbHelper.createDebugTable(db);
3174 }
3175
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003176 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003177 db.execSQL(""
3178 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
3179 + " BEGIN"
3180 + " DELETE FROM " + TABLE_AUTHTOKENS
3181 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
3182 + " DELETE FROM " + TABLE_EXTRAS
3183 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003184 + " DELETE FROM " + TABLE_GRANTS
3185 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07003186 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07003187 }
3188
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003189 private void createGrantsTable(SQLiteDatabase db) {
3190 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
3191 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
3192 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
3193 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
3194 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
3195 + "," + GRANTS_GRANTEE_UID + "))");
3196 }
3197
Fred Quintana60307342009-03-24 22:48:12 -07003198 @Override
3199 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003200 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07003201
Fred Quintanaa698f422009-04-08 19:14:54 -07003202 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003203 // no longer need to do anything since the work is done
3204 // when upgrading from version 2
3205 oldVersion++;
3206 }
3207
3208 if (oldVersion == 2) {
3209 createGrantsTable(db);
3210 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
3211 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07003212 oldVersion++;
3213 }
Costin Manolache3348f142009-09-29 18:58:36 -07003214
3215 if (oldVersion == 3) {
3216 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
3217 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
3218 oldVersion++;
3219 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08003220
3221 if (oldVersion == 4) {
3222 createSharedAccountsTable(db);
3223 oldVersion++;
3224 }
3225
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003226 if (oldVersion == 5) {
3227 addOldAccountNameColumn(db);
3228 oldVersion++;
3229 }
3230
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003231 if (oldVersion == 6) {
3232 addLastSuccessfullAuthenticatedTimeColumn(db);
3233 oldVersion++;
3234 }
3235
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003236 if (oldVersion == 7) {
3237 addDebugTable(db);
3238 oldVersion++;
3239 }
3240
Amith Yamasani67df64b2012-12-14 12:09:36 -08003241 if (oldVersion != newVersion) {
3242 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
3243 }
Fred Quintana60307342009-03-24 22:48:12 -07003244 }
3245
3246 @Override
3247 public void onOpen(SQLiteDatabase db) {
3248 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
3249 }
3250 }
3251
Fred Quintana60307342009-03-24 22:48:12 -07003252 public IBinder onBind(Intent intent) {
3253 return asBinder();
3254 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003255
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003256 /**
3257 * Searches array of arguments for the specified string
3258 * @param args array of argument strings
3259 * @param value value to search for
3260 * @return true if the value is contained in the array
3261 */
3262 private static boolean scanArgs(String[] args, String value) {
3263 if (args != null) {
3264 for (String arg : args) {
3265 if (value.equals(arg)) {
3266 return true;
3267 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003268 }
3269 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003270 return false;
3271 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003272
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003273 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003274 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07003275 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
3276 != PackageManager.PERMISSION_GRANTED) {
3277 fout.println("Permission Denial: can't dump AccountsManager from from pid="
3278 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
3279 + " without permission " + android.Manifest.permission.DUMP);
3280 return;
3281 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003282 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003283 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07003284
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003285 final List<UserInfo> users = getUserManager().getUsers();
3286 for (UserInfo user : users) {
3287 ipw.println("User " + user + ":");
3288 ipw.increaseIndent();
3289 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
3290 ipw.println();
3291 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003292 }
3293 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003294
Amith Yamasani04e0d262012-02-14 11:50:53 -08003295 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
3296 String[] args, boolean isCheckinRequest) {
3297 synchronized (userAccounts.cacheLock) {
3298 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003299
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003300 if (isCheckinRequest) {
3301 // This is a checkin request. *Only* upload the account types and the count of each.
3302 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
3303 null, null, ACCOUNTS_TYPE, null, null);
3304 try {
3305 while (cursor.moveToNext()) {
3306 // print type,count
3307 fout.println(cursor.getString(0) + "," + cursor.getString(1));
3308 }
3309 } finally {
3310 if (cursor != null) {
3311 cursor.close();
3312 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003313 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003314 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003315 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07003316 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003317 fout.println("Accounts: " + accounts.length);
3318 for (Account account : accounts) {
3319 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003320 }
Fred Quintana307da1a2010-01-21 14:24:20 -08003321
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003322 // Add debug information.
3323 fout.println();
3324 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
3325 null, null, null, null, DebugDbHelper.TIMESTAMP);
3326 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
3327 fout.println("Accounts History");
3328 try {
3329 while (cursor.moveToNext()) {
3330 // print type,count
3331 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
3332 cursor.getString(2) + "," + cursor.getString(3) + ","
3333 + cursor.getString(4) + "," + cursor.getString(5));
3334 }
3335 } finally {
3336 cursor.close();
3337 }
3338
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003339 fout.println();
3340 synchronized (mSessions) {
3341 final long now = SystemClock.elapsedRealtime();
3342 fout.println("Active Sessions: " + mSessions.size());
3343 for (Session session : mSessions.values()) {
3344 fout.println(" " + session.toDebugString(now));
3345 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003346 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003347
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003348 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003349 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003350 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003351 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003352 }
3353
Amith Yamasani04e0d262012-02-14 11:50:53 -08003354 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Dianne Hackborn41203752012-08-31 14:05:51 -07003355 Intent intent, int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003356 long identityToken = clearCallingIdentity();
3357 try {
3358 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3359 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
3360 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003361
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003362 if (intent.getComponent() != null &&
3363 GrantCredentialsPermissionActivity.class.getName().equals(
3364 intent.getComponent().getClassName())) {
Dianne Hackborn41203752012-08-31 14:05:51 -07003365 createNoCredentialsPermissionNotification(account, intent, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003366 } else {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003367 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07003368 intent.addCategory(String.valueOf(notificationId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003369 Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
3370 0 /* when */);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003371 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003372 Context contextForUser = getContextForUser(user);
Fred Quintana33f889a2009-09-14 17:31:26 -07003373 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003374 contextForUser.getText(R.string.notification_title).toString();
Alan Viverette4a357cd2015-03-18 18:37:18 -07003375 n.color = contextForUser.getColor(
Selim Cinek255dd042014-08-19 22:29:02 +02003376 com.android.internal.R.color.system_notification_accent_color);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003377 n.setLatestEventInfo(contextForUser,
Fred Quintana33f889a2009-09-14 17:31:26 -07003378 String.format(notificationTitleFormat, account.name),
Dianne Hackborn41203752012-08-31 14:05:51 -07003379 message, PendingIntent.getActivityAsUser(
3380 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003381 null, user));
3382 installNotification(notificationId, n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003383 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003384 } finally {
3385 restoreCallingIdentity(identityToken);
3386 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003387 }
3388
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003389 protected void installNotification(final int notificationId, final Notification n,
3390 UserHandle user) {
Fred Quintana56285a62010-12-02 14:20:51 -08003391 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003392 .notifyAsUser(null, notificationId, n, user);
Fred Quintana56285a62010-12-02 14:20:51 -08003393 }
3394
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003395 protected void cancelNotification(int id, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003396 long identityToken = clearCallingIdentity();
3397 try {
3398 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003399 .cancelAsUser(null, id, user);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003400 } finally {
3401 restoreCallingIdentity(identityToken);
3402 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003403 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003404
Fred Quintanab38eb142010-02-24 13:40:54 -08003405 /** Succeeds if any of the specified permissions are granted. */
3406 private void checkBinderPermission(String... permissions) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003407 final int uid = Binder.getCallingUid();
Fred Quintanab38eb142010-02-24 13:40:54 -08003408
3409 for (String perm : permissions) {
3410 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
3411 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08003412 Log.v(TAG, " caller uid " + uid + " has " + perm);
Fred Quintanab38eb142010-02-24 13:40:54 -08003413 }
3414 return;
3415 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003416 }
Fred Quintanab38eb142010-02-24 13:40:54 -08003417
3418 String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
Fred Quintana56285a62010-12-02 14:20:51 -08003419 Log.w(TAG, " " + msg);
Fred Quintanab38eb142010-02-24 13:40:54 -08003420 throw new SecurityException(msg);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003421 }
3422
Amith Yamasani67df64b2012-12-14 12:09:36 -08003423 private int handleIncomingUser(int userId) {
3424 try {
3425 return ActivityManagerNative.getDefault().handleIncomingUser(
3426 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
3427 } catch (RemoteException re) {
3428 // Shouldn't happen, local.
3429 }
3430 return userId;
3431 }
3432
Christopher Tateccbf84f2013-05-08 15:25:41 -07003433 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003434 final int callingUserId = UserHandle.getUserId(callingUid);
3435
3436 final PackageManager userPackageManager;
3437 try {
3438 userPackageManager = mContext.createPackageContextAsUser(
3439 "android", 0, new UserHandle(callingUserId)).getPackageManager();
3440 } catch (NameNotFoundException e) {
3441 return false;
3442 }
3443
3444 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07003445 for (String name : packages) {
3446 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003447 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08003448 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08003449 && (packageInfo.applicationInfo.privateFlags
3450 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07003451 return true;
3452 }
3453 } catch (PackageManager.NameNotFoundException e) {
3454 return false;
3455 }
3456 }
3457 return false;
3458 }
3459
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003460 private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
Christopher Tateccbf84f2013-05-08 15:25:41 -07003461 final boolean isPrivileged = isPrivileged(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07003462 final boolean fromAuthenticator = account != null
3463 && hasAuthenticatorUid(account.type, callerUid);
3464 final boolean hasExplicitGrants = account != null
Amith Yamasani04e0d262012-02-14 11:50:53 -08003465 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003466 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3467 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08003468 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003469 + ": is authenticator? " + fromAuthenticator
3470 + ", has explicit permission? " + hasExplicitGrants);
3471 }
Christopher Tateccbf84f2013-05-08 15:25:41 -07003472 return fromAuthenticator || hasExplicitGrants || isPrivileged;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003473 }
3474
Fred Quintana1a231912009-10-15 11:31:30 -07003475 private boolean hasAuthenticatorUid(String accountType, int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003476 final int callingUserId = UserHandle.getUserId(callingUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003477 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003478 mAuthenticatorCache.getAllServices(callingUserId)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003479 if (serviceInfo.type.type.equals(accountType)) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003480 return (serviceInfo.uid == callingUid) ||
Fred Quintana56285a62010-12-02 14:20:51 -08003481 (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003482 == PackageManager.SIGNATURE_MATCH);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003483 }
3484 }
3485 return false;
3486 }
3487
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003488 private boolean isAccountPresentForCaller(String accountName, String accountType) {
3489 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
3490 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
3491 if (account.name.equals(accountName)) {
3492 return true;
3493 }
3494 }
3495 }
3496 return false;
3497 }
3498
Amith Yamasani04e0d262012-02-14 11:50:53 -08003499 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
3500 int callerUid) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003501 if (callerUid == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003502 return true;
3503 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003504 UserAccounts accounts = getUserAccountsForCaller();
3505 synchronized (accounts.cacheLock) {
3506 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
3507 String[] args = { String.valueOf(callerUid), authTokenType,
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003508 account.name, account.type};
3509 final boolean permissionGranted =
3510 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
3511 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
3512 // TODO: Skip this check when running automated tests. Replace this
3513 // with a more general solution.
3514 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08003515 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003516 + " but ignoring since device is in test harness.");
3517 return true;
3518 }
3519 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003520 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003521 }
3522
3523 private void checkCallingUidAgainstAuthenticator(Account account) {
3524 final int uid = Binder.getCallingUid();
Fred Quintana31957f12009-10-21 13:43:10 -07003525 if (account == null || !hasAuthenticatorUid(account.type, uid)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003526 String msg = "caller uid " + uid + " is different than the authenticator's uid";
3527 Log.w(TAG, msg);
3528 throw new SecurityException(msg);
3529 }
3530 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3531 Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
3532 }
3533 }
3534
3535 private void checkAuthenticateAccountsPermission(Account account) {
3536 checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
3537 checkCallingUidAgainstAuthenticator(account);
3538 }
3539
3540 private void checkReadAccountsPermission() {
3541 checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
3542 }
3543
3544 private void checkManageAccountsPermission() {
3545 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
3546 }
3547
Fred Quintanab38eb142010-02-24 13:40:54 -08003548 private void checkManageAccountsOrUseCredentialsPermissions() {
3549 checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
3550 Manifest.permission.USE_CREDENTIALS);
3551 }
3552
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003553 private boolean canUserModifyAccounts(int userId) {
3554 if (getUserManager().getUserRestrictions(new UserHandle(userId))
3555 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
3556 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08003557 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003558 return true;
3559 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01003560
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003561 private boolean canUserModifyAccountsForType(int userId, String accountType) {
Sander Alewijnseda1350f2014-05-08 16:59:42 +01003562 DevicePolicyManager dpm = (DevicePolicyManager) mContext
3563 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003564 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02003565 if (typesArray == null) {
3566 return true;
3567 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01003568 for (String forbiddenType : typesArray) {
3569 if (forbiddenType.equals(accountType)) {
3570 return false;
3571 }
3572 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08003573 return true;
3574 }
3575
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003576 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07003577 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
3578 throws RemoteException {
3579 final int callingUid = getCallingUid();
3580
Amith Yamasani27db4682013-03-30 17:07:47 -07003581 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07003582 throw new SecurityException();
3583 }
3584
3585 if (value) {
3586 grantAppPermission(account, authTokenType, uid);
3587 } else {
3588 revokeAppPermission(account, authTokenType, uid);
3589 }
3590 }
3591
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003592 /**
3593 * Allow callers with the given uid permission to get credentials for account/authTokenType.
3594 * <p>
3595 * Although this is public it can only be accessed via the AccountManagerService object
3596 * which is in the system. This means we don't need to protect it with permissions.
3597 * @hide
3598 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07003599 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07003600 if (account == null || authTokenType == null) {
3601 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07003602 return;
3603 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003604 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003605 synchronized (accounts.cacheLock) {
3606 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003607 db.beginTransaction();
3608 try {
3609 long accountId = getAccountIdLocked(db, account);
3610 if (accountId >= 0) {
3611 ContentValues values = new ContentValues();
3612 values.put(GRANTS_ACCOUNTS_ID, accountId);
3613 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
3614 values.put(GRANTS_GRANTEE_UID, uid);
3615 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
3616 db.setTransactionSuccessful();
3617 }
3618 } finally {
3619 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003620 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003621 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
3622 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003623 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003624 }
3625
3626 /**
3627 * Don't allow callers with the given uid permission to get credentials for
3628 * account/authTokenType.
3629 * <p>
3630 * Although this is public it can only be accessed via the AccountManagerService object
3631 * which is in the system. This means we don't need to protect it with permissions.
3632 * @hide
3633 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07003634 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07003635 if (account == null || authTokenType == null) {
3636 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07003637 return;
3638 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003639 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003640 synchronized (accounts.cacheLock) {
3641 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003642 db.beginTransaction();
3643 try {
3644 long accountId = getAccountIdLocked(db, account);
3645 if (accountId >= 0) {
3646 db.delete(TABLE_GRANTS,
3647 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
3648 + GRANTS_GRANTEE_UID + "=?",
3649 new String[]{String.valueOf(accountId), authTokenType,
3650 String.valueOf(uid)});
3651 db.setTransactionSuccessful();
3652 }
3653 } finally {
3654 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003655 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003656 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
3657 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003658 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003659 }
Fred Quintana56285a62010-12-02 14:20:51 -08003660
3661 static final private String stringArrayToString(String[] value) {
3662 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
3663 }
3664
Amith Yamasani04e0d262012-02-14 11:50:53 -08003665 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
3666 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003667 if (oldAccountsForType != null) {
3668 ArrayList<Account> newAccountsList = new ArrayList<Account>();
3669 for (Account curAccount : oldAccountsForType) {
3670 if (!curAccount.equals(account)) {
3671 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08003672 }
3673 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003674 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003675 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003676 } else {
3677 Account[] newAccountsForType = new Account[newAccountsList.size()];
3678 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003679 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003680 }
Fred Quintana56285a62010-12-02 14:20:51 -08003681 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003682 accounts.userDataCache.remove(account);
3683 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003684 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08003685 }
3686
3687 /**
3688 * This assumes that the caller has already checked that the account is not already present.
3689 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08003690 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
3691 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003692 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
3693 Account[] newAccountsForType = new Account[oldLength + 1];
3694 if (accountsForType != null) {
3695 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08003696 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003697 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003698 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08003699 }
3700
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003701 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07003702 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003703 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07003704 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003705 return unfiltered;
3706 }
Amith Yamasani0c19bf52013-10-03 10:34:58 -07003707 UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
3708 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003709 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003710 // If any of the packages is a white listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003711 // otherwise return non-shared accounts only.
3712 // This might be a temporary way to specify a whitelist
3713 String whiteList = mContext.getResources().getString(
3714 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
3715 for (String packageName : packages) {
3716 if (whiteList.contains(";" + packageName + ";")) {
3717 return unfiltered;
3718 }
3719 }
3720 ArrayList<Account> allowed = new ArrayList<Account>();
3721 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
3722 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003723 String requiredAccountType = "";
3724 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07003725 // If there's an explicit callingPackage specified, check if that package
3726 // opted in to see restricted accounts.
3727 if (callingPackage != null) {
3728 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003729 if (pi != null && pi.restrictedAccountType != null) {
3730 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07003731 }
3732 } else {
3733 // Otherwise check if the callingUid has a package that has opted in
3734 for (String packageName : packages) {
3735 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
3736 if (pi != null && pi.restrictedAccountType != null) {
3737 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07003738 break;
3739 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003740 }
3741 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003742 } catch (NameNotFoundException nnfe) {
3743 }
3744 for (Account account : unfiltered) {
3745 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003746 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003747 } else {
3748 boolean found = false;
3749 for (Account shared : sharedAccounts) {
3750 if (shared.equals(account)) {
3751 found = true;
3752 break;
3753 }
3754 }
3755 if (!found) {
3756 allowed.add(account);
3757 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003758 }
3759 }
3760 Account[] filtered = new Account[allowed.size()];
3761 allowed.toArray(filtered);
3762 return filtered;
3763 } else {
3764 return unfiltered;
3765 }
3766 }
3767
Amith Yamasani27db4682013-03-30 17:07:47 -07003768 /*
3769 * packageName can be null. If not null, it should be used to filter out restricted accounts
3770 * that the package is not allowed to access.
3771 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003772 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07003773 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003774 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003775 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003776 if (accounts == null) {
3777 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08003778 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003779 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07003780 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08003781 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003782 } else {
3783 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003784 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003785 totalLength += accounts.length;
3786 }
3787 if (totalLength == 0) {
3788 return EMPTY_ACCOUNT_ARRAY;
3789 }
3790 Account[] accounts = new Account[totalLength];
3791 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003792 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003793 System.arraycopy(accountsOfType, 0, accounts, totalLength,
3794 accountsOfType.length);
3795 totalLength += accountsOfType.length;
3796 }
Amith Yamasani27db4682013-03-30 17:07:47 -07003797 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08003798 }
3799 }
3800
Amith Yamasani04e0d262012-02-14 11:50:53 -08003801 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
3802 Account account, String key, String value) {
3803 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003804 if (userDataForAccount == null) {
3805 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003806 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003807 }
3808 if (value == null) {
3809 userDataForAccount.remove(key);
3810 } else {
3811 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08003812 }
3813 }
3814
Amith Yamasani04e0d262012-02-14 11:50:53 -08003815 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
3816 Account account, String key, String value) {
3817 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003818 if (authTokensForAccount == null) {
3819 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003820 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003821 }
3822 if (value == null) {
3823 authTokensForAccount.remove(key);
3824 } else {
3825 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08003826 }
3827 }
3828
Amith Yamasani04e0d262012-02-14 11:50:53 -08003829 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
3830 String authTokenType) {
3831 synchronized (accounts.cacheLock) {
3832 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08003833 if (authTokensForAccount == null) {
3834 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08003835 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003836 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003837 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08003838 }
3839 return authTokensForAccount.get(authTokenType);
3840 }
3841 }
3842
Amith Yamasani04e0d262012-02-14 11:50:53 -08003843 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
3844 synchronized (accounts.cacheLock) {
3845 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08003846 if (userDataForAccount == null) {
3847 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08003848 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003849 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003850 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08003851 }
3852 return userDataForAccount.get(key);
3853 }
3854 }
3855
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003856 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
3857 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08003858 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08003859 Cursor cursor = db.query(TABLE_EXTRAS,
3860 COLUMNS_EXTRAS_KEY_AND_VALUE,
3861 SELECTION_USERDATA_BY_ACCOUNT,
3862 new String[]{account.name, account.type},
3863 null, null, null);
3864 try {
3865 while (cursor.moveToNext()) {
3866 final String tmpkey = cursor.getString(0);
3867 final String value = cursor.getString(1);
3868 userDataForAccount.put(tmpkey, value);
3869 }
3870 } finally {
3871 cursor.close();
3872 }
3873 return userDataForAccount;
3874 }
3875
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003876 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
3877 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08003878 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08003879 Cursor cursor = db.query(TABLE_AUTHTOKENS,
3880 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
3881 SELECTION_AUTHTOKENS_BY_ACCOUNT,
3882 new String[]{account.name, account.type},
3883 null, null, null);
3884 try {
3885 while (cursor.moveToNext()) {
3886 final String type = cursor.getString(0);
3887 final String authToken = cursor.getString(1);
3888 authTokensForAccount.put(type, authToken);
3889 }
3890 } finally {
3891 cursor.close();
3892 }
3893 return authTokensForAccount;
3894 }
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003895
3896 private Context getContextForUser(UserHandle user) {
3897 try {
3898 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
3899 } catch (NameNotFoundException e) {
3900 // Default to mContext, not finding the package system is running as is unlikely.
3901 return mContext;
3902 }
3903 }
Fred Quintana60307342009-03-24 22:48:12 -07003904}