blob: 50d311ff27482cee5d75e39be350bcd0a5c637eb [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;
Carlos Valdivia91979be2015-05-22 14:11:35 -070020import android.accounts.AbstractAccountAuthenticator;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080021import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
25import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070026import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080027import android.accounts.GrantCredentialsPermissionActivity;
28import android.accounts.IAccountAuthenticator;
29import android.accounts.IAccountAuthenticatorResponse;
30import android.accounts.IAccountManager;
31import android.accounts.IAccountManagerResponse;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080032import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070033import android.app.ActivityManagerNative;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070034import android.app.AppGlobals;
Doug Zongker885cfc232009-10-21 16:52:44 -070035import android.app.Notification;
36import android.app.NotificationManager;
37import android.app.PendingIntent;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010038import android.app.admin.DevicePolicyManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070039import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070040import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070041import android.content.ContentValues;
42import android.content.Context;
43import android.content.Intent;
44import android.content.IntentFilter;
Fred Quintanab839afc2009-10-14 15:57:28 -070045import android.content.ServiceConnection;
Doug Zongker885cfc232009-10-21 16:52:44 -070046import android.content.pm.ApplicationInfo;
47import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070048import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070049import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070050import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070051import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070052import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070053import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070054import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070055import android.database.Cursor;
56import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070057import android.database.sqlite.SQLiteDatabase;
58import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070059import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070060import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070061import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080062import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070063import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070064import android.os.IBinder;
65import android.os.Looper;
66import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070067import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070068import android.os.Process;
Fred Quintanaa698f422009-04-08 19:14:54 -070069import android.os.RemoteException;
70import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070071import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070072import android.os.UserManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070073import android.text.TextUtils;
74import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070075import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070076import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080077import android.util.SparseArray;
Fred Quintana60307342009-03-24 22:48:12 -070078
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070079import com.android.internal.R;
Amith Yamasani67df64b2012-12-14 12:09:36 -080080import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080081import com.android.internal.util.IndentingPrintWriter;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070082import com.android.server.FgThread;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070083import com.google.android.collect.Lists;
84import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070085
Oscar Montemayora8529f62009-11-18 10:14:20 -080086import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -070087import java.io.FileDescriptor;
88import java.io.PrintWriter;
Carlos Valdivia91979be2015-05-22 14:11:35 -070089import java.lang.ref.WeakReference;
Carlos Valdivia91979be2015-05-22 14:11:35 -070090import java.security.MessageDigest;
91import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070092import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -070093import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -080094import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -070095import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070096import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070097import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070098import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -080099import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700100import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800101import java.util.Map;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700102import java.util.concurrent.atomic.AtomicInteger;
103import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700104
Fred Quintana60307342009-03-24 22:48:12 -0700105/**
106 * A system service that provides account, password, and authtoken management for all
107 * accounts on the device. Some of these calls are implemented with the help of the corresponding
108 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
109 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700110 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700111 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700112 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700113public class AccountManagerService
114 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800115 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700116 private static final String TAG = "AccountManagerService";
117
118 private static final int TIMEOUT_DELAY_MS = 1000 * 60;
119 private static final String DATABASE_NAME = "accounts.db";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700120 private static final int DATABASE_VERSION = 8;
121
122 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700123
124 private final Context mContext;
125
Fred Quintana56285a62010-12-02 14:20:51 -0800126 private final PackageManager mPackageManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700127 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800128
Fred Quintana60307342009-03-24 22:48:12 -0700129 private final MessageHandler mMessageHandler;
130
131 // Messages that can be sent on mHandler
132 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700133 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700134
Fred Quintana56285a62010-12-02 14:20:51 -0800135 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700136
137 private static final String TABLE_ACCOUNTS = "accounts";
138 private static final String ACCOUNTS_ID = "_id";
139 private static final String ACCOUNTS_NAME = "name";
140 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700141 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700142 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700143 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800144 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
145 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700146
147 private static final String TABLE_AUTHTOKENS = "authtokens";
148 private static final String AUTHTOKENS_ID = "_id";
149 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
150 private static final String AUTHTOKENS_TYPE = "type";
151 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
152
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700153 private static final String TABLE_GRANTS = "grants";
154 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
155 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
156 private static final String GRANTS_GRANTEE_UID = "uid";
157
Fred Quintana60307342009-03-24 22:48:12 -0700158 private static final String TABLE_EXTRAS = "extras";
159 private static final String EXTRAS_ID = "_id";
160 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
161 private static final String EXTRAS_KEY = "key";
162 private static final String EXTRAS_VALUE = "value";
163
164 private static final String TABLE_META = "meta";
165 private static final String META_KEY = "key";
166 private static final String META_VALUE = "value";
167
Amith Yamasani67df64b2012-12-14 12:09:36 -0800168 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
169
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700170 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
171 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700172 private static final Intent ACCOUNTS_CHANGED_INTENT;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700173 static {
174 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
175 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
176 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700177
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700178 private static final String COUNT_OF_MATCHING_GRANTS = ""
179 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
180 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
181 + " AND " + GRANTS_GRANTEE_UID + "=?"
182 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
183 + " AND " + ACCOUNTS_NAME + "=?"
184 + " AND " + ACCOUNTS_TYPE + "=?";
185
Fred Quintana56285a62010-12-02 14:20:51 -0800186 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
187 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700188
Fred Quintana56285a62010-12-02 14:20:51 -0800189 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
190 AUTHTOKENS_AUTHTOKEN};
191
192 private static final String SELECTION_USERDATA_BY_ACCOUNT =
193 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
194 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
195
Fred Quintanaa698f422009-04-08 19:14:54 -0700196 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700197 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
198
Amith Yamasani04e0d262012-02-14 11:50:53 -0800199 static class UserAccounts {
200 private final int userId;
201 private final DatabaseHelper openHelper;
202 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
203 credentialsPermissionNotificationIds =
204 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
205 private final HashMap<Account, Integer> signinRequiredNotificationIds =
206 new HashMap<Account, Integer>();
207 private final Object cacheLock = new Object();
208 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700209 private final HashMap<String, Account[]> accountCache =
210 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800211 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800212 private final HashMap<Account, HashMap<String, String>> userDataCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800213 new HashMap<Account, HashMap<String, String>>();
214 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800215 private final HashMap<Account, HashMap<String, String>> authTokenCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800216 new HashMap<Account, HashMap<String, String>>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700217
218 /** protected by the {@link #cacheLock} */
219 private final HashMap<Account, WeakReference<TokenCache>> accountTokenCaches = new HashMap<>();
220
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700221 /**
222 * protected by the {@link #cacheLock}
223 *
224 * Caches the previous names associated with an account. Previous names
225 * should be cached because we expect that when an Account is renamed,
226 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
227 * want to know if the accounts they care about have been renamed.
228 *
229 * The previous names are wrapped in an {@link AtomicReference} so that
230 * we can distinguish between those accounts with no previous names and
231 * those whose previous names haven't been cached (yet).
232 */
233 private final HashMap<Account, AtomicReference<String>> previousNameCache =
234 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800235
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700236 private int debugDbInsertionPoint = -1;
237 private SQLiteStatement statementForLogging;
238
Amith Yamasani04e0d262012-02-14 11:50:53 -0800239 UserAccounts(Context context, int userId) {
240 this.userId = userId;
241 synchronized (cacheLock) {
242 openHelper = new DatabaseHelper(context, userId);
243 }
244 }
245 }
246
247 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
248
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700249 private static AtomicReference<AccountManagerService> sThis =
250 new AtomicReference<AccountManagerService>();
Fred Quintana31957f12009-10-21 13:43:10 -0700251 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700252
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700253 /**
254 * This should only be called by system code. One should only call this after the service
255 * has started.
256 * @return a reference to the AccountManagerService instance
257 * @hide
258 */
259 public static AccountManagerService getSingleton() {
260 return sThis.get();
261 }
Fred Quintana60307342009-03-24 22:48:12 -0700262
Fred Quintana56285a62010-12-02 14:20:51 -0800263 public AccountManagerService(Context context) {
264 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700265 }
266
Fred Quintana56285a62010-12-02 14:20:51 -0800267 public AccountManagerService(Context context, PackageManager packageManager,
268 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700269 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800270 mPackageManager = packageManager;
Fred Quintana60307342009-03-24 22:48:12 -0700271
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700272 mMessageHandler = new MessageHandler(FgThread.get().getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700273
Fred Quintana56285a62010-12-02 14:20:51 -0800274 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800275 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700276
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700277 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800278
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800279 IntentFilter intentFilter = new IntentFilter();
280 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
281 intentFilter.addDataScheme("package");
282 mContext.registerReceiver(new BroadcastReceiver() {
283 @Override
284 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700285 // Don't delete accounts when updating a authenticator's
286 // package.
287 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
288 purgeOldGrantsAll();
289 }
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800290 }
291 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800292
Amith Yamasani13593602012-03-22 16:16:17 -0700293 IntentFilter userFilter = new IntentFilter();
294 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800295 userFilter.addAction(Intent.ACTION_USER_STARTED);
296 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700297 @Override
298 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800299 String action = intent.getAction();
300 if (Intent.ACTION_USER_REMOVED.equals(action)) {
301 onUserRemoved(intent);
302 } else if (Intent.ACTION_USER_STARTED.equals(action)) {
303 onUserStarted(intent);
304 }
Amith Yamasani13593602012-03-22 16:16:17 -0700305 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800306 }, UserHandle.ALL, userFilter, null, null);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800307 }
308
Dianne Hackborn164371f2013-10-01 19:10:13 -0700309 @Override
310 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
311 throws RemoteException {
312 try {
313 return super.onTransact(code, data, reply, flags);
314 } catch (RuntimeException e) {
315 // The account manager only throws security exceptions, so let's
316 // log all others.
317 if (!(e instanceof SecurityException)) {
318 Slog.wtf(TAG, "Account Manager Crash", e);
319 }
320 throw e;
321 }
322 }
323
Kenny Root26ff6622012-07-30 12:58:03 -0700324 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -0700325 }
326
Amith Yamasani258848d2012-08-10 17:06:33 -0700327 private UserManager getUserManager() {
328 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700329 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700330 }
331 return mUserManager;
332 }
333
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700334 /* Caller should lock mUsers */
335 private UserAccounts initUserLocked(int userId) {
336 UserAccounts accounts = mUsers.get(userId);
337 if (accounts == null) {
338 accounts = new UserAccounts(mContext, userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700339 initializeDebugDbSizeAndCompileSqlStatementForLogging(
340 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700341 mUsers.append(userId, accounts);
342 purgeOldGrants(accounts);
343 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800344 }
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700345 return accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800346 }
347
348 private void purgeOldGrantsAll() {
349 synchronized (mUsers) {
350 for (int i = 0; i < mUsers.size(); i++) {
351 purgeOldGrants(mUsers.valueAt(i));
352 }
353 }
354 }
355
356 private void purgeOldGrants(UserAccounts accounts) {
357 synchronized (accounts.cacheLock) {
358 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800359 final Cursor cursor = db.query(TABLE_GRANTS,
360 new String[]{GRANTS_GRANTEE_UID},
361 null, null, GRANTS_GRANTEE_UID, null, null);
362 try {
363 while (cursor.moveToNext()) {
364 final int uid = cursor.getInt(0);
365 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
366 if (packageExists) {
367 continue;
368 }
369 Log.d(TAG, "deleting grants for UID " + uid
370 + " because its package is no longer installed");
371 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
372 new String[]{Integer.toString(uid)});
373 }
374 } finally {
375 cursor.close();
376 }
377 }
378 }
379
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700380 /**
381 * Validate internal set of accounts against installed authenticators for
382 * given user. Clears cached authenticators before validating.
383 */
384 public void validateAccounts(int userId) {
385 final UserAccounts accounts = getUserAccounts(userId);
386
387 // Invalidate user-specific cache to make sure we catch any
388 // removed authenticators.
389 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
390 }
391
392 /**
393 * Validate internal set of accounts against installed authenticators for
394 * given user. Clear cached authenticators before validating when requested.
395 */
396 private void validateAccountsInternal(
397 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
398 if (invalidateAuthenticatorCache) {
399 mAuthenticatorCache.invalidateCache(accounts.userId);
400 }
401
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700402 final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
403 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
404 mAuthenticatorCache.getAllServices(accounts.userId)) {
405 knownAuth.add(service.type);
406 }
407
Amith Yamasani04e0d262012-02-14 11:50:53 -0800408 synchronized (accounts.cacheLock) {
409 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800410 boolean accountDeleted = false;
411 Cursor cursor = db.query(TABLE_ACCOUNTS,
412 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
Marvin Paul48fcd4e2014-12-01 18:26:07 -0800413 null, null, null, null, ACCOUNTS_ID);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800414 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800415 accounts.accountCache.clear();
Fred Quintana56285a62010-12-02 14:20:51 -0800416 final HashMap<String, ArrayList<String>> accountNamesByType =
Amith Yamasanib483a992012-05-22 13:14:25 -0700417 new LinkedHashMap<String, ArrayList<String>>();
Fred Quintana56285a62010-12-02 14:20:51 -0800418 while (cursor.moveToNext()) {
419 final long accountId = cursor.getLong(0);
420 final String accountType = cursor.getString(1);
421 final String accountName = cursor.getString(2);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700422
423 if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700424 Slog.w(TAG, "deleting account " + accountName + " because type "
Fred Quintana56285a62010-12-02 14:20:51 -0800425 + accountType + " no longer has a registered authenticator");
426 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
427 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700428
429 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
430 accountId, accounts);
431
Fred Quintana56285a62010-12-02 14:20:51 -0800432 final Account account = new Account(accountName, accountType);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800433 accounts.userDataCache.remove(account);
434 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -0700435 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -0800436 } else {
437 ArrayList<String> accountNames = accountNamesByType.get(accountType);
438 if (accountNames == null) {
439 accountNames = new ArrayList<String>();
440 accountNamesByType.put(accountType, accountNames);
441 }
442 accountNames.add(accountName);
443 }
444 }
Andy McFadden2f362292012-01-20 14:43:38 -0800445 for (Map.Entry<String, ArrayList<String>> cur
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800446 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800447 final String accountType = cur.getKey();
448 final ArrayList<String> accountNames = cur.getValue();
449 final Account[] accountsForType = new Account[accountNames.size()];
450 int i = 0;
451 for (String accountName : accountNames) {
452 accountsForType[i] = new Account(accountName, accountType);
453 ++i;
454 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800455 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800456 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800457 } finally {
458 cursor.close();
459 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800460 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800461 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800462 }
463 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700464 }
465
Amith Yamasani04e0d262012-02-14 11:50:53 -0800466 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700467 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -0800468 }
469
470 protected UserAccounts getUserAccounts(int userId) {
471 synchronized (mUsers) {
472 UserAccounts accounts = mUsers.get(userId);
473 if (accounts == null) {
Amith Yamasani0c19bf52013-10-03 10:34:58 -0700474 accounts = initUserLocked(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800475 mUsers.append(userId, accounts);
476 }
477 return accounts;
478 }
479 }
480
Amith Yamasani13593602012-03-22 16:16:17 -0700481 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700482 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -0700483 if (userId < 1) return;
484
485 UserAccounts accounts;
486 synchronized (mUsers) {
487 accounts = mUsers.get(userId);
488 mUsers.remove(userId);
489 }
490 if (accounts == null) {
491 File dbFile = new File(getDatabaseName(userId));
492 dbFile.delete();
493 return;
494 }
495
496 synchronized (accounts.cacheLock) {
497 accounts.openHelper.close();
498 File dbFile = new File(getDatabaseName(userId));
499 dbFile.delete();
500 }
501 }
502
Amith Yamasani67df64b2012-12-14 12:09:36 -0800503 private void onUserStarted(Intent intent) {
504 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
505 if (userId < 1) return;
506
507 // Check if there's a shared account that needs to be created as an account
508 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
509 if (sharedAccounts == null || sharedAccounts.length == 0) return;
510 Account[] accounts = getAccountsAsUser(null, userId);
511 for (Account sa : sharedAccounts) {
512 if (ArrayUtils.contains(accounts, sa)) continue;
513 // Account doesn't exist. Copy it now.
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000514 copyAccountToUser(null /*no response*/, sa, UserHandle.USER_OWNER, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800515 }
516 }
517
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700518 @Override
519 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700520 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -0700521 }
522
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800523 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -0700524 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700525 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800526 if (Log.isLoggable(TAG, Log.VERBOSE)) {
527 Log.v(TAG, "getPassword: " + account
528 + ", caller's uid " + Binder.getCallingUid()
529 + ", pid " + Binder.getCallingPid());
530 }
Fred Quintana382601f2010-03-25 12:25:10 -0700531 if (account == null) throw new IllegalArgumentException("account is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700532 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
533 String msg = String.format(
534 "uid %s cannot get secrets for accounts of type: %s",
535 callingUid,
536 account.type);
537 throw new SecurityException(msg);
538 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800539 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700540 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700541 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800542 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700543 } finally {
544 restoreCallingIdentity(identityToken);
545 }
546 }
547
Amith Yamasani04e0d262012-02-14 11:50:53 -0800548 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700549 if (account == null) {
550 return null;
551 }
552
Amith Yamasani04e0d262012-02-14 11:50:53 -0800553 synchronized (accounts.cacheLock) {
554 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800555 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
556 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
557 new String[]{account.name, account.type}, null, null, null);
558 try {
559 if (cursor.moveToNext()) {
560 return cursor.getString(0);
561 }
562 return null;
563 } finally {
564 cursor.close();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700565 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700566 }
567 }
568
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800569 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700570 public String getPreviousName(Account account) {
571 if (Log.isLoggable(TAG, Log.VERBOSE)) {
572 Log.v(TAG, "getPreviousName: " + account
573 + ", caller's uid " + Binder.getCallingUid()
574 + ", pid " + Binder.getCallingPid());
575 }
576 if (account == null) throw new IllegalArgumentException("account is null");
577 UserAccounts accounts = getUserAccountsForCaller();
578 long identityToken = clearCallingIdentity();
579 try {
580 return readPreviousNameInternal(accounts, account);
581 } finally {
582 restoreCallingIdentity(identityToken);
583 }
584 }
585
586 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
587 if (account == null) {
588 return null;
589 }
590 synchronized (accounts.cacheLock) {
591 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
592 if (previousNameRef == null) {
593 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
594 Cursor cursor = db.query(
595 TABLE_ACCOUNTS,
596 new String[]{ ACCOUNTS_PREVIOUS_NAME },
597 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
598 new String[] { account.name, account.type },
599 null,
600 null,
601 null);
602 try {
603 if (cursor.moveToNext()) {
604 String previousName = cursor.getString(0);
605 previousNameRef = new AtomicReference<String>(previousName);
606 accounts.previousNameCache.put(account, previousNameRef);
607 return previousName;
608 } else {
609 return null;
610 }
611 } finally {
612 cursor.close();
613 }
614 } else {
615 return previousNameRef.get();
616 }
617 }
618 }
619
620 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700621 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700622 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800623 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700624 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
625 account, key, callingUid, Binder.getCallingPid());
626 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -0800627 }
Fred Quintana382601f2010-03-25 12:25:10 -0700628 if (account == null) throw new IllegalArgumentException("account is null");
629 if (key == null) throw new IllegalArgumentException("key is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700630 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
631 String msg = String.format(
632 "uid %s cannot get user data for accounts of type: %s",
633 callingUid,
634 account.type);
635 throw new SecurityException(msg);
636 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800637 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700638 long identityToken = clearCallingIdentity();
639 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800640 return readUserDataInternal(accounts, account, key);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700641 } finally {
642 restoreCallingIdentity(identityToken);
643 }
644 }
645
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800646 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100647 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -0800648 if (Log.isLoggable(TAG, Log.VERBOSE)) {
649 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100650 + "for user id " + userId
Fred Quintana56285a62010-12-02 14:20:51 -0800651 + "caller's uid " + Binder.getCallingUid()
652 + ", pid " + Binder.getCallingPid());
653 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100654 // Only allow the system process to read accounts of other users
655 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
656 + " trying get authenticator types for " + userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700657 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700658 try {
Fred Quintana97889762009-06-15 12:29:24 -0700659 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700660 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
Fred Quintana97889762009-06-15 12:29:24 -0700661 AuthenticatorDescription[] types =
662 new AuthenticatorDescription[authenticatorCollection.size()];
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700663 int i = 0;
Fred Quintana97889762009-06-15 12:29:24 -0700664 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
Fred Quintana718d8a22009-04-29 17:53:20 -0700665 : authenticatorCollection) {
666 types[i] = authenticator.type;
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700667 i++;
668 }
669 return types;
670 } finally {
671 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700672 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700673 }
674
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100675 private void enforceCrossUserPermission(int userId, String errorMessage) {
676 if (userId != UserHandle.getCallingUserId()
677 && Binder.getCallingUid() != Process.myUid()
678 && mContext.checkCallingOrSelfPermission(
679 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
680 != PackageManager.PERMISSION_GRANTED) {
681 throw new SecurityException(errorMessage);
682 }
683 }
684
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700685 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -0700686 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700687 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800688 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700689 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700690 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -0800691 + ", pid " + Binder.getCallingPid());
692 }
Fred Quintana382601f2010-03-25 12:25:10 -0700693 if (account == null) throw new IllegalArgumentException("account is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700694 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
695 String msg = String.format(
696 "uid %s cannot explicitly add accounts of type: %s",
697 callingUid,
698 account.type);
699 throw new SecurityException(msg);
700 }
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700701 /*
702 * Child users are not allowed to add accounts. Only the accounts that are
703 * shared by the parent profile can be added to child profile.
704 *
705 * TODO: Only allow accounts that were shared to be added by
706 * a limited user.
707 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700708
Amith Yamasani04e0d262012-02-14 11:50:53 -0800709 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana60307342009-03-24 22:48:12 -0700710 // fails if the account already exists
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700711 int uid = getCallingUid();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700712 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700713 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700714 return addAccountInternal(accounts, account, password, extras, false, uid);
Fred Quintana60307342009-03-24 22:48:12 -0700715 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700716 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700717 }
718 }
719
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000720 @Override
721 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
722 int userFrom, int userTo) {
723 enforceCrossUserPermission(UserHandle.USER_ALL, "Calling copyAccountToUser requires "
724 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800725 final UserAccounts fromAccounts = getUserAccounts(userFrom);
726 final UserAccounts toAccounts = getUserAccounts(userTo);
727 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000728 if (response != null) {
729 Bundle result = new Bundle();
730 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
731 try {
732 response.onResult(result);
733 } catch (RemoteException e) {
734 Slog.w(TAG, "Failed to report error back to the client." + e);
735 }
736 }
737 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800738 }
739
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000740 Slog.d(TAG, "Copying account " + account.name
741 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800742 long identityToken = clearCallingIdentity();
743 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000744 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800745 false /* stripAuthTokenFromResult */, account.name,
746 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700747 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800748 protected String toDebugString(long now) {
749 return super.toDebugString(now) + ", getAccountCredentialsForClone"
750 + ", " + account.type;
751 }
752
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700753 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800754 public void run() throws RemoteException {
755 mAuthenticator.getAccountCredentialsForCloning(this, account);
756 }
757
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700758 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800759 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000760 if (result != null
761 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
762 // Create a Session for the target user and pass in the bundle
763 completeCloningAccount(response, result, account, toAccounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800764 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800765 super.onResult(result);
766 }
767 }
768 }.bind();
769 } finally {
770 restoreCallingIdentity(identityToken);
771 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800772 }
773
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800774 @Override
775 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700776 final int callingUid = Binder.getCallingUid();
777 if (Log.isLoggable(TAG, Log.VERBOSE)) {
778 String msg = String.format(
779 "accountAuthenticated( account: %s, callerUid: %s)",
780 account,
781 callingUid);
782 Log.v(TAG, msg);
783 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800784 if (account == null) {
785 throw new IllegalArgumentException("account is null");
786 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700787 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
788 String msg = String.format(
789 "uid %s cannot notify authentication for accounts of type: %s",
790 callingUid,
791 account.type);
792 throw new SecurityException(msg);
793 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800794 int userId = Binder.getCallingUserHandle().getIdentifier();
795 if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
796 return false;
797 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -0700798 return updateLastAuthenticatedTime(account);
799 }
800
801 private boolean updateLastAuthenticatedTime(Account account) {
802 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800803 synchronized (accounts.cacheLock) {
804 final ContentValues values = new ContentValues();
805 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
806 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
807 int i = db.update(
808 TABLE_ACCOUNTS,
809 values,
810 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
811 new String[] {
812 account.name, account.type
813 });
814 if (i > 0) {
815 return true;
816 }
817 }
818 return false;
819 }
820
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000821 private void completeCloningAccount(IAccountManagerResponse response,
822 final Bundle accountCredentials, final Account account, final UserAccounts targetUser) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800823 long id = clearCallingIdentity();
824 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000825 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800826 false /* stripAuthTokenFromResult */, account.name,
827 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700828 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800829 protected String toDebugString(long now) {
830 return super.toDebugString(now) + ", getAccountCredentialsForClone"
831 + ", " + account.type;
832 }
833
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700834 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800835 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700836 // Confirm that the owner's account still exists before this step.
837 UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER);
838 synchronized (owner.cacheLock) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000839 for (Account acc : getAccounts(UserHandle.USER_OWNER)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700840 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000841 mAuthenticator.addAccountFromCredentials(
842 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700843 break;
844 }
845 }
846 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800847 }
848
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700849 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800850 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000851 // TODO: Anything to do if if succedded?
852 // TODO: If it failed: Show error notification? Should we remove the shadow
853 // account to avoid retries?
854 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800855 }
856
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700857 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800858 public void onError(int errorCode, String errorMessage) {
859 super.onError(errorCode, errorMessage);
860 // TODO: Show error notification to user
861 // TODO: Should we remove the shadow account so that it doesn't keep trying?
862 }
863
864 }.bind();
865 } finally {
866 restoreCallingIdentity(id);
867 }
868 }
869
Amith Yamasani04e0d262012-02-14 11:50:53 -0800870 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700871 Bundle extras, boolean restricted, int callingUid) {
Fred Quintana743dfad2010-07-15 10:59:25 -0700872 if (account == null) {
873 return false;
874 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800875 synchronized (accounts.cacheLock) {
876 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800877 db.beginTransaction();
878 try {
879 long numMatches = DatabaseUtils.longForQuery(db,
880 "select count(*) from " + TABLE_ACCOUNTS
881 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
882 new String[]{account.name, account.type});
883 if (numMatches > 0) {
884 Log.w(TAG, "insertAccountIntoDatabase: " + account
885 + ", skipping since the account already exists");
886 return false;
887 }
888 ContentValues values = new ContentValues();
889 values.put(ACCOUNTS_NAME, account.name);
890 values.put(ACCOUNTS_TYPE, account.type);
891 values.put(ACCOUNTS_PASSWORD, password);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800892 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800893 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
894 if (accountId < 0) {
895 Log.w(TAG, "insertAccountIntoDatabase: " + account
896 + ", skipping the DB insert failed");
897 return false;
898 }
899 if (extras != null) {
900 for (String key : extras.keySet()) {
901 final String value = extras.getString(key);
902 if (insertExtraLocked(db, accountId, key, value) < 0) {
903 Log.w(TAG, "insertAccountIntoDatabase: " + account
904 + ", skipping since insertExtra failed for key " + key);
905 return false;
906 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700907 }
908 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800909 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700910
911 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
912 accounts, callingUid);
913
Amith Yamasani04e0d262012-02-14 11:50:53 -0800914 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800915 } finally {
916 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700917 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800918 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700919 }
920 if (accounts.userId == UserHandle.USER_OWNER) {
921 addAccountToLimitedUsers(account);
922 }
923 return true;
924 }
925
926 /**
927 * Adds the account to all limited users as shared accounts. If the user is currently
928 * running, then clone the account too.
929 * @param account the account to share with limited users
930 */
931 private void addAccountToLimitedUsers(Account account) {
Mita Yunf4c240e2013-04-01 21:12:43 -0700932 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -0700933 for (UserInfo user : users) {
934 if (user.isRestricted()) {
935 addSharedAccountAsUser(account, user.id);
936 try {
937 if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) {
938 mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
939 MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id,
940 account));
941 }
942 } catch (RemoteException re) {
943 // Shouldn't happen
944 }
945 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700946 }
947 }
948
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800949 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -0700950 ContentValues values = new ContentValues();
951 values.put(EXTRAS_KEY, key);
952 values.put(EXTRAS_ACCOUNTS_ID, accountId);
953 values.put(EXTRAS_VALUE, value);
954 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
955 }
956
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800957 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -0800958 public void hasFeatures(IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800959 Account account, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -0800960 if (Log.isLoggable(TAG, Log.VERBOSE)) {
961 Log.v(TAG, "hasFeatures: " + account
962 + ", response " + response
963 + ", features " + stringArrayToString(features)
964 + ", caller's uid " + Binder.getCallingUid()
965 + ", pid " + Binder.getCallingPid());
966 }
Fred Quintana382601f2010-03-25 12:25:10 -0700967 if (response == null) throw new IllegalArgumentException("response is null");
968 if (account == null) throw new IllegalArgumentException("account is null");
969 if (features == null) throw new IllegalArgumentException("features is null");
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800970 checkReadAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800971 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800972 long identityToken = clearCallingIdentity();
973 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800974 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800975 } finally {
976 restoreCallingIdentity(identityToken);
977 }
978 }
979
980 private class TestFeaturesSession extends Session {
981 private final String[] mFeatures;
982 private final Account mAccount;
983
Amith Yamasani04e0d262012-02-14 11:50:53 -0800984 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800985 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800986 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800987 true /* stripAuthTokenFromResult */, account.name,
988 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800989 mFeatures = features;
990 mAccount = account;
991 }
992
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700993 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -0800994 public void run() throws RemoteException {
995 try {
996 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
997 } catch (RemoteException e) {
998 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
999 }
1000 }
1001
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001002 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001003 public void onResult(Bundle result) {
1004 IAccountManagerResponse response = getResponseAndClose();
1005 if (response != null) {
1006 try {
1007 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001008 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001009 return;
1010 }
Fred Quintana56285a62010-12-02 14:20:51 -08001011 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1012 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1013 + response);
1014 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001015 final Bundle newResult = new Bundle();
1016 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1017 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1018 response.onResult(newResult);
1019 } catch (RemoteException e) {
1020 // if the caller is dead then there is no one to care about remote exceptions
1021 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1022 Log.v(TAG, "failure while notifying response", e);
1023 }
1024 }
1025 }
1026 }
1027
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001028 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001029 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001030 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001031 + ", " + mAccount
1032 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1033 }
1034 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001035
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001036 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001037 public void renameAccount(
1038 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001039 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001040 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1041 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001042 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001043 + ", pid " + Binder.getCallingPid());
1044 }
1045 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001046 if (!isAccountOwnedByCallingUid(accountToRename.type, callingUid)) {
1047 String msg = String.format(
1048 "uid %s cannot rename accounts of type: %s",
1049 callingUid,
1050 accountToRename.type);
1051 throw new SecurityException(msg);
1052 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001053 UserAccounts accounts = getUserAccountsForCaller();
1054 long identityToken = clearCallingIdentity();
1055 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001056 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName,
1057 callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001058 Bundle result = new Bundle();
1059 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1060 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1061 try {
1062 response.onResult(result);
1063 } catch (RemoteException e) {
1064 Log.w(TAG, e.getMessage());
1065 }
1066 } finally {
1067 restoreCallingIdentity(identityToken);
1068 }
1069 }
1070
1071 private Account renameAccountInternal(
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001072 UserAccounts accounts, Account accountToRename, String newName, int callingUid) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001073 Account resultAccount = null;
1074 /*
1075 * Cancel existing notifications. Let authenticators
1076 * re-post notifications as required. But we don't know if
1077 * the authenticators have bound their notifications to
1078 * now stale account name data.
1079 *
1080 * With a rename api, we might not need to do this anymore but it
1081 * shouldn't hurt.
1082 */
1083 cancelNotification(
1084 getSigninRequiredNotificationId(accounts, accountToRename),
1085 new UserHandle(accounts.userId));
1086 synchronized(accounts.credentialsPermissionNotificationIds) {
1087 for (Pair<Pair<Account, String>, Integer> pair:
1088 accounts.credentialsPermissionNotificationIds.keySet()) {
1089 if (accountToRename.equals(pair.first.first)) {
1090 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1091 cancelNotification(id, new UserHandle(accounts.userId));
1092 }
1093 }
1094 }
1095 synchronized (accounts.cacheLock) {
1096 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1097 db.beginTransaction();
1098 boolean isSuccessful = false;
1099 Account renamedAccount = new Account(newName, accountToRename.type);
1100 try {
1101 final ContentValues values = new ContentValues();
1102 values.put(ACCOUNTS_NAME, newName);
1103 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1104 final long accountId = getAccountIdLocked(db, accountToRename);
1105 if (accountId >= 0) {
1106 final String[] argsAccountId = { String.valueOf(accountId) };
1107 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1108 db.setTransactionSuccessful();
1109 isSuccessful = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001110 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1111 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001112 }
1113 } finally {
1114 db.endTransaction();
1115 if (isSuccessful) {
1116 /*
1117 * Database transaction was successful. Clean up cached
1118 * data associated with the account in the user profile.
1119 */
1120 insertAccountIntoCacheLocked(accounts, renamedAccount);
1121 /*
1122 * Extract the data and token caches before removing the
1123 * old account to preserve the user data associated with
1124 * the account.
1125 */
1126 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1127 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1128 removeAccountFromCacheLocked(accounts, accountToRename);
1129 /*
1130 * Update the cached data associated with the renamed
1131 * account.
1132 */
1133 accounts.userDataCache.put(renamedAccount, tmpData);
1134 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1135 accounts.previousNameCache.put(
1136 renamedAccount,
1137 new AtomicReference<String>(accountToRename.name));
1138 resultAccount = renamedAccount;
1139
1140 if (accounts.userId == UserHandle.USER_OWNER) {
1141 /*
1142 * Owner's account was renamed, rename the account for
1143 * those users with which the account was shared.
1144 */
1145 List<UserInfo> users = mUserManager.getUsers(true);
1146 for (UserInfo user : users) {
1147 if (!user.isPrimary() && user.isRestricted()) {
1148 renameSharedAccountAsUser(accountToRename, newName, user.id);
1149 }
1150 }
1151 }
1152 sendAccountsChangedBroadcast(accounts.userId);
1153 }
1154 }
1155 }
1156 return resultAccount;
1157 }
1158
1159 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001160 public void removeAccount(IAccountManagerResponse response, Account account,
1161 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001162 removeAccountAsUser(
1163 response,
1164 account,
1165 expectActivityLaunch,
1166 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001167 }
1168
1169 @Override
1170 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001171 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001172 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001173 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1174 Log.v(TAG, "removeAccount: " + account
1175 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001176 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001177 + ", pid " + Binder.getCallingPid()
1178 + ", for user id " + userId);
1179 }
1180 if (response == null) throw new IllegalArgumentException("response is null");
1181 if (account == null) throw new IllegalArgumentException("account is null");
1182
1183 // Only allow the system process to modify accounts of other users
1184 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
1185 + " trying to remove account for " + userId);
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001186 /*
1187 * Only the system or authenticator should be allowed to remove accounts for that
1188 * authenticator. This will let users remove accounts (via Settings in the system) but not
1189 * arbitrary applications (like competing authenticators).
1190 */
1191 if (!isAccountOwnedByCallingUid(account.type, callingUid) && !isSystemUid(callingUid)) {
1192 String msg = String.format(
1193 "uid %s cannot remove accounts of type: %s",
1194 callingUid,
1195 account.type);
1196 throw new SecurityException(msg);
1197 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001198
1199 UserAccounts accounts = getUserAccounts(userId);
1200 if (!canUserModifyAccounts(userId)) {
1201 try {
1202 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1203 "User cannot modify accounts");
1204 } catch (RemoteException re) {
1205 }
1206 return;
1207 }
1208 if (!canUserModifyAccountsForType(userId, account.type)) {
1209 try {
1210 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1211 "User cannot modify accounts of this type (policy).");
1212 } catch (RemoteException re) {
1213 }
1214 return;
1215 }
1216
1217 UserHandle user = new UserHandle(userId);
1218 long identityToken = clearCallingIdentity();
1219
1220 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001221 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001222 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08001223 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001224 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001225 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001226 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001227 }
1228 }
1229 }
1230
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001231 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1232
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001233 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001234 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1235 } finally {
1236 restoreCallingIdentity(identityToken);
1237 }
1238 }
1239
1240 @Override
1241 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001242 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001243 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1244 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001245 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001246 + ", pid " + Binder.getCallingPid());
1247 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001248 if (account == null) {
1249 /*
1250 * Null accounts should result in returning false, as per
1251 * AccountManage.addAccountExplicitly(...) java doc.
1252 */
1253 Log.e(TAG, "account is null");
1254 return false;
1255 } else if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
1256 String msg = String.format(
1257 "uid %s cannot explicitly add accounts of type: %s",
1258 callingUid,
1259 account.type);
1260 throw new SecurityException(msg);
1261 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001262
1263 UserAccounts accounts = getUserAccountsForCaller();
1264 int userId = Binder.getCallingUserHandle().getIdentifier();
1265 if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
1266 return false;
1267 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001268
1269 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1270
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001271 long identityToken = clearCallingIdentity();
1272 try {
1273 return removeAccountInternal(accounts, account);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001274 } finally {
1275 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001276 }
Fred Quintana60307342009-03-24 22:48:12 -07001277 }
1278
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001279 private class RemoveAccountSession extends Session {
1280 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001281 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001282 Account account, boolean expectActivityLaunch) {
1283 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001284 true /* stripAuthTokenFromResult */, account.name,
1285 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001286 mAccount = account;
1287 }
1288
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001289 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001290 protected String toDebugString(long now) {
1291 return super.toDebugString(now) + ", removeAccount"
1292 + ", account " + mAccount;
1293 }
1294
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001295 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001296 public void run() throws RemoteException {
1297 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1298 }
1299
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001300 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001301 public void onResult(Bundle result) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001302 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1303 && !result.containsKey(AccountManager.KEY_INTENT)) {
1304 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001305 if (removalAllowed) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001306 removeAccountInternal(mAccounts, mAccount);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001307 }
1308 IAccountManagerResponse response = getResponseAndClose();
1309 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001310 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1311 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1312 + response);
1313 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001314 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001315 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001316 try {
1317 response.onResult(result2);
1318 } catch (RemoteException e) {
1319 // ignore
1320 }
1321 }
1322 }
1323 super.onResult(result);
1324 }
1325 }
1326
Amith Yamasani04e0d262012-02-14 11:50:53 -08001327 /* For testing */
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001328 protected void removeAccountInternal(Account account) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001329 removeAccountInternal(getUserAccountsForCaller(), account);
1330 }
1331
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001332 private boolean removeAccountInternal(UserAccounts accounts, Account account) {
1333 int deleted;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001334 synchronized (accounts.cacheLock) {
1335 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001336 final long accountId = getAccountIdLocked(db, account);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001337 deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
1338 + "=?",
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001339 new String[]{account.name, account.type});
Amith Yamasani04e0d262012-02-14 11:50:53 -08001340 removeAccountFromCacheLocked(accounts, account);
1341 sendAccountsChangedBroadcast(accounts.userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001342
1343 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001344 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001345 if (accounts.userId == UserHandle.USER_OWNER) {
1346 // Owner's account was removed, remove from any users that are sharing
1347 // this account.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001348 int callingUid = getCallingUid();
Amith Yamasani67df64b2012-12-14 12:09:36 -08001349 long id = Binder.clearCallingIdentity();
1350 try {
1351 List<UserInfo> users = mUserManager.getUsers(true);
1352 for (UserInfo user : users) {
1353 if (!user.isPrimary() && user.isRestricted()) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001354 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001355 }
1356 }
1357 } finally {
1358 Binder.restoreCallingIdentity(id);
1359 }
1360 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001361 return (deleted > 0);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001362 }
1363
Maggie Benthalla12fccf2013-03-14 18:02:12 -04001364 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001365 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07001366 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001367 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1368 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07001369 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08001370 + ", pid " + Binder.getCallingPid());
1371 }
Fred Quintana382601f2010-03-25 12:25:10 -07001372 if (accountType == null) throw new IllegalArgumentException("accountType is null");
1373 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001374 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001375 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001376 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001377 synchronized (accounts.cacheLock) {
1378 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001379 db.beginTransaction();
1380 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001381 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001382 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001383 db.setTransactionSuccessful();
1384 } finally {
1385 db.endTransaction();
1386 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001387 }
Fred Quintana60307342009-03-24 22:48:12 -07001388 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001389 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001390 }
1391 }
1392
Carlos Valdivia91979be2015-05-22 14:11:35 -07001393 private void invalidateCustomTokenLocked(
1394 UserAccounts accounts,
1395 String accountType,
1396 String authToken) {
1397 if (authToken == null || accountType == null) {
1398 return;
1399 }
1400 // Also wipe out cached token in memory.
1401 for (Account a : accounts.accountTokenCaches.keySet()) {
1402 if (a.type.equals(accountType)) {
1403 WeakReference<TokenCache> tokenCacheRef =
1404 accounts.accountTokenCaches.get(a);
1405 TokenCache cache = null;
1406 if (tokenCacheRef != null && (cache = tokenCacheRef.get()) != null) {
1407 cache.remove(authToken);
1408 }
1409 }
1410 }
1411 }
1412
Amith Yamasani04e0d262012-02-14 11:50:53 -08001413 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1414 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001415 if (authToken == null || accountType == null) {
1416 return;
1417 }
Fred Quintana33269202009-04-20 16:05:10 -07001418 Cursor cursor = db.rawQuery(
1419 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1420 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1421 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1422 + " FROM " + TABLE_ACCOUNTS
1423 + " JOIN " + TABLE_AUTHTOKENS
1424 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1425 + " = " + AUTHTOKENS_ACCOUNTS_ID
1426 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
1427 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1428 new String[]{authToken, accountType});
1429 try {
1430 while (cursor.moveToNext()) {
1431 long authTokenId = cursor.getLong(0);
1432 String accountName = cursor.getString(1);
1433 String authTokenType = cursor.getString(2);
1434 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001435 writeAuthTokenIntoCacheLocked(
1436 accounts,
1437 db,
1438 new Account(accountName, accountType),
1439 authTokenType,
1440 null);
Fred Quintana60307342009-03-24 22:48:12 -07001441 }
Fred Quintana33269202009-04-20 16:05:10 -07001442 } finally {
1443 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07001444 }
1445 }
1446
Carlos Valdivia91979be2015-05-22 14:11:35 -07001447 private void saveCachedToken(
1448 UserAccounts accounts,
1449 Account account,
1450 String callerPkg,
1451 byte[] callerSigDigest,
1452 String tokenType,
1453 String token,
1454 long expiryMillis) {
1455
1456 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1457 return;
1458 }
1459 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1460 new UserHandle(accounts.userId));
1461 synchronized (accounts.cacheLock) {
1462 TokenCache cache = getTokenCacheForAccountLocked(accounts, account);
1463 if (cache != null) {
1464 cache.put(token, tokenType, callerPkg, callerSigDigest, expiryMillis);
1465 }
1466 return;
1467 }
1468 }
1469
Amith Yamasani04e0d262012-02-14 11:50:53 -08001470 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1471 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07001472 if (account == null || type == null) {
1473 return false;
1474 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001475 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1476 new UserHandle(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001477 synchronized (accounts.cacheLock) {
1478 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001479 db.beginTransaction();
1480 try {
1481 long accountId = getAccountIdLocked(db, account);
1482 if (accountId < 0) {
1483 return false;
1484 }
1485 db.delete(TABLE_AUTHTOKENS,
1486 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1487 new String[]{type});
1488 ContentValues values = new ContentValues();
1489 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1490 values.put(AUTHTOKENS_TYPE, type);
1491 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1492 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1493 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001494 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001495 return true;
1496 }
Fred Quintana33269202009-04-20 16:05:10 -07001497 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001498 } finally {
1499 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07001500 }
Fred Quintana60307342009-03-24 22:48:12 -07001501 }
1502 }
1503
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001504 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001505 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001506 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001507 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1508 Log.v(TAG, "peekAuthToken: " + account
1509 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001510 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001511 + ", pid " + Binder.getCallingPid());
1512 }
Fred Quintana382601f2010-03-25 12:25:10 -07001513 if (account == null) throw new IllegalArgumentException("account is null");
1514 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001515 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
1516 String msg = String.format(
1517 "uid %s cannot peek the authtokens associated with accounts of type: %s",
1518 callingUid,
1519 account.type);
1520 throw new SecurityException(msg);
1521 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001522 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001523 long identityToken = clearCallingIdentity();
1524 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001525 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001526 } finally {
1527 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001528 }
Fred Quintana60307342009-03-24 22:48:12 -07001529 }
1530
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001531 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001532 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001533 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001534 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1535 Log.v(TAG, "setAuthToken: " + account
1536 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001537 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001538 + ", pid " + Binder.getCallingPid());
1539 }
Fred Quintana382601f2010-03-25 12:25:10 -07001540 if (account == null) throw new IllegalArgumentException("account is null");
1541 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001542 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
1543 String msg = String.format(
1544 "uid %s cannot set auth tokens associated with accounts of type: %s",
1545 callingUid,
1546 account.type);
1547 throw new SecurityException(msg);
1548 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001549 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001550 long identityToken = clearCallingIdentity();
1551 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001552 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001553 } finally {
1554 restoreCallingIdentity(identityToken);
1555 }
Fred Quintana60307342009-03-24 22:48:12 -07001556 }
1557
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001558 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001559 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001560 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001561 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1562 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001563 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001564 + ", pid " + Binder.getCallingPid());
1565 }
Fred Quintana382601f2010-03-25 12:25:10 -07001566 if (account == null) throw new IllegalArgumentException("account is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001567 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
1568 String msg = String.format(
1569 "uid %s cannot set secrets for accounts of type: %s",
1570 callingUid,
1571 account.type);
1572 throw new SecurityException(msg);
1573 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001574 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001575 long identityToken = clearCallingIdentity();
1576 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001577 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001578 } finally {
1579 restoreCallingIdentity(identityToken);
1580 }
Fred Quintana60307342009-03-24 22:48:12 -07001581 }
1582
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001583 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1584 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07001585 if (account == null) {
1586 return;
1587 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001588 synchronized (accounts.cacheLock) {
1589 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001590 db.beginTransaction();
1591 try {
1592 final ContentValues values = new ContentValues();
1593 values.put(ACCOUNTS_PASSWORD, password);
1594 final long accountId = getAccountIdLocked(db, account);
1595 if (accountId >= 0) {
1596 final String[] argsAccountId = {String.valueOf(accountId)};
1597 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1598 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001599 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001600 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001601 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001602
1603 String action = (password == null || password.length() == 0) ?
1604 DebugDbHelper.ACTION_CLEAR_PASSWORD
1605 : DebugDbHelper.ACTION_SET_PASSWORD;
1606 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08001607 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001608 } finally {
1609 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001610 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001611 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001612 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001613 }
1614
Amith Yamasani04e0d262012-02-14 11:50:53 -08001615 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08001616 Log.i(TAG, "the accounts changed, sending broadcast of "
1617 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001618 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07001619 }
1620
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001621 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001622 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001623 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001624 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1625 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001626 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001627 + ", pid " + Binder.getCallingPid());
1628 }
Fred Quintana382601f2010-03-25 12:25:10 -07001629 if (account == null) throw new IllegalArgumentException("account is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001630 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
1631 String msg = String.format(
1632 "uid %s cannot clear passwords for accounts of type: %s",
1633 callingUid,
1634 account.type);
1635 throw new SecurityException(msg);
1636 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001637 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001638 long identityToken = clearCallingIdentity();
1639 try {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001640 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001641 } finally {
1642 restoreCallingIdentity(identityToken);
1643 }
Fred Quintana60307342009-03-24 22:48:12 -07001644 }
1645
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001646 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001647 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001648 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001649 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1650 Log.v(TAG, "setUserData: " + account
1651 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001652 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001653 + ", pid " + Binder.getCallingPid());
1654 }
Fred Quintana382601f2010-03-25 12:25:10 -07001655 if (key == null) throw new IllegalArgumentException("key is null");
1656 if (account == null) throw new IllegalArgumentException("account is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001657 if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
1658 String msg = String.format(
1659 "uid %s cannot set user data for accounts of type: %s",
1660 callingUid,
1661 account.type);
1662 throw new SecurityException(msg);
1663 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001664 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001665 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001666 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001667 setUserdataInternal(accounts, account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -07001668 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001669 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001670 }
1671 }
1672
Amith Yamasani04e0d262012-02-14 11:50:53 -08001673 private void setUserdataInternal(UserAccounts accounts, Account account, String key,
1674 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07001675 if (account == null || key == null) {
1676 return;
1677 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001678 synchronized (accounts.cacheLock) {
1679 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001680 db.beginTransaction();
1681 try {
1682 long accountId = getAccountIdLocked(db, account);
1683 if (accountId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001684 return;
1685 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001686 long extrasId = getExtrasIdLocked(db, accountId, key);
1687 if (extrasId < 0 ) {
1688 extrasId = insertExtraLocked(db, accountId, key, value);
1689 if (extrasId < 0) {
1690 return;
1691 }
1692 } else {
1693 ContentValues values = new ContentValues();
1694 values.put(EXTRAS_VALUE, value);
1695 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1696 return;
1697 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001698
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001699 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001700 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001701 db.setTransactionSuccessful();
1702 } finally {
1703 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001704 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001705 }
1706 }
1707
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001708 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08001709 if (result == null) {
1710 Log.e(TAG, "the result is unexpectedly null", new Exception());
1711 }
1712 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1713 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1714 + response);
1715 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001716 try {
1717 response.onResult(result);
1718 } catch (RemoteException e) {
1719 // if the caller is dead then there is no one to care about remote
1720 // exceptions
1721 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1722 Log.v(TAG, "failure while notifying response", e);
1723 }
1724 }
1725 }
1726
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001727 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07001728 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1729 final String authTokenType)
1730 throws RemoteException {
1731 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08001732 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1733
Fred Quintanad9640ec2012-05-23 12:37:00 -07001734 final int callingUid = getCallingUid();
1735 clearCallingIdentity();
Amith Yamasani27db4682013-03-30 17:07:47 -07001736 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07001737 throw new SecurityException("can only call from system");
1738 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001739 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
Costin Manolache5f383ad92010-12-02 16:44:46 -08001740 long identityToken = clearCallingIdentity();
1741 try {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001742 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
1743 false /* stripAuthTokenFromResult */, null /* accountName */,
1744 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001745 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001746 protected String toDebugString(long now) {
1747 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07001748 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08001749 + ", authTokenType " + authTokenType;
1750 }
1751
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001752 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001753 public void run() throws RemoteException {
1754 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1755 }
1756
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001757 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001758 public void onResult(Bundle result) {
1759 if (result != null) {
1760 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1761 Bundle bundle = new Bundle();
1762 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1763 super.onResult(bundle);
1764 return;
1765 } else {
1766 super.onResult(result);
1767 }
1768 }
1769 }.bind();
1770 } finally {
1771 restoreCallingIdentity(identityToken);
1772 }
1773 }
1774
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001775 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07001776 public void getAuthToken(
1777 IAccountManagerResponse response,
1778 final Account account,
1779 final String authTokenType,
1780 final boolean notifyOnAuthFailure,
1781 final boolean expectActivityLaunch,
1782 final Bundle loginOptions) {
1783
Fred Quintana56285a62010-12-02 14:20:51 -08001784 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1785 Log.v(TAG, "getAuthToken: " + account
1786 + ", response " + response
1787 + ", authTokenType " + authTokenType
1788 + ", notifyOnAuthFailure " + notifyOnAuthFailure
1789 + ", expectActivityLaunch " + expectActivityLaunch
1790 + ", caller's uid " + Binder.getCallingUid()
1791 + ", pid " + Binder.getCallingPid());
1792 }
Fred Quintana382601f2010-03-25 12:25:10 -07001793 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001794 try {
1795 if (account == null) {
1796 Slog.w(TAG, "getAuthToken called with null account");
1797 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
1798 return;
1799 }
1800 if (authTokenType == null) {
1801 Slog.w(TAG, "getAuthToken called with null authTokenType");
1802 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
1803 return;
1804 }
1805 } catch (RemoteException e) {
1806 Slog.w(TAG, "Failed to report error back to the client." + e);
1807 return;
1808 }
1809
Dianne Hackborn41203752012-08-31 14:05:51 -07001810 final UserAccounts accounts = getUserAccountsForCaller();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001811 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
1812 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1813 AuthenticatorDescription.newKey(account.type), accounts.userId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001814
Costin Manolachea40c6302010-12-13 14:50:45 -08001815 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07001816 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08001817
1818 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001819 final int callerUid = Binder.getCallingUid();
Costin Manolachea40c6302010-12-13 14:50:45 -08001820 final boolean permissionGranted = customTokens ||
1821 permissionIsGranted(account, authTokenType, callerUid);
1822
Carlos Valdivia91979be2015-05-22 14:11:35 -07001823 // Get the calling package. We will use it for the purpose of caching.
1824 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07001825 List<String> callerOwnedPackageNames;
1826 long ident = Binder.clearCallingIdentity();
1827 try {
1828 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
1829 } finally {
1830 Binder.restoreCallingIdentity(ident);
1831 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001832 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
1833 String msg = String.format(
1834 "Uid %s is attempting to illegally masquerade as package %s!",
1835 callerUid,
1836 callerPkg);
1837 throw new SecurityException(msg);
1838 }
1839
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001840 // let authenticator know the identity of the caller
1841 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1842 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07001843
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001844 if (notifyOnAuthFailure) {
1845 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08001846 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001847
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001848 long identityToken = clearCallingIdentity();
1849 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07001850 // Distill the caller's package signatures into a single digest.
1851 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
1852
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001853 // if the caller has permission, do the peek. otherwise go the more expensive
1854 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08001855 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001856 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001857 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001858 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001859 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1860 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1861 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001862 onResult(response, result);
1863 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001864 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001865 }
1866
Carlos Valdivia91979be2015-05-22 14:11:35 -07001867 if (customTokens) {
1868 /*
1869 * Look up tokens in the new cache only if the loginOptions don't have parameters
1870 * outside of those expected to be injected by the AccountManager, e.g.
1871 * ANDORID_PACKAGE_NAME.
1872 */
1873 String token = readCachedTokenInternal(
1874 accounts,
1875 account,
1876 authTokenType,
1877 callerPkg,
1878 callerPkgSigDigest);
1879 if (token != null) {
1880 Bundle result = new Bundle();
1881 result.putString(AccountManager.KEY_AUTHTOKEN, token);
1882 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1883 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
1884 onResult(response, result);
1885 return;
1886 }
1887 }
1888
Amith Yamasani04e0d262012-02-14 11:50:53 -08001889 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001890 false /* stripAuthTokenFromResult */, account.name,
1891 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001892 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001893 protected String toDebugString(long now) {
1894 if (loginOptions != null) loginOptions.keySet();
1895 return super.toDebugString(now) + ", getAuthToken"
1896 + ", " + account
1897 + ", authTokenType " + authTokenType
1898 + ", loginOptions " + loginOptions
1899 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
1900 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001901
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001902 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001903 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001904 // If the caller doesn't have permission then create and return the
1905 // "grant permission" intent instead of the "getAuthToken" intent.
1906 if (!permissionGranted) {
1907 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1908 } else {
1909 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
1910 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001911 }
1912
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001913 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001914 public void onResult(Bundle result) {
1915 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001916 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001917 Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
1918 new AccountAuthenticatorResponse(this),
1919 authTokenType,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001920 result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001921 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001922 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001923 onResult(bundle);
1924 return;
1925 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001926 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001927 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001928 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1929 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001930 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001931 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001932 "the type and name should not be empty");
1933 return;
1934 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001935 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08001936 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07001937 saveAuthTokenToDatabase(
1938 mAccounts,
1939 resultAccount,
1940 authTokenType,
1941 authToken);
1942 }
1943 long expiryMillis = result.getLong(
1944 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
1945 if (customTokens
1946 && expiryMillis > System.currentTimeMillis()) {
1947 saveCachedToken(
1948 mAccounts,
1949 account,
1950 callerPkg,
1951 callerPkgSigDigest,
1952 authTokenType,
1953 authToken,
1954 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08001955 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001956 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001957
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001958 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08001959 if (intent != null && notifyOnAuthFailure && !customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001960 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001961 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Dianne Hackborn41203752012-08-31 14:05:51 -07001962 intent, accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001963 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001964 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001965 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07001966 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001967 }.bind();
1968 } finally {
1969 restoreCallingIdentity(identityToken);
1970 }
Fred Quintana60307342009-03-24 22:48:12 -07001971 }
1972
Carlos Valdivia91979be2015-05-22 14:11:35 -07001973 private byte[] calculatePackageSignatureDigest(String callerPkg) {
1974 MessageDigest digester;
1975 try {
1976 digester = MessageDigest.getInstance("SHA-256");
1977 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
1978 callerPkg, PackageManager.GET_SIGNATURES);
1979 for (Signature sig : pkgInfo.signatures) {
1980 digester.update(sig.toByteArray());
1981 }
1982 } catch (NoSuchAlgorithmException x) {
1983 Log.wtf(TAG, "SHA-256 should be available", x);
1984 digester = null;
1985 } catch (NameNotFoundException e) {
1986 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
1987 digester = null;
1988 }
1989 return (digester == null) ? null : digester.digest();
1990 }
1991
Dianne Hackborn41203752012-08-31 14:05:51 -07001992 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
1993 int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001994 int uid = intent.getIntExtra(
1995 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
1996 String authTokenType = intent.getStringExtra(
1997 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
1998 String authTokenLabel = intent.getStringExtra(
1999 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);
2000
Eric Fischeree452ee2009-08-31 17:58:06 -07002001 final String titleAndSubtitle =
2002 mContext.getString(R.string.permission_request_notification_with_subtitle,
2003 account.name);
2004 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07002005 String title = titleAndSubtitle;
2006 String subtitle = "";
2007 if (index > 0) {
2008 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002009 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07002010 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002011 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01002012 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002013 Notification n = new Notification.Builder(contextForUser)
2014 .setSmallIcon(android.R.drawable.stat_sys_warning)
2015 .setWhen(0)
2016 .setColor(contextForUser.getColor(
2017 com.android.internal.R.color.system_notification_accent_color))
2018 .setContentTitle(title)
2019 .setContentText(subtitle)
2020 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2021 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2022 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002023 installNotification(getCredentialPermissionNotificationId(
2024 account, authTokenType, uid), n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002025 }
2026
Costin Manolache5f383ad92010-12-02 16:44:46 -08002027 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
2028 AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002029
2030 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07002031 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08002032 // Since it was set in Eclair+ we can't change it without breaking apps using
2033 // the intent from a non-Activity context.
2034 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002035 intent.addCategory(
2036 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08002037
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002038 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002039 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2040 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002041 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002042
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002043 return intent;
2044 }
2045
2046 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2047 int uid) {
2048 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002049 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002050 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002051 final Pair<Pair<Account, String>, Integer> key =
2052 new Pair<Pair<Account, String>, Integer>(
2053 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002054 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002055 if (id == null) {
2056 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002057 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002058 }
2059 }
2060 return id;
2061 }
2062
Amith Yamasani04e0d262012-02-14 11:50:53 -08002063 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002064 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002065 synchronized (accounts.signinRequiredNotificationIds) {
2066 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002067 if (id == null) {
2068 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002069 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002070 }
2071 }
2072 return id;
2073 }
2074
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002075 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07002076 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07002077 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002078 final boolean expectActivityLaunch, final Bundle optionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08002079 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2080 Log.v(TAG, "addAccount: accountType " + accountType
2081 + ", response " + response
2082 + ", authTokenType " + authTokenType
2083 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2084 + ", expectActivityLaunch " + expectActivityLaunch
2085 + ", caller's uid " + Binder.getCallingUid()
2086 + ", pid " + Binder.getCallingPid());
2087 }
Fred Quintana382601f2010-03-25 12:25:10 -07002088 if (response == null) throw new IllegalArgumentException("response is null");
2089 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002090
Amith Yamasani71e6c692013-03-24 17:39:28 -07002091 // Is user disallowed from modifying accounts?
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002092 int userId = Binder.getCallingUserHandle().getIdentifier();
2093 if (!canUserModifyAccounts(userId)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002094 try {
2095 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2096 "User is not allowed to add an account!");
2097 } catch (RemoteException re) {
2098 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002099 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002100 return;
2101 }
2102 if (!canUserModifyAccountsForType(userId, accountType)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002103 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002104 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2105 "User cannot modify accounts of this type (policy).");
2106 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002107 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002108 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2109 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002110 return;
2111 }
2112
Amith Yamasani04e0d262012-02-14 11:50:53 -08002113 UserAccounts accounts = getUserAccountsForCaller();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002114 final int pid = Binder.getCallingPid();
2115 final int uid = Binder.getCallingUid();
2116 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2117 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2118 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2119
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002120 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS);
2121
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002122 long identityToken = clearCallingIdentity();
2123 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002124 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002125 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002126 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002127 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002128 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07002129 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07002130 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002131 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002132
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002133 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002134 protected String toDebugString(long now) {
2135 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07002136 + ", accountType " + accountType
2137 + ", requiredFeatures "
2138 + (requiredFeatures != null
2139 ? TextUtils.join(",", requiredFeatures)
2140 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002141 }
2142 }.bind();
2143 } finally {
2144 restoreCallingIdentity(identityToken);
2145 }
Fred Quintana60307342009-03-24 22:48:12 -07002146 }
2147
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002148 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002149 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2150 final String authTokenType, final String[] requiredFeatures,
2151 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
2152 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2153 Log.v(TAG, "addAccount: accountType " + accountType
2154 + ", response " + response
2155 + ", authTokenType " + authTokenType
2156 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2157 + ", expectActivityLaunch " + expectActivityLaunch
2158 + ", caller's uid " + Binder.getCallingUid()
2159 + ", pid " + Binder.getCallingPid()
2160 + ", for user id " + userId);
2161 }
2162 if (response == null) throw new IllegalArgumentException("response is null");
2163 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002164
2165 // Only allow the system process to add accounts of other users
2166 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
2167 + " trying to add account for " + userId);
2168
2169 // Is user disallowed from modifying accounts?
2170 if (!canUserModifyAccounts(userId)) {
2171 try {
2172 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2173 "User is not allowed to add an account!");
2174 } catch (RemoteException re) {
2175 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002176 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002177 return;
2178 }
2179 if (!canUserModifyAccountsForType(userId, accountType)) {
2180 try {
2181 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2182 "User cannot modify accounts of this type (policy).");
2183 } catch (RemoteException re) {
2184 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002185 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2186 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002187 return;
2188 }
2189
2190 UserAccounts accounts = getUserAccounts(userId);
2191 final int pid = Binder.getCallingPid();
2192 final int uid = Binder.getCallingUid();
2193 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2194 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2195 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2196
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002197 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS);
2198
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002199 long identityToken = clearCallingIdentity();
2200 try {
2201 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002202 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002203 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002204 @Override
2205 public void run() throws RemoteException {
2206 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2207 options);
2208 }
2209
2210 @Override
2211 protected String toDebugString(long now) {
2212 return super.toDebugString(now) + ", addAccount"
2213 + ", accountType " + accountType
2214 + ", requiredFeatures "
2215 + (requiredFeatures != null
2216 ? TextUtils.join(",", requiredFeatures)
2217 : null);
2218 }
2219 }.bind();
2220 } finally {
2221 restoreCallingIdentity(identityToken);
2222 }
2223 }
2224
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002225 private void showCantAddAccount(int errorCode, int userId) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002226 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2227 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2228 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2229 long identityToken = clearCallingIdentity();
2230 try {
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002231 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002232 } finally {
2233 restoreCallingIdentity(identityToken);
2234 }
2235 }
2236
2237 @Override
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002238 public void confirmCredentialsAsUser(IAccountManagerResponse response,
2239 final Account account, final Bundle options, final boolean expectActivityLaunch,
2240 int userId) {
2241 // Only allow the system process to read accounts of other users
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01002242 enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002243 + " trying to confirm account credentials for " + userId);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002244
Fred Quintana56285a62010-12-02 14:20:51 -08002245 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2246 Log.v(TAG, "confirmCredentials: " + account
2247 + ", response " + response
2248 + ", expectActivityLaunch " + expectActivityLaunch
2249 + ", caller's uid " + Binder.getCallingUid()
2250 + ", pid " + Binder.getCallingPid());
2251 }
Fred Quintana382601f2010-03-25 12:25:10 -07002252 if (response == null) throw new IllegalArgumentException("response is null");
2253 if (account == null) throw new IllegalArgumentException("account is null");
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002254 UserAccounts accounts = getUserAccounts(userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002255 long identityToken = clearCallingIdentity();
2256 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002257 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002258 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002259 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002260 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002261 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002262 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002263 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002264 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002265 protected String toDebugString(long now) {
2266 return super.toDebugString(now) + ", confirmCredentials"
2267 + ", " + account;
2268 }
2269 }.bind();
2270 } finally {
2271 restoreCallingIdentity(identityToken);
2272 }
Fred Quintana60307342009-03-24 22:48:12 -07002273 }
2274
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002275 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002276 public void updateCredentials(IAccountManagerResponse response, final Account account,
2277 final String authTokenType, final boolean expectActivityLaunch,
2278 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08002279 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2280 Log.v(TAG, "updateCredentials: " + account
2281 + ", response " + response
2282 + ", authTokenType " + authTokenType
2283 + ", expectActivityLaunch " + expectActivityLaunch
2284 + ", caller's uid " + Binder.getCallingUid()
2285 + ", pid " + Binder.getCallingPid());
2286 }
Fred Quintana382601f2010-03-25 12:25:10 -07002287 if (response == null) throw new IllegalArgumentException("response is null");
2288 if (account == null) throw new IllegalArgumentException("account is null");
2289 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08002290 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002291 long identityToken = clearCallingIdentity();
2292 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002293 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002294 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002295 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002296 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002297 public void run() throws RemoteException {
2298 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
2299 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002300 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002301 protected String toDebugString(long now) {
2302 if (loginOptions != null) loginOptions.keySet();
2303 return super.toDebugString(now) + ", updateCredentials"
2304 + ", " + account
2305 + ", authTokenType " + authTokenType
2306 + ", loginOptions " + loginOptions;
2307 }
2308 }.bind();
2309 } finally {
2310 restoreCallingIdentity(identityToken);
2311 }
Fred Quintana60307342009-03-24 22:48:12 -07002312 }
2313
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002314 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002315 public void editProperties(IAccountManagerResponse response, final String accountType,
2316 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002317 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002318 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2319 Log.v(TAG, "editProperties: accountType " + accountType
2320 + ", response " + response
2321 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002322 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002323 + ", pid " + Binder.getCallingPid());
2324 }
Fred Quintana382601f2010-03-25 12:25:10 -07002325 if (response == null) throw new IllegalArgumentException("response is null");
2326 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002327 if (!isAccountOwnedByCallingUid(accountType, callingUid) && !isSystemUid(callingUid)) {
2328 String msg = String.format(
2329 "uid %s cannot edit authenticator properites for account type: %s",
2330 callingUid,
2331 accountType);
2332 throw new SecurityException(msg);
2333 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002334 UserAccounts accounts = getUserAccountsForCaller();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002335 long identityToken = clearCallingIdentity();
2336 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002337 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002338 true /* stripAuthTokenFromResult */, null /* accountName */,
2339 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002340 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002341 public void run() throws RemoteException {
2342 mAuthenticator.editProperties(this, mAccountType);
2343 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002344 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002345 protected String toDebugString(long now) {
2346 return super.toDebugString(now) + ", editProperties"
2347 + ", accountType " + accountType;
2348 }
2349 }.bind();
2350 } finally {
2351 restoreCallingIdentity(identityToken);
2352 }
Fred Quintana60307342009-03-24 22:48:12 -07002353 }
2354
Fred Quintana33269202009-04-20 16:05:10 -07002355 private class GetAccountsByTypeAndFeatureSession extends Session {
2356 private final String[] mFeatures;
2357 private volatile Account[] mAccountsOfType = null;
2358 private volatile ArrayList<Account> mAccountsWithFeatures = null;
2359 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002360 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07002361
Amith Yamasani04e0d262012-02-14 11:50:53 -08002362 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002363 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002364 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002365 true /* stripAuthTokenFromResult */, null /* accountName */,
2366 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002367 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07002368 mFeatures = features;
2369 }
2370
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002371 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002372 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002373 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002374 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
2375 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002376 }
Fred Quintana33269202009-04-20 16:05:10 -07002377 // check whether each account matches the requested features
2378 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
2379 mCurrentAccount = 0;
2380
2381 checkAccount();
2382 }
2383
2384 public void checkAccount() {
2385 if (mCurrentAccount >= mAccountsOfType.length) {
2386 sendResult();
2387 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002388 }
Fred Quintana33269202009-04-20 16:05:10 -07002389
Fred Quintana29e94b82010-03-10 12:11:51 -08002390 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
2391 if (accountAuthenticator == null) {
2392 // It is possible that the authenticator has died, which is indicated by
2393 // mAuthenticator being set to null. If this happens then just abort.
2394 // There is no need to send back a result or error in this case since
2395 // that already happened when mAuthenticator was cleared.
2396 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2397 Log.v(TAG, "checkAccount: aborting session since we are no longer"
2398 + " connected to the authenticator, " + toDebugString());
2399 }
2400 return;
2401 }
Fred Quintana33269202009-04-20 16:05:10 -07002402 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08002403 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07002404 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002405 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07002406 }
2407 }
2408
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002409 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002410 public void onResult(Bundle result) {
2411 mNumResults++;
2412 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002413 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07002414 return;
2415 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002416 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07002417 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
2418 }
2419 mCurrentAccount++;
2420 checkAccount();
2421 }
2422
2423 public void sendResult() {
2424 IAccountManagerResponse response = getResponseAndClose();
2425 if (response != null) {
2426 try {
2427 Account[] accounts = new Account[mAccountsWithFeatures.size()];
2428 for (int i = 0; i < accounts.length; i++) {
2429 accounts[i] = mAccountsWithFeatures.get(i);
2430 }
Fred Quintana56285a62010-12-02 14:20:51 -08002431 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2432 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2433 + response);
2434 }
Fred Quintana33269202009-04-20 16:05:10 -07002435 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002436 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07002437 response.onResult(result);
2438 } catch (RemoteException e) {
2439 // if the caller is dead then there is no one to care about remote exceptions
2440 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2441 Log.v(TAG, "failure while notifying response", e);
2442 }
2443 }
2444 }
2445 }
2446
2447
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002448 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002449 protected String toDebugString(long now) {
2450 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
2451 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
2452 }
2453 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002454
Amith Yamasani04e0d262012-02-14 11:50:53 -08002455 /**
2456 * Returns the accounts for a specific user
2457 * @hide
2458 */
2459 public Account[] getAccounts(int userId) {
2460 checkReadAccountsPermission();
2461 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002462 int callingUid = Binder.getCallingUid();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002463 long identityToken = clearCallingIdentity();
2464 try {
2465 synchronized (accounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002466 return getAccountsFromCacheLocked(accounts, null, callingUid, null);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002467 }
2468 } finally {
2469 restoreCallingIdentity(identityToken);
2470 }
2471 }
2472
Amith Yamasanif29f2362012-04-05 18:29:52 -07002473 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002474 * Returns accounts for all running users.
2475 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07002476 * @hide
2477 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002478 public AccountAndUser[] getRunningAccounts() {
2479 final int[] runningUserIds;
2480 try {
2481 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
2482 } catch (RemoteException e) {
2483 // Running in system_server; should never happen
2484 throw new RuntimeException(e);
2485 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07002486 return getAccounts(runningUserIds);
2487 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07002488
Jeff Sharkey6eb96202012-10-10 13:13:54 -07002489 /** {@hide} */
2490 public AccountAndUser[] getAllAccounts() {
2491 final List<UserInfo> users = getUserManager().getUsers();
2492 final int[] userIds = new int[users.size()];
2493 for (int i = 0; i < userIds.length; i++) {
2494 userIds[i] = users.get(i).id;
2495 }
2496 return getAccounts(userIds);
2497 }
2498
2499 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002500 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07002501 for (int userId : userIds) {
2502 UserAccounts userAccounts = getUserAccounts(userId);
2503 if (userAccounts == null) continue;
2504 synchronized (userAccounts.cacheLock) {
2505 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
2506 Binder.getCallingUid(), null);
2507 for (int a = 0; a < accounts.length; a++) {
2508 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07002509 }
2510 }
2511 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002512
2513 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
2514 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07002515 }
2516
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002517 @Override
2518 public Account[] getAccountsAsUser(String type, int userId) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002519 return getAccountsAsUser(type, userId, null, -1);
2520 }
2521
2522 private Account[] getAccountsAsUser(String type, int userId, String callingPackage,
2523 int packageUid) {
2524 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002525 // Only allow the system process to read accounts of other users
2526 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07002527 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08002528 && mContext.checkCallingOrSelfPermission(
2529 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
2530 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002531 throw new SecurityException("User " + UserHandle.getCallingUserId()
2532 + " trying to get account for " + userId);
2533 }
2534
Fred Quintana56285a62010-12-02 14:20:51 -08002535 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2536 Log.v(TAG, "getAccounts: accountType " + type
2537 + ", caller's uid " + Binder.getCallingUid()
2538 + ", pid " + Binder.getCallingPid());
2539 }
Amith Yamasani27db4682013-03-30 17:07:47 -07002540 // If the original calling app was using the framework account chooser activity, we'll
2541 // be passed in the original caller's uid here, which is what should be used for filtering.
2542 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
2543 callingUid = packageUid;
2544 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002545 checkReadAccountsPermission();
2546 long identityToken = clearCallingIdentity();
2547 try {
Simranjit Singh Kohli6a184872015-05-19 10:58:01 -07002548 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002549 synchronized (accounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002550 return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002551 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002552 } finally {
2553 restoreCallingIdentity(identityToken);
2554 }
2555 }
2556
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002557 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08002558 public boolean addSharedAccountAsUser(Account account, int userId) {
2559 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002560 UserAccounts accounts = getUserAccounts(userId);
2561 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Amith Yamasani67df64b2012-12-14 12:09:36 -08002562 ContentValues values = new ContentValues();
2563 values.put(ACCOUNTS_NAME, account.name);
2564 values.put(ACCOUNTS_TYPE, account.type);
2565 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
2566 new String[] {account.name, account.type});
2567 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
2568 if (accountId < 0) {
2569 Log.w(TAG, "insertAccountIntoDatabase: " + account
2570 + ", skipping the DB insert failed");
2571 return false;
2572 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002573 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002574 return true;
2575 }
2576
2577 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002578 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
2579 userId = handleIncomingUser(userId);
2580 UserAccounts accounts = getUserAccounts(userId);
2581 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002582 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002583 final ContentValues values = new ContentValues();
2584 values.put(ACCOUNTS_NAME, newName);
2585 values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
2586 int r = db.update(
2587 TABLE_SHARED_ACCOUNTS,
2588 values,
2589 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
2590 new String[] { account.name, account.type });
2591 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002592 int callingUid = getCallingUid();
2593 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
2594 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002595 // Recursively rename the account.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002596 renameAccountInternal(accounts, account, newName, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002597 }
2598 return r > 0;
2599 }
2600
2601 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08002602 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002603 return removeSharedAccountAsUser(account, userId, getCallingUid());
2604 }
2605
2606 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08002607 userId = handleIncomingUser(userId);
2608 UserAccounts accounts = getUserAccounts(userId);
2609 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002610 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002611 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
2612 new String[] {account.name, account.type});
2613 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002614 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
2615 sharedTableAccountId, accounts, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002616 removeAccountInternal(accounts, account);
2617 }
2618 return r > 0;
2619 }
2620
2621 @Override
2622 public Account[] getSharedAccountsAsUser(int userId) {
2623 userId = handleIncomingUser(userId);
2624 UserAccounts accounts = getUserAccounts(userId);
2625 ArrayList<Account> accountList = new ArrayList<Account>();
2626 Cursor cursor = null;
2627 try {
2628 cursor = accounts.openHelper.getReadableDatabase()
2629 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
2630 null, null, null, null, null);
2631 if (cursor != null && cursor.moveToFirst()) {
2632 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
2633 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
2634 do {
2635 accountList.add(new Account(cursor.getString(nameIndex),
2636 cursor.getString(typeIndex)));
2637 } while (cursor.moveToNext());
2638 }
2639 } finally {
2640 if (cursor != null) {
2641 cursor.close();
2642 }
2643 }
2644 Account[] accountArray = new Account[accountList.size()];
2645 accountList.toArray(accountArray);
2646 return accountArray;
2647 }
2648
2649 @Override
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002650 public Account[] getAccounts(String type) {
2651 return getAccountsAsUser(type, UserHandle.getCallingUserId());
2652 }
2653
Amith Yamasani27db4682013-03-30 17:07:47 -07002654 @Override
2655 public Account[] getAccountsForPackage(String packageName, int uid) {
2656 int callingUid = Binder.getCallingUid();
2657 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
2658 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
2659 + callingUid + " with uid=" + uid);
2660 }
2661 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid);
2662 }
2663
Amith Yamasani3b458ad2013-04-18 18:40:07 -07002664 @Override
2665 public Account[] getAccountsByTypeForPackage(String type, String packageName) {
2666 checkBinderPermission(android.Manifest.permission.INTERACT_ACROSS_USERS);
2667 int packageUid = -1;
2668 try {
2669 packageUid = AppGlobals.getPackageManager().getPackageUid(
2670 packageName, UserHandle.getCallingUserId());
2671 } catch (RemoteException re) {
2672 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
2673 return new Account[0];
2674 }
2675 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid);
2676 }
2677
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002678 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002679 public void getAccountsByFeatures(IAccountManagerResponse response,
Fred Quintana33269202009-04-20 16:05:10 -07002680 String type, String[] features) {
Fred Quintana56285a62010-12-02 14:20:51 -08002681 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2682 Log.v(TAG, "getAccounts: accountType " + type
2683 + ", response " + response
2684 + ", features " + stringArrayToString(features)
2685 + ", caller's uid " + Binder.getCallingUid()
2686 + ", pid " + Binder.getCallingPid());
2687 }
Fred Quintana382601f2010-03-25 12:25:10 -07002688 if (response == null) throw new IllegalArgumentException("response is null");
2689 if (type == null) throw new IllegalArgumentException("accountType is null");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002690 checkReadAccountsPermission();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002691 UserAccounts userAccounts = getUserAccountsForCaller();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002692 int callingUid = Binder.getCallingUid();
Fred Quintana33269202009-04-20 16:05:10 -07002693 long identityToken = clearCallingIdentity();
2694 try {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002695 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002696 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002697 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002698 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002699 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002700 Bundle result = new Bundle();
2701 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
2702 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002703 return;
2704 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002705 new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features,
2706 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07002707 } finally {
2708 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002709 }
2710 }
2711
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002712 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
2713 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
2714 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
2715 try {
2716 if (cursor.moveToNext()) {
2717 return cursor.getLong(0);
2718 }
2719 return -1;
2720 } finally {
2721 cursor.close();
2722 }
2723 }
2724
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002725 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07002726 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002727 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07002728 try {
2729 if (cursor.moveToNext()) {
2730 return cursor.getLong(0);
2731 }
2732 return -1;
2733 } finally {
2734 cursor.close();
2735 }
2736 }
2737
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002738 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07002739 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
2740 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
2741 new String[]{key}, null, null, null);
2742 try {
2743 if (cursor.moveToNext()) {
2744 return cursor.getLong(0);
2745 }
2746 return -1;
2747 } finally {
2748 cursor.close();
2749 }
2750 }
2751
Fred Quintanaa698f422009-04-08 19:14:54 -07002752 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07002753 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07002754 IAccountManagerResponse mResponse;
2755 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07002756 final boolean mExpectActivityLaunch;
2757 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002758 final String mAccountName;
2759 // Indicates if we need to add auth details(like last credential time)
2760 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002761 // If set, we need to update the last authenticated time. This is
2762 // currently
2763 // used on
2764 // successful confirming credentials.
2765 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07002766
Fred Quintana33269202009-04-20 16:05:10 -07002767 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07002768 private int mNumRequestContinued = 0;
2769 private int mNumErrors = 0;
2770
Fred Quintana60307342009-03-24 22:48:12 -07002771 IAccountAuthenticator mAuthenticator = null;
2772
Fred Quintana8570f742010-02-18 10:32:54 -08002773 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002774 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08002775
Amith Yamasani04e0d262012-02-14 11:50:53 -08002776 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002777 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
2778 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002779 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
2780 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
2781 }
2782
2783 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
2784 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
2785 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07002786 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08002787 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07002788 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08002789 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08002790 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07002791 mResponse = response;
2792 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07002793 mExpectActivityLaunch = expectActivityLaunch;
2794 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002795 mAccountName = accountName;
2796 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002797 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002798
Fred Quintanaa698f422009-04-08 19:14:54 -07002799 synchronized (mSessions) {
2800 mSessions.put(toString(), this);
2801 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002802 if (response != null) {
2803 try {
2804 response.asBinder().linkToDeath(this, 0 /* flags */);
2805 } catch (RemoteException e) {
2806 mResponse = null;
2807 binderDied();
2808 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002809 }
Fred Quintana60307342009-03-24 22:48:12 -07002810 }
2811
Fred Quintanaa698f422009-04-08 19:14:54 -07002812 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07002813 if (mResponse == null) {
2814 // this session has already been closed
2815 return null;
2816 }
Fred Quintana60307342009-03-24 22:48:12 -07002817 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07002818 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07002819 return response;
2820 }
2821
Fred Quintanaa698f422009-04-08 19:14:54 -07002822 private void close() {
2823 synchronized (mSessions) {
2824 if (mSessions.remove(toString()) == null) {
2825 // the session was already closed, so bail out now
2826 return;
2827 }
2828 }
2829 if (mResponse != null) {
2830 // stop listening for response deaths
2831 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
2832
2833 // clear this so that we don't accidentally send any further results
2834 mResponse = null;
2835 }
2836 cancelTimeout();
2837 unbind();
2838 }
2839
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002840 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002841 public void binderDied() {
2842 mResponse = null;
2843 close();
2844 }
2845
2846 protected String toDebugString() {
2847 return toDebugString(SystemClock.elapsedRealtime());
2848 }
2849
2850 protected String toDebugString(long now) {
2851 return "Session: expectLaunch " + mExpectActivityLaunch
2852 + ", connected " + (mAuthenticator != null)
2853 + ", stats (" + mNumResults + "/" + mNumRequestContinued
2854 + "/" + mNumErrors + ")"
2855 + ", lifetime " + ((now - mCreationTime) / 1000.0);
2856 }
2857
Fred Quintana60307342009-03-24 22:48:12 -07002858 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07002859 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2860 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
2861 }
Fred Quintanab839afc2009-10-14 15:57:28 -07002862 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07002863 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002864 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07002865 }
2866 }
2867
2868 private void unbind() {
2869 if (mAuthenticator != null) {
2870 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07002871 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07002872 }
2873 }
2874
2875 public void scheduleTimeout() {
2876 mMessageHandler.sendMessageDelayed(
2877 mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
2878 }
2879
2880 public void cancelTimeout() {
2881 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
2882 }
2883
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002884 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07002885 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07002886 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07002887 try {
2888 run();
2889 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002890 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07002891 "remote exception");
2892 }
Fred Quintana60307342009-03-24 22:48:12 -07002893 }
2894
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002895 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07002896 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07002897 mAuthenticator = null;
2898 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07002899 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07002900 try {
2901 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
2902 "disconnected");
2903 } catch (RemoteException e) {
2904 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2905 Log.v(TAG, "Session.onServiceDisconnected: "
2906 + "caught RemoteException while responding", e);
2907 }
2908 }
Fred Quintana60307342009-03-24 22:48:12 -07002909 }
2910 }
2911
Fred Quintanab839afc2009-10-14 15:57:28 -07002912 public abstract void run() throws RemoteException;
2913
Fred Quintana60307342009-03-24 22:48:12 -07002914 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07002915 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07002916 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07002917 try {
2918 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
2919 "timeout");
2920 } catch (RemoteException e) {
2921 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2922 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
2923 e);
2924 }
2925 }
Fred Quintana60307342009-03-24 22:48:12 -07002926 }
2927 }
2928
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002929 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002930 public void onResult(Bundle result) {
2931 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002932 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002933 if (result != null) {
2934 boolean isSuccessfulConfirmCreds = result.getBoolean(
2935 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002936 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002937 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
2938 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002939 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002940 // mUpdateLastAuthenticatedTime is true and the confirmRequest
2941 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07002942 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002943 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002944 if (needUpdate || mAuthDetailsRequired) {
2945 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
2946 if (needUpdate && accountPresent) {
2947 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
2948 }
2949 if (mAuthDetailsRequired) {
2950 long lastAuthenticatedTime = -1;
2951 if (accountPresent) {
2952 lastAuthenticatedTime = DatabaseUtils.longForQuery(
2953 mAccounts.openHelper.getReadableDatabase(),
2954 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
2955 + " from " +
2956 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
2957 + ACCOUNTS_TYPE + "=?",
2958 new String[] {
2959 mAccountName, mAccountType
2960 });
2961 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07002962 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002963 lastAuthenticatedTime);
2964 }
2965 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002966 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002967 if (result != null
2968 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2969 /*
2970 * The Authenticator API allows third party authenticators to
2971 * supply arbitrary intents to other apps that they can run,
2972 * this can be very bad when those apps are in the system like
2973 * the System Settings.
2974 */
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002975 int authenticatorUid = Binder.getCallingUid();
Carlos Valdivia416747a2013-10-14 17:22:42 -07002976 long bid = Binder.clearCallingIdentity();
2977 try {
2978 PackageManager pm = mContext.getPackageManager();
2979 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
2980 int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
2981 if (PackageManager.SIGNATURE_MATCH !=
2982 pm.checkSignatures(authenticatorUid, targetUid)) {
2983 throw new SecurityException(
2984 "Activity to be started with KEY_INTENT must " +
2985 "share Authenticator's signatures");
2986 }
2987 } finally {
2988 Binder.restoreCallingIdentity(bid);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002989 }
2990 }
2991 if (result != null
2992 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002993 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2994 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002995 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2996 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002997 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
2998 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002999 }
Fred Quintana60307342009-03-24 22:48:12 -07003000 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003001 IAccountManagerResponse response;
3002 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003003 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003004 response = mResponse;
3005 } else {
3006 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003007 }
Fred Quintana60307342009-03-24 22:48:12 -07003008 if (response != null) {
3009 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07003010 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08003011 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3012 Log.v(TAG, getClass().getSimpleName()
3013 + " calling onError() on response " + response);
3014 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003015 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07003016 "null bundle returned");
3017 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08003018 if (mStripAuthTokenFromResult) {
3019 result.remove(AccountManager.KEY_AUTHTOKEN);
3020 }
Fred Quintana56285a62010-12-02 14:20:51 -08003021 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3022 Log.v(TAG, getClass().getSimpleName()
3023 + " calling onResult() on response " + response);
3024 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003025 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
3026 (intent == null)) {
3027 // All AccountManager error codes are greater than 0
3028 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
3029 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3030 } else {
3031 response.onResult(result);
3032 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003033 }
Fred Quintana60307342009-03-24 22:48:12 -07003034 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003035 // if the caller is dead then there is no one to care about remote exceptions
3036 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3037 Log.v(TAG, "failure while notifying response", e);
3038 }
Fred Quintana60307342009-03-24 22:48:12 -07003039 }
3040 }
3041 }
Fred Quintana60307342009-03-24 22:48:12 -07003042
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003043 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003044 public void onRequestContinued() {
3045 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07003046 }
3047
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003048 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003049 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003050 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07003051 IAccountManagerResponse response = getResponseAndClose();
3052 if (response != null) {
3053 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08003054 Log.v(TAG, getClass().getSimpleName()
3055 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07003056 }
3057 try {
3058 response.onError(errorCode, errorMessage);
3059 } catch (RemoteException e) {
3060 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3061 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
3062 }
3063 }
3064 } else {
3065 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3066 Log.v(TAG, "Session.onError: already closed");
3067 }
Fred Quintana60307342009-03-24 22:48:12 -07003068 }
3069 }
Fred Quintanab839afc2009-10-14 15:57:28 -07003070
3071 /**
3072 * find the component name for the authenticator and initiate a bind
3073 * if no authenticator or the bind fails then return false, otherwise return true
3074 */
3075 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003076 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
3077 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
3078 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07003079 if (authenticatorInfo == null) {
3080 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3081 Log.v(TAG, "there is no authenticator for " + authenticatorType
3082 + ", bailing out");
3083 }
3084 return false;
3085 }
3086
3087 Intent intent = new Intent();
3088 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
3089 intent.setComponent(authenticatorInfo.componentName);
3090 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3091 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
3092 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08003093 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
3094 new UserHandle(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07003095 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3096 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
3097 }
3098 return false;
3099 }
3100
3101
3102 return true;
3103 }
Fred Quintana60307342009-03-24 22:48:12 -07003104 }
3105
3106 private class MessageHandler extends Handler {
3107 MessageHandler(Looper looper) {
3108 super(looper);
3109 }
Costin Manolache3348f142009-09-29 18:58:36 -07003110
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003111 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003112 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07003113 switch (msg.what) {
3114 case MESSAGE_TIMED_OUT:
3115 Session session = (Session)msg.obj;
3116 session.onTimedOut();
3117 break;
3118
Amith Yamasani5be347b2013-03-31 17:44:31 -07003119 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00003120 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07003121 break;
3122
Fred Quintana60307342009-03-24 22:48:12 -07003123 default:
3124 throw new IllegalStateException("unhandled message: " + msg.what);
3125 }
3126 }
3127 }
3128
Amith Yamasani04e0d262012-02-14 11:50:53 -08003129 private static String getDatabaseName(int userId) {
3130 File systemDir = Environment.getSystemSecureDirectory();
Amith Yamasani61f57372012-08-31 12:12:28 -07003131 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003132 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07003133 // Migrate old file, if it exists, to the new location.
3134 // Make sure the new file doesn't already exist. A dummy file could have been
3135 // accidentally created in the old location, causing the new one to become corrupted
3136 // as well.
Amith Yamasani04e0d262012-02-14 11:50:53 -08003137 File oldFile = new File(systemDir, DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07003138 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07003139 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07003140 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07003141 if (!userDir.exists()) {
3142 if (!userDir.mkdirs()) {
3143 throw new IllegalStateException("User dir cannot be created: " + userDir);
3144 }
3145 }
3146 if (!oldFile.renameTo(databaseFile)) {
3147 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
3148 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003149 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08003150 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003151 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08003152 }
3153
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003154 private static class DebugDbHelper{
3155 private DebugDbHelper() {
3156 }
3157
3158 private static String TABLE_DEBUG = "debug_table";
3159
3160 // Columns for the table
3161 private static String ACTION_TYPE = "action_type";
3162 private static String TIMESTAMP = "time";
3163 private static String CALLER_UID = "caller_uid";
3164 private static String TABLE_NAME = "table_name";
3165 private static String KEY = "primary_key";
3166
3167 // These actions correspond to the occurrence of real actions. Since
3168 // these are called by the authenticators, the uid associated will be
3169 // of the authenticator.
3170 private static String ACTION_SET_PASSWORD = "action_set_password";
3171 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
3172 private static String ACTION_ACCOUNT_ADD = "action_account_add";
3173 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
3174 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
3175 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
3176
3177 // These actions don't necessarily correspond to any action on
3178 // accountDb taking place. As an example, there might be a request for
3179 // addingAccount, which might not lead to addition of account on grounds
3180 // of bad authentication. We will still be logging it to keep track of
3181 // who called.
3182 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
3183 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
3184 private static String ACTION_CALLED_ACCOUNT_RENAME = "action_called_account_rename";
3185
3186 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3187
3188 private static String UPDATE_WHERE_CLAUSE = KEY + "=?";
3189
3190 private static void createDebugTable(SQLiteDatabase db) {
3191 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
3192 + ACCOUNTS_ID + " INTEGER,"
3193 + ACTION_TYPE + " TEXT NOT NULL, "
3194 + TIMESTAMP + " DATETIME,"
3195 + CALLER_UID + " INTEGER NOT NULL,"
3196 + TABLE_NAME + " TEXT NOT NULL,"
3197 + KEY + " INTEGER PRIMARY KEY)");
3198 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
3199 }
3200 }
3201
3202 private void logRecord(UserAccounts accounts, String action, String tableName) {
3203 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3204 logRecord(db, action, tableName, -1, accounts);
3205 }
3206
3207 /*
3208 * This function receives an opened writable database.
3209 */
3210 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3211 UserAccounts userAccount) {
3212 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
3213 }
3214
3215 /*
3216 * This function receives an opened writable database.
3217 */
3218 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3219 UserAccounts userAccount, int callingUid) {
3220 SQLiteStatement logStatement = userAccount.statementForLogging;
3221 logStatement.bindLong(1, accountId);
3222 logStatement.bindString(2, action);
3223 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
3224 logStatement.bindLong(4, callingUid);
3225 logStatement.bindString(5, tableName);
3226 logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
3227 logStatement.execute();
3228 logStatement.clearBindings();
3229 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
3230 % MAX_DEBUG_DB_SIZE;
3231 }
3232
3233 /*
3234 * This should only be called once to compile the sql statement for logging
3235 * and to find the insertion point.
3236 */
3237 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
3238 UserAccounts userAccount) {
3239 // Initialize the count if not done earlier.
3240 int size = (int) getDebugTableRowCount(db);
3241 if (size >= MAX_DEBUG_DB_SIZE) {
3242 // Table is full, and we need to find the point where to insert.
3243 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
3244 } else {
3245 userAccount.debugDbInsertionPoint = size;
3246 }
3247 compileSqlStatementForLogging(db, userAccount);
3248 }
3249
3250 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
3251 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
3252 + " VALUES (?,?,?,?,?,?)";
3253 userAccount.statementForLogging = db.compileStatement(sql);
3254 }
3255
3256 private long getDebugTableRowCount(SQLiteDatabase db) {
3257 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
3258 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3259 }
3260
3261 /*
3262 * Finds the row key where the next insertion should take place. This should
3263 * be invoked only if the table has reached its full capacity.
3264 */
3265 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
3266 // This query finds the smallest timestamp value (and if 2 records have
3267 // same timestamp, the choose the lower id).
3268 String queryCountDebugDbRows = new StringBuilder()
3269 .append("SELECT ").append(DebugDbHelper.KEY)
3270 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
3271 .append(" ORDER BY ")
3272 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
3273 .append(" LIMIT 1")
3274 .toString();
3275 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3276 }
3277
Amith Yamasani04e0d262012-02-14 11:50:53 -08003278 static class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08003279
Amith Yamasani04e0d262012-02-14 11:50:53 -08003280 public DatabaseHelper(Context context, int userId) {
3281 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
Fred Quintana60307342009-03-24 22:48:12 -07003282 }
3283
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003284 /**
3285 * This call needs to be made while the mCacheLock is held. The way to
3286 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
3287 * @param db The database.
3288 */
Fred Quintana60307342009-03-24 22:48:12 -07003289 @Override
3290 public void onCreate(SQLiteDatabase db) {
3291 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
3292 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3293 + ACCOUNTS_NAME + " TEXT NOT NULL, "
3294 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
3295 + ACCOUNTS_PASSWORD + " TEXT, "
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003296 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003297 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
Fred Quintana60307342009-03-24 22:48:12 -07003298 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
3299
3300 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
3301 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3302 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
3303 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
3304 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
3305 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
3306
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003307 createGrantsTable(db);
3308
Fred Quintana60307342009-03-24 22:48:12 -07003309 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
3310 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3311 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
3312 + EXTRAS_KEY + " TEXT NOT NULL, "
3313 + EXTRAS_VALUE + " TEXT, "
3314 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
3315
3316 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
3317 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
3318 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07003319
Amith Yamasani67df64b2012-12-14 12:09:36 -08003320 createSharedAccountsTable(db);
3321
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003322 createAccountsDeletionTrigger(db);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003323
3324 DebugDbHelper.createDebugTable(db);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003325 }
3326
Amith Yamasani67df64b2012-12-14 12:09:36 -08003327 private void createSharedAccountsTable(SQLiteDatabase db) {
3328 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
3329 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3330 + ACCOUNTS_NAME + " TEXT NOT NULL, "
3331 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
3332 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
3333 }
3334
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003335 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
3336 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
3337 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
3338 }
3339
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003340 private void addOldAccountNameColumn(SQLiteDatabase db) {
3341 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
3342 }
3343
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003344 private void addDebugTable(SQLiteDatabase db) {
3345 DebugDbHelper.createDebugTable(db);
3346 }
3347
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003348 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003349 db.execSQL(""
3350 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
3351 + " BEGIN"
3352 + " DELETE FROM " + TABLE_AUTHTOKENS
3353 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
3354 + " DELETE FROM " + TABLE_EXTRAS
3355 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003356 + " DELETE FROM " + TABLE_GRANTS
3357 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07003358 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07003359 }
3360
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003361 private void createGrantsTable(SQLiteDatabase db) {
3362 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
3363 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
3364 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
3365 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
3366 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
3367 + "," + GRANTS_GRANTEE_UID + "))");
3368 }
3369
Fred Quintana60307342009-03-24 22:48:12 -07003370 @Override
3371 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003372 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07003373
Fred Quintanaa698f422009-04-08 19:14:54 -07003374 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003375 // no longer need to do anything since the work is done
3376 // when upgrading from version 2
3377 oldVersion++;
3378 }
3379
3380 if (oldVersion == 2) {
3381 createGrantsTable(db);
3382 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
3383 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07003384 oldVersion++;
3385 }
Costin Manolache3348f142009-09-29 18:58:36 -07003386
3387 if (oldVersion == 3) {
3388 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
3389 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
3390 oldVersion++;
3391 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08003392
3393 if (oldVersion == 4) {
3394 createSharedAccountsTable(db);
3395 oldVersion++;
3396 }
3397
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003398 if (oldVersion == 5) {
3399 addOldAccountNameColumn(db);
3400 oldVersion++;
3401 }
3402
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003403 if (oldVersion == 6) {
3404 addLastSuccessfullAuthenticatedTimeColumn(db);
3405 oldVersion++;
3406 }
3407
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003408 if (oldVersion == 7) {
3409 addDebugTable(db);
3410 oldVersion++;
3411 }
3412
Amith Yamasani67df64b2012-12-14 12:09:36 -08003413 if (oldVersion != newVersion) {
3414 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
3415 }
Fred Quintana60307342009-03-24 22:48:12 -07003416 }
3417
3418 @Override
3419 public void onOpen(SQLiteDatabase db) {
3420 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
3421 }
3422 }
3423
Fred Quintana60307342009-03-24 22:48:12 -07003424 public IBinder onBind(Intent intent) {
3425 return asBinder();
3426 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003427
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003428 /**
3429 * Searches array of arguments for the specified string
3430 * @param args array of argument strings
3431 * @param value value to search for
3432 * @return true if the value is contained in the array
3433 */
3434 private static boolean scanArgs(String[] args, String value) {
3435 if (args != null) {
3436 for (String arg : args) {
3437 if (value.equals(arg)) {
3438 return true;
3439 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003440 }
3441 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003442 return false;
3443 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003444
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003445 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003446 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07003447 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
3448 != PackageManager.PERMISSION_GRANTED) {
3449 fout.println("Permission Denial: can't dump AccountsManager from from pid="
3450 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
3451 + " without permission " + android.Manifest.permission.DUMP);
3452 return;
3453 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003454 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003455 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07003456
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003457 final List<UserInfo> users = getUserManager().getUsers();
3458 for (UserInfo user : users) {
3459 ipw.println("User " + user + ":");
3460 ipw.increaseIndent();
3461 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
3462 ipw.println();
3463 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003464 }
3465 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003466
Amith Yamasani04e0d262012-02-14 11:50:53 -08003467 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
3468 String[] args, boolean isCheckinRequest) {
3469 synchronized (userAccounts.cacheLock) {
3470 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003471
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003472 if (isCheckinRequest) {
3473 // This is a checkin request. *Only* upload the account types and the count of each.
3474 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
3475 null, null, ACCOUNTS_TYPE, null, null);
3476 try {
3477 while (cursor.moveToNext()) {
3478 // print type,count
3479 fout.println(cursor.getString(0) + "," + cursor.getString(1));
3480 }
3481 } finally {
3482 if (cursor != null) {
3483 cursor.close();
3484 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003485 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003486 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003487 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07003488 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003489 fout.println("Accounts: " + accounts.length);
3490 for (Account account : accounts) {
3491 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003492 }
Fred Quintana307da1a2010-01-21 14:24:20 -08003493
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003494 // Add debug information.
3495 fout.println();
3496 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
3497 null, null, null, null, DebugDbHelper.TIMESTAMP);
3498 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
3499 fout.println("Accounts History");
3500 try {
3501 while (cursor.moveToNext()) {
3502 // print type,count
3503 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
3504 cursor.getString(2) + "," + cursor.getString(3) + ","
3505 + cursor.getString(4) + "," + cursor.getString(5));
3506 }
3507 } finally {
3508 cursor.close();
3509 }
3510
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003511 fout.println();
3512 synchronized (mSessions) {
3513 final long now = SystemClock.elapsedRealtime();
3514 fout.println("Active Sessions: " + mSessions.size());
3515 for (Session session : mSessions.values()) {
3516 fout.println(" " + session.toDebugString(now));
3517 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003518 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003519
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003520 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003521 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003522 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07003523 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003524 }
3525
Amith Yamasani04e0d262012-02-14 11:50:53 -08003526 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Dianne Hackborn41203752012-08-31 14:05:51 -07003527 Intent intent, int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003528 long identityToken = clearCallingIdentity();
3529 try {
3530 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3531 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
3532 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003533
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003534 if (intent.getComponent() != null &&
3535 GrantCredentialsPermissionActivity.class.getName().equals(
3536 intent.getComponent().getClassName())) {
Dianne Hackborn41203752012-08-31 14:05:51 -07003537 createNoCredentialsPermissionNotification(account, intent, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003538 } else {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003539 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07003540 intent.addCategory(String.valueOf(notificationId));
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003541 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003542 Context contextForUser = getContextForUser(user);
Fred Quintana33f889a2009-09-14 17:31:26 -07003543 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003544 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04003545 Notification n = new Notification.Builder(contextForUser)
3546 .setWhen(0)
3547 .setSmallIcon(android.R.drawable.stat_sys_warning)
3548 .setColor(contextForUser.getColor(
3549 com.android.internal.R.color.system_notification_accent_color))
3550 .setContentTitle(String.format(notificationTitleFormat, account.name))
3551 .setContentText(message)
3552 .setContentIntent(PendingIntent.getActivityAsUser(
3553 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
3554 null, user))
3555 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003556 installNotification(notificationId, n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003557 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003558 } finally {
3559 restoreCallingIdentity(identityToken);
3560 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003561 }
3562
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003563 protected void installNotification(final int notificationId, final Notification n,
3564 UserHandle user) {
Fred Quintana56285a62010-12-02 14:20:51 -08003565 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003566 .notifyAsUser(null, notificationId, n, user);
Fred Quintana56285a62010-12-02 14:20:51 -08003567 }
3568
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003569 protected void cancelNotification(int id, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003570 long identityToken = clearCallingIdentity();
3571 try {
3572 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003573 .cancelAsUser(null, id, user);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003574 } finally {
3575 restoreCallingIdentity(identityToken);
3576 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003577 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003578
Fred Quintanab38eb142010-02-24 13:40:54 -08003579 /** Succeeds if any of the specified permissions are granted. */
3580 private void checkBinderPermission(String... permissions) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003581 final int uid = Binder.getCallingUid();
Fred Quintanab38eb142010-02-24 13:40:54 -08003582
3583 for (String perm : permissions) {
3584 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
3585 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08003586 Log.v(TAG, " caller uid " + uid + " has " + perm);
Fred Quintanab38eb142010-02-24 13:40:54 -08003587 }
3588 return;
3589 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003590 }
Fred Quintanab38eb142010-02-24 13:40:54 -08003591 String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
Fred Quintana56285a62010-12-02 14:20:51 -08003592 Log.w(TAG, " " + msg);
Fred Quintanab38eb142010-02-24 13:40:54 -08003593 throw new SecurityException(msg);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003594 }
3595
Amith Yamasani67df64b2012-12-14 12:09:36 -08003596 private int handleIncomingUser(int userId) {
3597 try {
3598 return ActivityManagerNative.getDefault().handleIncomingUser(
3599 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
3600 } catch (RemoteException re) {
3601 // Shouldn't happen, local.
3602 }
3603 return userId;
3604 }
3605
Christopher Tateccbf84f2013-05-08 15:25:41 -07003606 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003607 final int callingUserId = UserHandle.getUserId(callingUid);
3608
3609 final PackageManager userPackageManager;
3610 try {
3611 userPackageManager = mContext.createPackageContextAsUser(
3612 "android", 0, new UserHandle(callingUserId)).getPackageManager();
3613 } catch (NameNotFoundException e) {
3614 return false;
3615 }
3616
3617 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07003618 for (String name : packages) {
3619 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003620 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08003621 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08003622 && (packageInfo.applicationInfo.privateFlags
3623 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07003624 return true;
3625 }
3626 } catch (PackageManager.NameNotFoundException e) {
3627 return false;
3628 }
3629 }
3630 return false;
3631 }
3632
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003633 private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
Christopher Tateccbf84f2013-05-08 15:25:41 -07003634 final boolean isPrivileged = isPrivileged(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07003635 final boolean fromAuthenticator = account != null
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003636 && isAccountManagedByCaller(account.type, callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07003637 final boolean hasExplicitGrants = account != null
Amith Yamasani04e0d262012-02-14 11:50:53 -08003638 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003639 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3640 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08003641 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003642 + ": is authenticator? " + fromAuthenticator
3643 + ", has explicit permission? " + hasExplicitGrants);
3644 }
Christopher Tateccbf84f2013-05-08 15:25:41 -07003645 return fromAuthenticator || hasExplicitGrants || isPrivileged;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003646 }
3647
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003648 private boolean isAccountManagedByCaller(String accountType, int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003649 final int callingUserId = UserHandle.getUserId(callingUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003650 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003651 mAuthenticatorCache.getAllServices(callingUserId)) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003652 if (serviceInfo.type.type.equals(accountType)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003653 /*
3654 * We can't simply compare uids because uids can be recycled before the
3655 * authenticator cache is updated.
3656 */
3657 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
3658 return sigChk == PackageManager.SIGNATURE_MATCH;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003659 }
3660 }
3661 return false;
3662 }
3663
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003664 private boolean isAccountPresentForCaller(String accountName, String accountType) {
3665 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
3666 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
3667 if (account.name.equals(accountName)) {
3668 return true;
3669 }
3670 }
3671 }
3672 return false;
3673 }
3674
Amith Yamasani04e0d262012-02-14 11:50:53 -08003675 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
3676 int callerUid) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003677 if (callerUid == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003678 return true;
3679 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003680 UserAccounts accounts = getUserAccountsForCaller();
3681 synchronized (accounts.cacheLock) {
3682 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
3683 String[] args = { String.valueOf(callerUid), authTokenType,
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003684 account.name, account.type};
3685 final boolean permissionGranted =
3686 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
3687 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
3688 // TODO: Skip this check when running automated tests. Replace this
3689 // with a more general solution.
3690 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08003691 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003692 + " but ignoring since device is in test harness.");
3693 return true;
3694 }
3695 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003696 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003697 }
3698
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003699 private boolean isSystemUid(int callingUid) {
3700 String[] packages = null;
3701 long ident = Binder.clearCallingIdentity();
3702 try {
3703 packages = mPackageManager.getPackagesForUid(callingUid);
3704 } finally {
3705 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07003706 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003707 if (packages != null) {
3708 for (String name : packages) {
3709 try {
3710 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
3711 if (packageInfo != null
3712 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
3713 != 0) {
3714 return true;
3715 }
3716 } catch (PackageManager.NameNotFoundException e) {
3717 Log.w(TAG, String.format("Could not find package [%s]", name), e);
3718 }
3719 }
3720 } else {
3721 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07003722 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003723 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00003724 }
3725
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003726 private boolean isAccountOwnedByCallingUid(String accountType, int callingUid) {
3727 if (!isAccountManagedByCaller(accountType, callingUid)) {
3728 String msg = "caller uid " + callingUid + " is different than the authenticator's uid";
3729 Log.w(TAG, msg);
3730 return false;
3731 }
3732 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3733 Log.v(TAG, "caller uid " + callingUid + " is the same as the authenticator's uid");
3734 }
3735 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003736 }
3737
3738 private void checkReadAccountsPermission() {
3739 checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
3740 }
3741
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003742 private boolean canUserModifyAccounts(int userId) {
3743 if (getUserManager().getUserRestrictions(new UserHandle(userId))
3744 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
3745 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08003746 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003747 return true;
3748 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01003749
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003750 private boolean canUserModifyAccountsForType(int userId, String accountType) {
Sander Alewijnseda1350f2014-05-08 16:59:42 +01003751 DevicePolicyManager dpm = (DevicePolicyManager) mContext
3752 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003753 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02003754 if (typesArray == null) {
3755 return true;
3756 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01003757 for (String forbiddenType : typesArray) {
3758 if (forbiddenType.equals(accountType)) {
3759 return false;
3760 }
3761 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08003762 return true;
3763 }
3764
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003765 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07003766 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
3767 throws RemoteException {
3768 final int callingUid = getCallingUid();
3769
Amith Yamasani27db4682013-03-30 17:07:47 -07003770 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07003771 throw new SecurityException();
3772 }
3773
3774 if (value) {
3775 grantAppPermission(account, authTokenType, uid);
3776 } else {
3777 revokeAppPermission(account, authTokenType, uid);
3778 }
3779 }
3780
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003781 /**
3782 * Allow callers with the given uid permission to get credentials for account/authTokenType.
3783 * <p>
3784 * Although this is public it can only be accessed via the AccountManagerService object
3785 * which is in the system. This means we don't need to protect it with permissions.
3786 * @hide
3787 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07003788 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07003789 if (account == null || authTokenType == null) {
3790 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07003791 return;
3792 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003793 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003794 synchronized (accounts.cacheLock) {
3795 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003796 db.beginTransaction();
3797 try {
3798 long accountId = getAccountIdLocked(db, account);
3799 if (accountId >= 0) {
3800 ContentValues values = new ContentValues();
3801 values.put(GRANTS_ACCOUNTS_ID, accountId);
3802 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
3803 values.put(GRANTS_GRANTEE_UID, uid);
3804 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
3805 db.setTransactionSuccessful();
3806 }
3807 } finally {
3808 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003809 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003810 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
3811 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003812 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003813 }
3814
3815 /**
3816 * Don't allow callers with the given uid permission to get credentials for
3817 * account/authTokenType.
3818 * <p>
3819 * Although this is public it can only be accessed via the AccountManagerService object
3820 * which is in the system. This means we don't need to protect it with permissions.
3821 * @hide
3822 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07003823 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07003824 if (account == null || authTokenType == null) {
3825 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07003826 return;
3827 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003828 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003829 synchronized (accounts.cacheLock) {
3830 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003831 db.beginTransaction();
3832 try {
3833 long accountId = getAccountIdLocked(db, account);
3834 if (accountId >= 0) {
3835 db.delete(TABLE_GRANTS,
3836 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
3837 + GRANTS_GRANTEE_UID + "=?",
3838 new String[]{String.valueOf(accountId), authTokenType,
3839 String.valueOf(uid)});
3840 db.setTransactionSuccessful();
3841 }
3842 } finally {
3843 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003844 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003845 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
3846 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003847 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003848 }
Fred Quintana56285a62010-12-02 14:20:51 -08003849
3850 static final private String stringArrayToString(String[] value) {
3851 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
3852 }
3853
Amith Yamasani04e0d262012-02-14 11:50:53 -08003854 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
3855 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003856 if (oldAccountsForType != null) {
3857 ArrayList<Account> newAccountsList = new ArrayList<Account>();
3858 for (Account curAccount : oldAccountsForType) {
3859 if (!curAccount.equals(account)) {
3860 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08003861 }
3862 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003863 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003864 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003865 } else {
3866 Account[] newAccountsForType = new Account[newAccountsList.size()];
3867 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003868 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003869 }
Fred Quintana56285a62010-12-02 14:20:51 -08003870 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003871 accounts.userDataCache.remove(account);
3872 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003873 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08003874 }
3875
3876 /**
3877 * This assumes that the caller has already checked that the account is not already present.
3878 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08003879 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
3880 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003881 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
3882 Account[] newAccountsForType = new Account[oldLength + 1];
3883 if (accountsForType != null) {
3884 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08003885 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003886 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003887 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08003888 }
3889
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003890 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07003891 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003892 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07003893 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003894 return unfiltered;
3895 }
Amith Yamasani0c19bf52013-10-03 10:34:58 -07003896 UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
3897 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003898 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003899 // If any of the packages is a white listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003900 // otherwise return non-shared accounts only.
3901 // This might be a temporary way to specify a whitelist
3902 String whiteList = mContext.getResources().getString(
3903 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
3904 for (String packageName : packages) {
3905 if (whiteList.contains(";" + packageName + ";")) {
3906 return unfiltered;
3907 }
3908 }
3909 ArrayList<Account> allowed = new ArrayList<Account>();
3910 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
3911 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003912 String requiredAccountType = "";
3913 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07003914 // If there's an explicit callingPackage specified, check if that package
3915 // opted in to see restricted accounts.
3916 if (callingPackage != null) {
3917 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003918 if (pi != null && pi.restrictedAccountType != null) {
3919 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07003920 }
3921 } else {
3922 // Otherwise check if the callingUid has a package that has opted in
3923 for (String packageName : packages) {
3924 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
3925 if (pi != null && pi.restrictedAccountType != null) {
3926 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07003927 break;
3928 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003929 }
3930 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003931 } catch (NameNotFoundException nnfe) {
3932 }
3933 for (Account account : unfiltered) {
3934 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003935 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07003936 } else {
3937 boolean found = false;
3938 for (Account shared : sharedAccounts) {
3939 if (shared.equals(account)) {
3940 found = true;
3941 break;
3942 }
3943 }
3944 if (!found) {
3945 allowed.add(account);
3946 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003947 }
3948 }
3949 Account[] filtered = new Account[allowed.size()];
3950 allowed.toArray(filtered);
3951 return filtered;
3952 } else {
3953 return unfiltered;
3954 }
3955 }
3956
Amith Yamasani27db4682013-03-30 17:07:47 -07003957 /*
3958 * packageName can be null. If not null, it should be used to filter out restricted accounts
3959 * that the package is not allowed to access.
3960 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003961 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07003962 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003963 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003964 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003965 if (accounts == null) {
3966 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08003967 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003968 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07003969 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08003970 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003971 } else {
3972 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003973 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003974 totalLength += accounts.length;
3975 }
3976 if (totalLength == 0) {
3977 return EMPTY_ACCOUNT_ARRAY;
3978 }
3979 Account[] accounts = new Account[totalLength];
3980 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003981 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003982 System.arraycopy(accountsOfType, 0, accounts, totalLength,
3983 accountsOfType.length);
3984 totalLength += accountsOfType.length;
3985 }
Amith Yamasani27db4682013-03-30 17:07:47 -07003986 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08003987 }
3988 }
3989
Amith Yamasani04e0d262012-02-14 11:50:53 -08003990 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
3991 Account account, String key, String value) {
3992 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003993 if (userDataForAccount == null) {
3994 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003995 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003996 }
3997 if (value == null) {
3998 userDataForAccount.remove(key);
3999 } else {
4000 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004001 }
4002 }
4003
Carlos Valdivia91979be2015-05-22 14:11:35 -07004004 protected String readCachedTokenInternal(
4005 UserAccounts accounts,
4006 Account account,
4007 String tokenType,
4008 String callingPackage,
4009 byte[] pkgSigDigest) {
4010 synchronized (accounts.cacheLock) {
4011 TokenCache cache = getTokenCacheForAccountLocked(accounts, account);
4012 return cache.get(tokenType, callingPackage, pkgSigDigest);
4013 }
4014 }
4015
Amith Yamasani04e0d262012-02-14 11:50:53 -08004016 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4017 Account account, String key, String value) {
4018 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004019 if (authTokensForAccount == null) {
4020 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004021 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004022 }
4023 if (value == null) {
4024 authTokensForAccount.remove(key);
4025 } else {
4026 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004027 }
4028 }
4029
Amith Yamasani04e0d262012-02-14 11:50:53 -08004030 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
4031 String authTokenType) {
4032 synchronized (accounts.cacheLock) {
4033 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004034 if (authTokensForAccount == null) {
4035 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08004036 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004037 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004038 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004039 }
4040 return authTokensForAccount.get(authTokenType);
4041 }
4042 }
4043
Amith Yamasani04e0d262012-02-14 11:50:53 -08004044 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
4045 synchronized (accounts.cacheLock) {
4046 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004047 if (userDataForAccount == null) {
4048 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08004049 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004050 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004051 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004052 }
4053 return userDataForAccount.get(key);
4054 }
4055 }
4056
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004057 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
4058 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004059 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004060 Cursor cursor = db.query(TABLE_EXTRAS,
4061 COLUMNS_EXTRAS_KEY_AND_VALUE,
4062 SELECTION_USERDATA_BY_ACCOUNT,
4063 new String[]{account.name, account.type},
4064 null, null, null);
4065 try {
4066 while (cursor.moveToNext()) {
4067 final String tmpkey = cursor.getString(0);
4068 final String value = cursor.getString(1);
4069 userDataForAccount.put(tmpkey, value);
4070 }
4071 } finally {
4072 cursor.close();
4073 }
4074 return userDataForAccount;
4075 }
4076
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004077 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
4078 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004079 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004080 Cursor cursor = db.query(TABLE_AUTHTOKENS,
4081 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
4082 SELECTION_AUTHTOKENS_BY_ACCOUNT,
4083 new String[]{account.name, account.type},
4084 null, null, null);
4085 try {
4086 while (cursor.moveToNext()) {
4087 final String type = cursor.getString(0);
4088 final String authToken = cursor.getString(1);
4089 authTokensForAccount.put(type, authToken);
4090 }
4091 } finally {
4092 cursor.close();
4093 }
4094 return authTokensForAccount;
4095 }
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004096
Carlos Valdivia91979be2015-05-22 14:11:35 -07004097 protected TokenCache getTokenCacheForAccountLocked(UserAccounts accounts, Account account) {
4098 WeakReference<TokenCache> cacheRef = accounts.accountTokenCaches.get(account);
4099 TokenCache cache;
4100 if (cacheRef == null || (cache = cacheRef.get()) == null) {
4101 cache = new TokenCache();
4102 cacheRef = new WeakReference<>(cache);
4103 accounts.accountTokenCaches.put(account, cacheRef);
4104 }
4105 return cache;
4106 }
4107
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004108 private Context getContextForUser(UserHandle user) {
4109 try {
4110 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4111 } catch (NameNotFoundException e) {
4112 // Default to mContext, not finding the package system is running as is unlikely.
4113 return mContext;
4114 }
4115 }
Fred Quintana60307342009-03-24 22:48:12 -07004116}