blob: 1632f92dd40cef8fcc2a8bcb968e486b4edeb197 [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;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070032import android.annotation.NonNull;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080033import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070034import android.app.ActivityManagerNative;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070035import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070036import android.app.AppOpsManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070037import android.app.Notification;
38import android.app.NotificationManager;
39import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000040import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010041import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000042import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070043import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070044import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070045import android.content.ContentValues;
46import android.content.Context;
47import android.content.Intent;
48import android.content.IntentFilter;
Fred Quintanab839afc2009-10-14 15:57:28 -070049import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080050import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070051import android.content.pm.ApplicationInfo;
52import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070053import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070054import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070055import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070056import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070057import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070058import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070059import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070060import android.database.Cursor;
61import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070062import android.database.sqlite.SQLiteDatabase;
63import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070064import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070065import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070066import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080067import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070068import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070069import android.os.IBinder;
70import android.os.Looper;
71import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070072import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070073import android.os.Process;
Fred Quintanaa698f422009-04-08 19:14:54 -070074import android.os.RemoteException;
75import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070076import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070077import android.os.UserManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070078import android.text.TextUtils;
79import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070080import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070081import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080082import android.util.SparseArray;
Fred Quintana60307342009-03-24 22:48:12 -070083
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070084import com.android.internal.R;
Amith Yamasani67df64b2012-12-14 12:09:36 -080085import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080086import com.android.internal.util.IndentingPrintWriter;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070087import com.android.server.FgThread;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000088import com.android.server.LocalServices;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070089import com.google.android.collect.Lists;
90import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070091
Oscar Montemayora8529f62009-11-18 10:14:20 -080092import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -070093import java.io.FileDescriptor;
94import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -080095import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -070096import java.security.MessageDigest;
97import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070098import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -070099import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800100import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700101import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700102import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700103import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700104import java.util.HashSet;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800105import java.util.Iterator;
Fred Quintana56285a62010-12-02 14:20:51 -0800106import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700107import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800108import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800109import java.util.Map.Entry;
110import java.util.Set;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700111import java.util.concurrent.atomic.AtomicInteger;
112import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700113
Fred Quintana60307342009-03-24 22:48:12 -0700114/**
115 * A system service that provides account, password, and authtoken management for all
116 * accounts on the device. Some of these calls are implemented with the help of the corresponding
117 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
118 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700119 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700120 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700121 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700122public class AccountManagerService
123 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800124 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Sandra Kwan78812282015-11-04 11:19:47 -0800125
Fred Quintana60307342009-03-24 22:48:12 -0700126 private static final String TAG = "AccountManagerService";
127
Fred Quintana60307342009-03-24 22:48:12 -0700128 private static final String DATABASE_NAME = "accounts.db";
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800129 private static final int DATABASE_VERSION = 9;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700130
131 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700132
133 private final Context mContext;
134
Fred Quintana56285a62010-12-02 14:20:51 -0800135 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700136 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700137 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800138
Fred Quintana60307342009-03-24 22:48:12 -0700139 private final MessageHandler mMessageHandler;
140
141 // Messages that can be sent on mHandler
142 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700143 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700144
Fred Quintana56285a62010-12-02 14:20:51 -0800145 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700146
147 private static final String TABLE_ACCOUNTS = "accounts";
148 private static final String ACCOUNTS_ID = "_id";
149 private static final String ACCOUNTS_NAME = "name";
150 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700151 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700152 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700153 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800154 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
155 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700156
157 private static final String TABLE_AUTHTOKENS = "authtokens";
158 private static final String AUTHTOKENS_ID = "_id";
159 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
160 private static final String AUTHTOKENS_TYPE = "type";
161 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
162
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700163 private static final String TABLE_GRANTS = "grants";
164 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
165 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
166 private static final String GRANTS_GRANTEE_UID = "uid";
167
Fred Quintana60307342009-03-24 22:48:12 -0700168 private static final String TABLE_EXTRAS = "extras";
169 private static final String EXTRAS_ID = "_id";
170 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
171 private static final String EXTRAS_KEY = "key";
172 private static final String EXTRAS_VALUE = "value";
173
174 private static final String TABLE_META = "meta";
175 private static final String META_KEY = "key";
176 private static final String META_VALUE = "value";
177
Amith Yamasani67df64b2012-12-14 12:09:36 -0800178 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
179
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700180 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
181 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700182 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800183
Carlos Valdivia91979be2015-05-22 14:11:35 -0700184 static {
185 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
186 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
187 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700188
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700189 private static final String COUNT_OF_MATCHING_GRANTS = ""
190 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
191 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
192 + " AND " + GRANTS_GRANTEE_UID + "=?"
193 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
194 + " AND " + ACCOUNTS_NAME + "=?"
195 + " AND " + ACCOUNTS_TYPE + "=?";
196
Fred Quintana56285a62010-12-02 14:20:51 -0800197 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
198 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700199
Fred Quintana56285a62010-12-02 14:20:51 -0800200 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
201 AUTHTOKENS_AUTHTOKEN};
202
203 private static final String SELECTION_USERDATA_BY_ACCOUNT =
204 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
205 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
206
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800207 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
208 "auth_uid_for_type:";
209 private static final String META_KEY_DELIMITER = ":";
210 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
211
Fred Quintanaa698f422009-04-08 19:14:54 -0700212 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700213 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
214
Amith Yamasani04e0d262012-02-14 11:50:53 -0800215 static class UserAccounts {
216 private final int userId;
217 private final DatabaseHelper openHelper;
218 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
219 credentialsPermissionNotificationIds =
220 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
221 private final HashMap<Account, Integer> signinRequiredNotificationIds =
222 new HashMap<Account, Integer>();
223 private final Object cacheLock = new Object();
224 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700225 private final HashMap<String, Account[]> accountCache =
226 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800227 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800228 private final HashMap<Account, HashMap<String, String>> userDataCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800229 new HashMap<Account, HashMap<String, String>>();
230 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800231 private final HashMap<Account, HashMap<String, String>> authTokenCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800232 new HashMap<Account, HashMap<String, String>>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700233
234 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700235 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700236
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700237 /**
238 * protected by the {@link #cacheLock}
239 *
240 * Caches the previous names associated with an account. Previous names
241 * should be cached because we expect that when an Account is renamed,
242 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
243 * want to know if the accounts they care about have been renamed.
244 *
245 * The previous names are wrapped in an {@link AtomicReference} so that
246 * we can distinguish between those accounts with no previous names and
247 * those whose previous names haven't been cached (yet).
248 */
249 private final HashMap<Account, AtomicReference<String>> previousNameCache =
250 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800251
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700252 private int debugDbInsertionPoint = -1;
253 private SQLiteStatement statementForLogging;
254
Amith Yamasani04e0d262012-02-14 11:50:53 -0800255 UserAccounts(Context context, int userId) {
256 this.userId = userId;
257 synchronized (cacheLock) {
258 openHelper = new DatabaseHelper(context, userId);
259 }
260 }
261 }
262
263 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
264
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700265 private static AtomicReference<AccountManagerService> sThis =
266 new AtomicReference<AccountManagerService>();
Fred Quintana31957f12009-10-21 13:43:10 -0700267 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700268
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700269 /**
270 * This should only be called by system code. One should only call this after the service
271 * has started.
272 * @return a reference to the AccountManagerService instance
273 * @hide
274 */
275 public static AccountManagerService getSingleton() {
276 return sThis.get();
277 }
Fred Quintana60307342009-03-24 22:48:12 -0700278
Fred Quintana56285a62010-12-02 14:20:51 -0800279 public AccountManagerService(Context context) {
280 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700281 }
282
Fred Quintana56285a62010-12-02 14:20:51 -0800283 public AccountManagerService(Context context, PackageManager packageManager,
284 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700285 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800286 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700287 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700288
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700289 mMessageHandler = new MessageHandler(FgThread.get().getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700290
Fred Quintana56285a62010-12-02 14:20:51 -0800291 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800292 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700293
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700294 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800295
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800296 IntentFilter intentFilter = new IntentFilter();
297 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
298 intentFilter.addDataScheme("package");
299 mContext.registerReceiver(new BroadcastReceiver() {
300 @Override
301 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700302 // Don't delete accounts when updating a authenticator's
303 // package.
304 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700305 /* Purging data requires file io, don't block the main thread. This is probably
306 * less than ideal because we are introducing a race condition where old grants
307 * could be exercised until they are purged. But that race condition existed
308 * anyway with the broadcast receiver.
309 *
310 * Ideally, we would completely clear the cache, purge data from the database,
311 * and then rebuild the cache. All under the cache lock. But that change is too
312 * large at this point.
313 */
314 Runnable r = new Runnable() {
315 @Override
316 public void run() {
317 purgeOldGrantsAll();
318 }
319 };
320 new Thread(r).start();
Carlos Valdivia23f58262014-09-05 10:52:41 -0700321 }
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800322 }
323 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800324
Amith Yamasani13593602012-03-22 16:16:17 -0700325 IntentFilter userFilter = new IntentFilter();
326 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800327 userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800328 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700329 @Override
330 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800331 String action = intent.getAction();
332 if (Intent.ACTION_USER_REMOVED.equals(action)) {
333 onUserRemoved(intent);
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800334 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
335 onUserUnlocked(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800336 }
Amith Yamasani13593602012-03-22 16:16:17 -0700337 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800338 }, UserHandle.ALL, userFilter, null, null);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800339 }
340
Dianne Hackborn164371f2013-10-01 19:10:13 -0700341 @Override
342 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
343 throws RemoteException {
344 try {
345 return super.onTransact(code, data, reply, flags);
346 } catch (RuntimeException e) {
347 // The account manager only throws security exceptions, so let's
348 // log all others.
349 if (!(e instanceof SecurityException)) {
350 Slog.wtf(TAG, "Account Manager Crash", e);
351 }
352 throw e;
353 }
354 }
355
Kenny Root26ff6622012-07-30 12:58:03 -0700356 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -0700357 }
358
Amith Yamasani258848d2012-08-10 17:06:33 -0700359 private UserManager getUserManager() {
360 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700361 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700362 }
363 return mUserManager;
364 }
365
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700366 /**
367 * Validate internal set of accounts against installed authenticators for
368 * given user. Clears cached authenticators before validating.
369 */
370 public void validateAccounts(int userId) {
371 final UserAccounts accounts = getUserAccounts(userId);
372
373 // Invalidate user-specific cache to make sure we catch any
374 // removed authenticators.
375 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
376 }
377
378 /**
379 * Validate internal set of accounts against installed authenticators for
380 * given user. Clear cached authenticators before validating when requested.
381 */
382 private void validateAccountsInternal(
383 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
384 if (invalidateAuthenticatorCache) {
385 mAuthenticatorCache.invalidateCache(accounts.userId);
386 }
387
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800388 final HashMap<String, Integer> knownAuth = new HashMap<>();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700389 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
390 mAuthenticatorCache.getAllServices(accounts.userId)) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800391 knownAuth.put(service.type.type, service.uid);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700392 }
393
Amith Yamasani04e0d262012-02-14 11:50:53 -0800394 synchronized (accounts.cacheLock) {
395 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800396 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800397
398 // Get a list of stored authenticator type and UID
399 Cursor metaCursor = db.query(
400 TABLE_META,
401 new String[] {META_KEY, META_VALUE},
402 SELECTION_META_BY_AUTHENTICATOR_TYPE,
403 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
404 null /* groupBy */,
405 null /* having */,
406 META_KEY);
407 // Create a list of authenticator type whose previous uid no longer exists
408 HashSet<String> obsoleteAuthType = Sets.newHashSet();
409 try {
410 while (metaCursor.moveToNext()) {
411 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
412 String uid = metaCursor.getString(1);
413 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uid)) {
414 // Should never happen.
415 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
416 + ", uid empty: " + TextUtils.isEmpty(uid));
417 continue;
418 }
419 Integer knownUid = knownAuth.get(type);
420 if (knownUid != null && uid.equals(knownUid.toString())) {
421 // Remove it from the knownAuth list if it's unchanged.
422 knownAuth.remove(type);
423 } else {
424 // Only add it to the list if it no longer exists or uid different
425 obsoleteAuthType.add(type);
426 // And delete it from the TABLE_META
427 db.delete(
428 TABLE_META,
429 META_KEY + "=? AND " + META_VALUE + "=?",
430 new String[] {
431 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
432 uid}
433 );
434 }
435 }
436 } finally {
437 metaCursor.close();
438 }
439
440 // Add the newly registered authenticator to TABLE_META
441 Iterator<Entry<String, Integer>> iterator = knownAuth.entrySet().iterator();
442 while (iterator.hasNext()) {
443 Entry<String, Integer> entry = iterator.next();
444 ContentValues values = new ContentValues();
445 values.put(META_KEY,
446 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
447 values.put(META_VALUE, entry.getValue());
448 db.insert(TABLE_META, null, values);
449 }
450
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800451 Cursor cursor = db.query(TABLE_ACCOUNTS,
452 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
Marvin Paul48fcd4e2014-12-01 18:26:07 -0800453 null, null, null, null, ACCOUNTS_ID);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800454 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800455 accounts.accountCache.clear();
Fred Quintana56285a62010-12-02 14:20:51 -0800456 final HashMap<String, ArrayList<String>> accountNamesByType =
Amith Yamasanib483a992012-05-22 13:14:25 -0700457 new LinkedHashMap<String, ArrayList<String>>();
Fred Quintana56285a62010-12-02 14:20:51 -0800458 while (cursor.moveToNext()) {
459 final long accountId = cursor.getLong(0);
460 final String accountType = cursor.getString(1);
461 final String accountName = cursor.getString(2);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700462
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800463 if (obsoleteAuthType.contains(accountType)) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700464 Slog.w(TAG, "deleting account " + accountName + " because type "
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800465 + accountType + "'s registered authenticator no longer exist.");
Fred Quintana56285a62010-12-02 14:20:51 -0800466 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
467 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700468
469 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
470 accountId, accounts);
471
Fred Quintana56285a62010-12-02 14:20:51 -0800472 final Account account = new Account(accountName, accountType);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800473 accounts.userDataCache.remove(account);
474 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -0700475 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -0800476 } else {
477 ArrayList<String> accountNames = accountNamesByType.get(accountType);
478 if (accountNames == null) {
479 accountNames = new ArrayList<String>();
480 accountNamesByType.put(accountType, accountNames);
481 }
482 accountNames.add(accountName);
483 }
484 }
Andy McFadden2f362292012-01-20 14:43:38 -0800485 for (Map.Entry<String, ArrayList<String>> cur
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800486 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800487 final String accountType = cur.getKey();
488 final ArrayList<String> accountNames = cur.getValue();
489 final Account[] accountsForType = new Account[accountNames.size()];
490 int i = 0;
491 for (String accountName : accountNames) {
492 accountsForType[i] = new Account(accountName, accountType);
493 ++i;
494 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800495 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800496 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800497 } finally {
498 cursor.close();
499 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800500 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800501 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800502 }
503 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700504 }
505
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800506 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
507 Context context,
508 int userId) {
509 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
510 HashMap<String, Integer> knownAuth = new HashMap<>();
511 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
512 .getAllServices(userId)) {
513 knownAuth.put(service.type.type, service.uid);
514 }
515 return knownAuth;
516 }
517
Amith Yamasani04e0d262012-02-14 11:50:53 -0800518 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700519 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -0800520 }
521
522 protected UserAccounts getUserAccounts(int userId) {
523 synchronized (mUsers) {
524 UserAccounts accounts = mUsers.get(userId);
525 if (accounts == null) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700526 accounts = new UserAccounts(mContext, userId);
527 initializeDebugDbSizeAndCompileSqlStatementForLogging(
528 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800529 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700530 purgeOldGrants(accounts);
531 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800532 }
533 return accounts;
534 }
535 }
536
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700537 private void purgeOldGrantsAll() {
538 synchronized (mUsers) {
539 for (int i = 0; i < mUsers.size(); i++) {
540 purgeOldGrants(mUsers.valueAt(i));
541 }
542 }
543 }
544
545 private void purgeOldGrants(UserAccounts accounts) {
546 synchronized (accounts.cacheLock) {
547 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
548 final Cursor cursor = db.query(TABLE_GRANTS,
549 new String[]{GRANTS_GRANTEE_UID},
550 null, null, GRANTS_GRANTEE_UID, null, null);
551 try {
552 while (cursor.moveToNext()) {
553 final int uid = cursor.getInt(0);
554 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
555 if (packageExists) {
556 continue;
557 }
558 Log.d(TAG, "deleting grants for UID " + uid
559 + " because its package is no longer installed");
560 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
561 new String[]{Integer.toString(uid)});
562 }
563 } finally {
564 cursor.close();
565 }
566 }
567 }
568
Amith Yamasani13593602012-03-22 16:16:17 -0700569 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700570 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -0700571 if (userId < 1) return;
572
573 UserAccounts accounts;
574 synchronized (mUsers) {
575 accounts = mUsers.get(userId);
576 mUsers.remove(userId);
577 }
578 if (accounts == null) {
579 File dbFile = new File(getDatabaseName(userId));
580 dbFile.delete();
581 return;
582 }
583
584 synchronized (accounts.cacheLock) {
585 accounts.openHelper.close();
586 File dbFile = new File(getDatabaseName(userId));
587 dbFile.delete();
588 }
589 }
590
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800591 private void onUserUnlocked(Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800592 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
593 if (userId < 1) return;
594
595 // Check if there's a shared account that needs to be created as an account
596 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
597 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700598 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700599 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -0700600 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700601 : UserHandle.USER_SYSTEM;
602 if (parentUserId < 0) {
603 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
604 return;
605 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800606 for (Account sa : sharedAccounts) {
607 if (ArrayUtils.contains(accounts, sa)) continue;
608 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700609 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800610 }
611 }
612
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700613 @Override
614 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700615 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -0700616 }
617
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800618 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -0700619 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700620 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800621 if (Log.isLoggable(TAG, Log.VERBOSE)) {
622 Log.v(TAG, "getPassword: " + account
623 + ", caller's uid " + Binder.getCallingUid()
624 + ", pid " + Binder.getCallingPid());
625 }
Fred Quintana382601f2010-03-25 12:25:10 -0700626 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000627 int userId = UserHandle.getCallingUserId();
628 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700629 String msg = String.format(
630 "uid %s cannot get secrets for accounts of type: %s",
631 callingUid,
632 account.type);
633 throw new SecurityException(msg);
634 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700635 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700636 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700637 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800638 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700639 } finally {
640 restoreCallingIdentity(identityToken);
641 }
642 }
643
Amith Yamasani04e0d262012-02-14 11:50:53 -0800644 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700645 if (account == null) {
646 return null;
647 }
648
Amith Yamasani04e0d262012-02-14 11:50:53 -0800649 synchronized (accounts.cacheLock) {
650 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800651 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
652 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
653 new String[]{account.name, account.type}, null, null, null);
654 try {
655 if (cursor.moveToNext()) {
656 return cursor.getString(0);
657 }
658 return null;
659 } finally {
660 cursor.close();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700661 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700662 }
663 }
664
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800665 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700666 public String getPreviousName(Account account) {
667 if (Log.isLoggable(TAG, Log.VERBOSE)) {
668 Log.v(TAG, "getPreviousName: " + account
669 + ", caller's uid " + Binder.getCallingUid()
670 + ", pid " + Binder.getCallingPid());
671 }
672 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700673 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700674 long identityToken = clearCallingIdentity();
675 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700676 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700677 return readPreviousNameInternal(accounts, account);
678 } finally {
679 restoreCallingIdentity(identityToken);
680 }
681 }
682
683 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
684 if (account == null) {
685 return null;
686 }
687 synchronized (accounts.cacheLock) {
688 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
689 if (previousNameRef == null) {
690 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
691 Cursor cursor = db.query(
692 TABLE_ACCOUNTS,
693 new String[]{ ACCOUNTS_PREVIOUS_NAME },
694 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
695 new String[] { account.name, account.type },
696 null,
697 null,
698 null);
699 try {
700 if (cursor.moveToNext()) {
701 String previousName = cursor.getString(0);
702 previousNameRef = new AtomicReference<String>(previousName);
703 accounts.previousNameCache.put(account, previousNameRef);
704 return previousName;
705 } else {
706 return null;
707 }
708 } finally {
709 cursor.close();
710 }
711 } else {
712 return previousNameRef.get();
713 }
714 }
715 }
716
717 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700718 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700719 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800720 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700721 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
722 account, key, callingUid, Binder.getCallingPid());
723 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -0800724 }
Fred Quintana382601f2010-03-25 12:25:10 -0700725 if (account == null) throw new IllegalArgumentException("account is null");
726 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000727 int userId = UserHandle.getCallingUserId();
728 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700729 String msg = String.format(
730 "uid %s cannot get user data for accounts of type: %s",
731 callingUid,
732 account.type);
733 throw new SecurityException(msg);
734 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700735 long identityToken = clearCallingIdentity();
736 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700737 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +0000738 return readUserDataInternal(accounts, account, key);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700739 } finally {
740 restoreCallingIdentity(identityToken);
741 }
742 }
743
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800744 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100745 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700746 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800747 if (Log.isLoggable(TAG, Log.VERBOSE)) {
748 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100749 + "for user id " + userId
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700750 + "caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -0800751 + ", pid " + Binder.getCallingPid());
752 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100753 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700754 if (isCrossUser(callingUid, userId)) {
755 throw new SecurityException(
756 String.format(
757 "User %s tying to get authenticator types for %s" ,
758 UserHandle.getCallingUserId(),
759 userId));
760 }
761
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700762 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700763 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000764 return getAuthenticatorTypesInternal(userId);
765
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700766 } finally {
767 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700768 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700769 }
770
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000771 /**
772 * Should only be called inside of a clearCallingIdentity block.
773 */
774 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
775 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
776 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
777 AuthenticatorDescription[] types =
778 new AuthenticatorDescription[authenticatorCollection.size()];
779 int i = 0;
780 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
781 : authenticatorCollection) {
782 types[i] = authenticator.type;
783 i++;
784 }
785 return types;
786 }
787
788
789
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700790 private boolean isCrossUser(int callingUid, int userId) {
791 return (userId != UserHandle.getCallingUserId()
792 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100793 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700794 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
795 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100796 }
797
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700798 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -0700799 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600800 Bundle.setDefusable(extras, true);
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700801 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800802 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700803 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700804 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -0800805 + ", pid " + Binder.getCallingPid());
806 }
Fred Quintana382601f2010-03-25 12:25:10 -0700807 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000808 int userId = UserHandle.getCallingUserId();
809 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700810 String msg = String.format(
811 "uid %s cannot explicitly add accounts of type: %s",
812 callingUid,
813 account.type);
814 throw new SecurityException(msg);
815 }
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700816 /*
817 * Child users are not allowed to add accounts. Only the accounts that are
818 * shared by the parent profile can be added to child profile.
819 *
820 * TODO: Only allow accounts that were shared to be added by
821 * a limited user.
822 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700823
Fred Quintana60307342009-03-24 22:48:12 -0700824 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700825 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700826 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700827 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000828 return addAccountInternal(accounts, account, password, extras, false, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -0700829 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700830 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700831 }
832 }
833
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000834 @Override
835 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700836 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700837 int callingUid = Binder.getCallingUid();
838 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
839 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000840 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700841 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800842 final UserAccounts fromAccounts = getUserAccounts(userFrom);
843 final UserAccounts toAccounts = getUserAccounts(userTo);
844 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000845 if (response != null) {
846 Bundle result = new Bundle();
847 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
848 try {
849 response.onResult(result);
850 } catch (RemoteException e) {
851 Slog.w(TAG, "Failed to report error back to the client." + e);
852 }
853 }
854 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800855 }
856
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000857 Slog.d(TAG, "Copying account " + account.name
858 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800859 long identityToken = clearCallingIdentity();
860 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000861 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800862 false /* stripAuthTokenFromResult */, account.name,
863 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700864 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800865 protected String toDebugString(long now) {
866 return super.toDebugString(now) + ", getAccountCredentialsForClone"
867 + ", " + account.type;
868 }
869
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700870 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800871 public void run() throws RemoteException {
872 mAuthenticator.getAccountCredentialsForCloning(this, account);
873 }
874
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700875 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800876 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600877 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000878 if (result != null
879 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
880 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700881 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800882 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800883 super.onResult(result);
884 }
885 }
886 }.bind();
887 } finally {
888 restoreCallingIdentity(identityToken);
889 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800890 }
891
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800892 @Override
893 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700894 final int callingUid = Binder.getCallingUid();
895 if (Log.isLoggable(TAG, Log.VERBOSE)) {
896 String msg = String.format(
897 "accountAuthenticated( account: %s, callerUid: %s)",
898 account,
899 callingUid);
900 Log.v(TAG, msg);
901 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800902 if (account == null) {
903 throw new IllegalArgumentException("account is null");
904 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000905 int userId = UserHandle.getCallingUserId();
906 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700907 String msg = String.format(
908 "uid %s cannot notify authentication for accounts of type: %s",
909 callingUid,
910 account.type);
911 throw new SecurityException(msg);
912 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000913
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000914 if (!canUserModifyAccounts(userId, callingUid) ||
915 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800916 return false;
917 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000918
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700919 long identityToken = clearCallingIdentity();
920 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000921 UserAccounts accounts = getUserAccounts(userId);
922 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700923 } finally {
924 restoreCallingIdentity(identityToken);
925 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -0700926 }
927
928 private boolean updateLastAuthenticatedTime(Account account) {
929 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800930 synchronized (accounts.cacheLock) {
931 final ContentValues values = new ContentValues();
932 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
933 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
934 int i = db.update(
935 TABLE_ACCOUNTS,
936 values,
937 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
938 new String[] {
939 account.name, account.type
940 });
941 if (i > 0) {
942 return true;
943 }
944 }
945 return false;
946 }
947
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000948 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700949 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
950 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600951 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800952 long id = clearCallingIdentity();
953 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000954 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800955 false /* stripAuthTokenFromResult */, account.name,
956 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700957 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800958 protected String toDebugString(long now) {
959 return super.toDebugString(now) + ", getAccountCredentialsForClone"
960 + ", " + account.type;
961 }
962
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700963 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800964 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700965 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700966 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700967 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -0700968 for (Account acc : getAccounts(parentUserId,
969 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700970 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000971 mAuthenticator.addAccountFromCredentials(
972 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700973 break;
974 }
975 }
976 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800977 }
978
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700979 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800980 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -0600981 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000982 // TODO: Anything to do if if succedded?
983 // TODO: If it failed: Show error notification? Should we remove the shadow
984 // account to avoid retries?
985 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800986 }
987
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700988 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800989 public void onError(int errorCode, String errorMessage) {
990 super.onError(errorCode, errorMessage);
991 // TODO: Show error notification to user
992 // TODO: Should we remove the shadow account so that it doesn't keep trying?
993 }
994
995 }.bind();
996 } finally {
997 restoreCallingIdentity(id);
998 }
999 }
1000
Amith Yamasani04e0d262012-02-14 11:50:53 -08001001 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001002 Bundle extras, boolean restricted, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001003 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001004 if (account == null) {
1005 return false;
1006 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001007 synchronized (accounts.cacheLock) {
1008 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001009 db.beginTransaction();
1010 try {
1011 long numMatches = DatabaseUtils.longForQuery(db,
1012 "select count(*) from " + TABLE_ACCOUNTS
1013 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1014 new String[]{account.name, account.type});
1015 if (numMatches > 0) {
1016 Log.w(TAG, "insertAccountIntoDatabase: " + account
1017 + ", skipping since the account already exists");
1018 return false;
1019 }
1020 ContentValues values = new ContentValues();
1021 values.put(ACCOUNTS_NAME, account.name);
1022 values.put(ACCOUNTS_TYPE, account.type);
1023 values.put(ACCOUNTS_PASSWORD, password);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001024 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001025 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
1026 if (accountId < 0) {
1027 Log.w(TAG, "insertAccountIntoDatabase: " + account
1028 + ", skipping the DB insert failed");
1029 return false;
1030 }
1031 if (extras != null) {
1032 for (String key : extras.keySet()) {
1033 final String value = extras.getString(key);
1034 if (insertExtraLocked(db, accountId, key, value) < 0) {
1035 Log.w(TAG, "insertAccountIntoDatabase: " + account
1036 + ", skipping since insertExtra failed for key " + key);
1037 return false;
1038 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001039 }
1040 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001041 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001042
1043 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1044 accounts, callingUid);
1045
Amith Yamasani04e0d262012-02-14 11:50:53 -08001046 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001047 } finally {
1048 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001049 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001050 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001051 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001052 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1053 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001054 }
1055 return true;
1056 }
1057
1058 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001059 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001060 * running, then clone the account too.
1061 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001062 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001063 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001064 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001065 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001066 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001067 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001068 addSharedAccountAsUser(account, user.id);
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001069 if (getUserManager().isUserUnlocked(user.id)) {
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001070 mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
1071 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001072 }
1073 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001074 }
1075 }
1076
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001077 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -07001078 ContentValues values = new ContentValues();
1079 values.put(EXTRAS_KEY, key);
1080 values.put(EXTRAS_ACCOUNTS_ID, accountId);
1081 values.put(EXTRAS_VALUE, value);
1082 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
1083 }
1084
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001085 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001086 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001087 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001088 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001089 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1090 Log.v(TAG, "hasFeatures: " + account
1091 + ", response " + response
1092 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001093 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001094 + ", pid " + Binder.getCallingPid());
1095 }
Fred Quintana382601f2010-03-25 12:25:10 -07001096 if (response == null) throw new IllegalArgumentException("response is null");
1097 if (account == null) throw new IllegalArgumentException("account is null");
1098 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001099 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001100 checkReadAccountsPermitted(callingUid, account.type, userId,
1101 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001102
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001103 long identityToken = clearCallingIdentity();
1104 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001105 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001106 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001107 } finally {
1108 restoreCallingIdentity(identityToken);
1109 }
1110 }
1111
1112 private class TestFeaturesSession extends Session {
1113 private final String[] mFeatures;
1114 private final Account mAccount;
1115
Amith Yamasani04e0d262012-02-14 11:50:53 -08001116 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001117 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001118 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001119 true /* stripAuthTokenFromResult */, account.name,
1120 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001121 mFeatures = features;
1122 mAccount = account;
1123 }
1124
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001125 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001126 public void run() throws RemoteException {
1127 try {
1128 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1129 } catch (RemoteException e) {
1130 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1131 }
1132 }
1133
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001134 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001135 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001136 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001137 IAccountManagerResponse response = getResponseAndClose();
1138 if (response != null) {
1139 try {
1140 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001141 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001142 return;
1143 }
Fred Quintana56285a62010-12-02 14:20:51 -08001144 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1145 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1146 + response);
1147 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001148 final Bundle newResult = new Bundle();
1149 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1150 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1151 response.onResult(newResult);
1152 } catch (RemoteException e) {
1153 // if the caller is dead then there is no one to care about remote exceptions
1154 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1155 Log.v(TAG, "failure while notifying response", e);
1156 }
1157 }
1158 }
1159 }
1160
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001161 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001162 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001163 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001164 + ", " + mAccount
1165 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1166 }
1167 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001168
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001169 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001170 public void renameAccount(
1171 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001172 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001173 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1174 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001175 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001176 + ", pid " + Binder.getCallingPid());
1177 }
1178 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001179 int userId = UserHandle.getCallingUserId();
1180 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001181 String msg = String.format(
1182 "uid %s cannot rename accounts of type: %s",
1183 callingUid,
1184 accountToRename.type);
1185 throw new SecurityException(msg);
1186 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001187 long identityToken = clearCallingIdentity();
1188 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001189 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001190 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001191 Bundle result = new Bundle();
1192 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1193 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1194 try {
1195 response.onResult(result);
1196 } catch (RemoteException e) {
1197 Log.w(TAG, e.getMessage());
1198 }
1199 } finally {
1200 restoreCallingIdentity(identityToken);
1201 }
1202 }
1203
1204 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001205 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001206 Account resultAccount = null;
1207 /*
1208 * Cancel existing notifications. Let authenticators
1209 * re-post notifications as required. But we don't know if
1210 * the authenticators have bound their notifications to
1211 * now stale account name data.
1212 *
1213 * With a rename api, we might not need to do this anymore but it
1214 * shouldn't hurt.
1215 */
1216 cancelNotification(
1217 getSigninRequiredNotificationId(accounts, accountToRename),
1218 new UserHandle(accounts.userId));
1219 synchronized(accounts.credentialsPermissionNotificationIds) {
1220 for (Pair<Pair<Account, String>, Integer> pair:
1221 accounts.credentialsPermissionNotificationIds.keySet()) {
1222 if (accountToRename.equals(pair.first.first)) {
1223 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1224 cancelNotification(id, new UserHandle(accounts.userId));
1225 }
1226 }
1227 }
1228 synchronized (accounts.cacheLock) {
1229 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1230 db.beginTransaction();
1231 boolean isSuccessful = false;
1232 Account renamedAccount = new Account(newName, accountToRename.type);
1233 try {
1234 final ContentValues values = new ContentValues();
1235 values.put(ACCOUNTS_NAME, newName);
1236 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1237 final long accountId = getAccountIdLocked(db, accountToRename);
1238 if (accountId >= 0) {
1239 final String[] argsAccountId = { String.valueOf(accountId) };
1240 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1241 db.setTransactionSuccessful();
1242 isSuccessful = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001243 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1244 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001245 }
1246 } finally {
1247 db.endTransaction();
1248 if (isSuccessful) {
1249 /*
1250 * Database transaction was successful. Clean up cached
1251 * data associated with the account in the user profile.
1252 */
1253 insertAccountIntoCacheLocked(accounts, renamedAccount);
1254 /*
1255 * Extract the data and token caches before removing the
1256 * old account to preserve the user data associated with
1257 * the account.
1258 */
1259 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1260 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1261 removeAccountFromCacheLocked(accounts, accountToRename);
1262 /*
1263 * Update the cached data associated with the renamed
1264 * account.
1265 */
1266 accounts.userDataCache.put(renamedAccount, tmpData);
1267 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1268 accounts.previousNameCache.put(
1269 renamedAccount,
1270 new AtomicReference<String>(accountToRename.name));
1271 resultAccount = renamedAccount;
1272
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001273 int parentUserId = accounts.userId;
1274 if (canHaveProfile(parentUserId)) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001275 /*
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001276 * Owner or system user account was renamed, rename the account for
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001277 * those users with which the account was shared.
1278 */
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001279 List<UserInfo> users = getUserManager().getUsers(true);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001280 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001281 if (user.isRestricted()
1282 && (user.restrictedProfileParentId == parentUserId)) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001283 renameSharedAccountAsUser(accountToRename, newName, user.id);
1284 }
1285 }
1286 }
1287 sendAccountsChangedBroadcast(accounts.userId);
1288 }
1289 }
1290 }
1291 return resultAccount;
1292 }
1293
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001294 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001295 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001296 return userInfo != null && userInfo.canHaveProfile();
1297 }
1298
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001299 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001300 public void removeAccount(IAccountManagerResponse response, Account account,
1301 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001302 removeAccountAsUser(
1303 response,
1304 account,
1305 expectActivityLaunch,
1306 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001307 }
1308
1309 @Override
1310 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001311 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001312 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001313 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1314 Log.v(TAG, "removeAccount: " + account
1315 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001316 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001317 + ", pid " + Binder.getCallingPid()
1318 + ", for user id " + userId);
1319 }
1320 if (response == null) throw new IllegalArgumentException("response is null");
1321 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001322 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001323 if (isCrossUser(callingUid, userId)) {
1324 throw new SecurityException(
1325 String.format(
1326 "User %s tying remove account for %s" ,
1327 UserHandle.getCallingUserId(),
1328 userId));
1329 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001330 /*
1331 * Only the system or authenticator should be allowed to remove accounts for that
1332 * authenticator. This will let users remove accounts (via Settings in the system) but not
1333 * arbitrary applications (like competing authenticators).
1334 */
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001335 UserHandle user = new UserHandle(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00001336 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1337 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001338 String msg = String.format(
1339 "uid %s cannot remove accounts of type: %s",
1340 callingUid,
1341 account.type);
1342 throw new SecurityException(msg);
1343 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001344 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001345 try {
1346 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1347 "User cannot modify accounts");
1348 } catch (RemoteException re) {
1349 }
1350 return;
1351 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001352 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001353 try {
1354 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1355 "User cannot modify accounts of this type (policy).");
1356 } catch (RemoteException re) {
1357 }
1358 return;
1359 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001360 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001361 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001362 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001363 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001364 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08001365 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001366 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001367 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001368 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001369 }
1370 }
1371 }
1372
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001373 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1374
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001375 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001376 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1377 } finally {
1378 restoreCallingIdentity(identityToken);
1379 }
1380 }
1381
1382 @Override
1383 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001384 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001385 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1386 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001387 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001388 + ", pid " + Binder.getCallingPid());
1389 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001390 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001391 if (account == null) {
1392 /*
1393 * Null accounts should result in returning false, as per
1394 * AccountManage.addAccountExplicitly(...) java doc.
1395 */
1396 Log.e(TAG, "account is null");
1397 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001398 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001399 String msg = String.format(
1400 "uid %s cannot explicitly add accounts of type: %s",
1401 callingUid,
1402 account.type);
1403 throw new SecurityException(msg);
1404 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001405 UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001406 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001407 long identityToken = clearCallingIdentity();
1408 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001409 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001410 } finally {
1411 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001412 }
Fred Quintana60307342009-03-24 22:48:12 -07001413 }
1414
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001415 private class RemoveAccountSession extends Session {
1416 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001417 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001418 Account account, boolean expectActivityLaunch) {
1419 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001420 true /* stripAuthTokenFromResult */, account.name,
1421 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001422 mAccount = account;
1423 }
1424
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001425 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001426 protected String toDebugString(long now) {
1427 return super.toDebugString(now) + ", removeAccount"
1428 + ", account " + mAccount;
1429 }
1430
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001431 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001432 public void run() throws RemoteException {
1433 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1434 }
1435
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001436 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001437 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001438 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001439 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1440 && !result.containsKey(AccountManager.KEY_INTENT)) {
1441 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001442 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001443 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001444 }
1445 IAccountManagerResponse response = getResponseAndClose();
1446 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001447 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1448 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1449 + response);
1450 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001451 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001452 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001453 try {
1454 response.onResult(result2);
1455 } catch (RemoteException e) {
1456 // ignore
1457 }
1458 }
1459 }
1460 super.onResult(result);
1461 }
1462 }
1463
Amith Yamasani04e0d262012-02-14 11:50:53 -08001464 /* For testing */
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001465 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001466 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001467 }
1468
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001469 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001470 int deleted;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001471 synchronized (accounts.cacheLock) {
1472 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001473 final long accountId = getAccountIdLocked(db, account);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001474 deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
1475 + "=?",
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001476 new String[]{account.name, account.type});
Amith Yamasani04e0d262012-02-14 11:50:53 -08001477 removeAccountFromCacheLocked(accounts, account);
1478 sendAccountsChangedBroadcast(accounts.userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001479
1480 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001481 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001482 long id = Binder.clearCallingIdentity();
1483 try {
1484 int parentUserId = accounts.userId;
1485 if (canHaveProfile(parentUserId)) {
1486 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001487 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001488 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001489 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001490 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001491 }
1492 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001493 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001494 } finally {
1495 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001496 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001497 return (deleted > 0);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001498 }
1499
Maggie Benthalla12fccf2013-03-14 18:02:12 -04001500 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001501 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07001502 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001503 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1504 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07001505 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08001506 + ", pid " + Binder.getCallingPid());
1507 }
Fred Quintana382601f2010-03-25 12:25:10 -07001508 if (accountType == null) throw new IllegalArgumentException("accountType is null");
1509 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001510 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001511 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001512 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001513 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001514 synchronized (accounts.cacheLock) {
1515 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001516 db.beginTransaction();
1517 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001518 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001519 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001520 db.setTransactionSuccessful();
1521 } finally {
1522 db.endTransaction();
1523 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001524 }
Fred Quintana60307342009-03-24 22:48:12 -07001525 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001526 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001527 }
1528 }
1529
Carlos Valdivia91979be2015-05-22 14:11:35 -07001530 private void invalidateCustomTokenLocked(
1531 UserAccounts accounts,
1532 String accountType,
1533 String authToken) {
1534 if (authToken == null || accountType == null) {
1535 return;
1536 }
1537 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001538 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001539 }
1540
Amith Yamasani04e0d262012-02-14 11:50:53 -08001541 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1542 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001543 if (authToken == null || accountType == null) {
1544 return;
1545 }
Fred Quintana33269202009-04-20 16:05:10 -07001546 Cursor cursor = db.rawQuery(
1547 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1548 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1549 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1550 + " FROM " + TABLE_ACCOUNTS
1551 + " JOIN " + TABLE_AUTHTOKENS
1552 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1553 + " = " + AUTHTOKENS_ACCOUNTS_ID
1554 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
1555 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1556 new String[]{authToken, accountType});
1557 try {
1558 while (cursor.moveToNext()) {
1559 long authTokenId = cursor.getLong(0);
1560 String accountName = cursor.getString(1);
1561 String authTokenType = cursor.getString(2);
1562 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001563 writeAuthTokenIntoCacheLocked(
1564 accounts,
1565 db,
1566 new Account(accountName, accountType),
1567 authTokenType,
1568 null);
Fred Quintana60307342009-03-24 22:48:12 -07001569 }
Fred Quintana33269202009-04-20 16:05:10 -07001570 } finally {
1571 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07001572 }
1573 }
1574
Carlos Valdivia91979be2015-05-22 14:11:35 -07001575 private void saveCachedToken(
1576 UserAccounts accounts,
1577 Account account,
1578 String callerPkg,
1579 byte[] callerSigDigest,
1580 String tokenType,
1581 String token,
1582 long expiryMillis) {
1583
1584 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1585 return;
1586 }
1587 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1588 new UserHandle(accounts.userId));
1589 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001590 accounts.accountTokenCaches.put(
1591 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001592 }
1593 }
1594
Amith Yamasani04e0d262012-02-14 11:50:53 -08001595 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1596 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07001597 if (account == null || type == null) {
1598 return false;
1599 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001600 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1601 new UserHandle(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001602 synchronized (accounts.cacheLock) {
1603 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001604 db.beginTransaction();
1605 try {
1606 long accountId = getAccountIdLocked(db, account);
1607 if (accountId < 0) {
1608 return false;
1609 }
1610 db.delete(TABLE_AUTHTOKENS,
1611 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1612 new String[]{type});
1613 ContentValues values = new ContentValues();
1614 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1615 values.put(AUTHTOKENS_TYPE, type);
1616 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1617 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1618 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001619 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001620 return true;
1621 }
Fred Quintana33269202009-04-20 16:05:10 -07001622 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001623 } finally {
1624 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07001625 }
Fred Quintana60307342009-03-24 22:48:12 -07001626 }
1627 }
1628
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001629 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001630 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001631 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001632 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1633 Log.v(TAG, "peekAuthToken: " + account
1634 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001635 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001636 + ", pid " + Binder.getCallingPid());
1637 }
Fred Quintana382601f2010-03-25 12:25:10 -07001638 if (account == null) throw new IllegalArgumentException("account is null");
1639 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001640 int userId = UserHandle.getCallingUserId();
1641 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001642 String msg = String.format(
1643 "uid %s cannot peek the authtokens associated with accounts of type: %s",
1644 callingUid,
1645 account.type);
1646 throw new SecurityException(msg);
1647 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001648 long identityToken = clearCallingIdentity();
1649 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001650 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001651 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001652 } finally {
1653 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001654 }
Fred Quintana60307342009-03-24 22:48:12 -07001655 }
1656
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001657 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001658 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001659 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001660 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1661 Log.v(TAG, "setAuthToken: " + account
1662 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001663 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001664 + ", pid " + Binder.getCallingPid());
1665 }
Fred Quintana382601f2010-03-25 12:25:10 -07001666 if (account == null) throw new IllegalArgumentException("account is null");
1667 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001668 int userId = UserHandle.getCallingUserId();
1669 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001670 String msg = String.format(
1671 "uid %s cannot set auth tokens associated with accounts of type: %s",
1672 callingUid,
1673 account.type);
1674 throw new SecurityException(msg);
1675 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001676 long identityToken = clearCallingIdentity();
1677 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001678 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001679 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001680 } finally {
1681 restoreCallingIdentity(identityToken);
1682 }
Fred Quintana60307342009-03-24 22:48:12 -07001683 }
1684
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001685 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001686 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001687 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001688 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1689 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001690 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001691 + ", pid " + Binder.getCallingPid());
1692 }
Fred Quintana382601f2010-03-25 12:25:10 -07001693 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001694 int userId = UserHandle.getCallingUserId();
1695 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001696 String msg = String.format(
1697 "uid %s cannot set secrets for accounts of type: %s",
1698 callingUid,
1699 account.type);
1700 throw new SecurityException(msg);
1701 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001702 long identityToken = clearCallingIdentity();
1703 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001704 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001705 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001706 } finally {
1707 restoreCallingIdentity(identityToken);
1708 }
Fred Quintana60307342009-03-24 22:48:12 -07001709 }
1710
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001711 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1712 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07001713 if (account == null) {
1714 return;
1715 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001716 synchronized (accounts.cacheLock) {
1717 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001718 db.beginTransaction();
1719 try {
1720 final ContentValues values = new ContentValues();
1721 values.put(ACCOUNTS_PASSWORD, password);
1722 final long accountId = getAccountIdLocked(db, account);
1723 if (accountId >= 0) {
1724 final String[] argsAccountId = {String.valueOf(accountId)};
1725 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1726 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001727 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001728 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001729 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001730
1731 String action = (password == null || password.length() == 0) ?
1732 DebugDbHelper.ACTION_CLEAR_PASSWORD
1733 : DebugDbHelper.ACTION_SET_PASSWORD;
1734 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08001735 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001736 } finally {
1737 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001738 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001739 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001740 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001741 }
1742
Amith Yamasani04e0d262012-02-14 11:50:53 -08001743 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08001744 Log.i(TAG, "the accounts changed, sending broadcast of "
1745 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001746 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07001747 }
1748
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001749 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001750 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001751 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001752 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1753 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001754 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001755 + ", pid " + Binder.getCallingPid());
1756 }
Fred Quintana382601f2010-03-25 12:25:10 -07001757 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001758 int userId = UserHandle.getCallingUserId();
1759 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001760 String msg = String.format(
1761 "uid %s cannot clear passwords for accounts of type: %s",
1762 callingUid,
1763 account.type);
1764 throw new SecurityException(msg);
1765 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001766 long identityToken = clearCallingIdentity();
1767 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001768 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001769 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001770 } finally {
1771 restoreCallingIdentity(identityToken);
1772 }
Fred Quintana60307342009-03-24 22:48:12 -07001773 }
1774
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001775 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001776 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001777 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001778 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1779 Log.v(TAG, "setUserData: " + account
1780 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001781 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001782 + ", pid " + Binder.getCallingPid());
1783 }
Fred Quintana382601f2010-03-25 12:25:10 -07001784 if (key == null) throw new IllegalArgumentException("key is null");
1785 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001786 int userId = UserHandle.getCallingUserId();
1787 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001788 String msg = String.format(
1789 "uid %s cannot set user data for accounts of type: %s",
1790 callingUid,
1791 account.type);
1792 throw new SecurityException(msg);
1793 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001794 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001795 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001796 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001797 setUserdataInternal(accounts, account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -07001798 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001799 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001800 }
1801 }
1802
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001803 private void setUserdataInternal(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08001804 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07001805 if (account == null || key == null) {
1806 return;
1807 }
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001808 synchronized (accounts.cacheLock) {
1809 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1810 db.beginTransaction();
1811 try {
1812 long accountId = getAccountIdLocked(db, account);
1813 if (accountId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001814 return;
1815 }
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001816 long extrasId = getExtrasIdLocked(db, accountId, key);
1817 if (extrasId < 0 ) {
1818 extrasId = insertExtraLocked(db, accountId, key, value);
1819 if (extrasId < 0) {
1820 return;
1821 }
1822 } else {
1823 ContentValues values = new ContentValues();
1824 values.put(EXTRAS_VALUE, value);
1825 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1826 return;
1827 }
Simranjit Singh Kohlia666d742015-08-07 13:34:09 -07001828
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001829 }
1830 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
1831 db.setTransactionSuccessful();
1832 } finally {
1833 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001834 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001835 }
1836 }
1837
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001838 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08001839 if (result == null) {
1840 Log.e(TAG, "the result is unexpectedly null", new Exception());
1841 }
1842 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1843 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1844 + response);
1845 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001846 try {
1847 response.onResult(result);
1848 } catch (RemoteException e) {
1849 // if the caller is dead then there is no one to care about remote
1850 // exceptions
1851 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1852 Log.v(TAG, "failure while notifying response", e);
1853 }
1854 }
1855 }
1856
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001857 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07001858 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1859 final String authTokenType)
1860 throws RemoteException {
1861 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08001862 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1863
Fred Quintanad9640ec2012-05-23 12:37:00 -07001864 final int callingUid = getCallingUid();
1865 clearCallingIdentity();
Amith Yamasani27db4682013-03-30 17:07:47 -07001866 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07001867 throw new SecurityException("can only call from system");
1868 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001869 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001870 long identityToken = clearCallingIdentity();
1871 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001872 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001873 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
1874 false /* stripAuthTokenFromResult */, null /* accountName */,
1875 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001876 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001877 protected String toDebugString(long now) {
1878 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07001879 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08001880 + ", authTokenType " + authTokenType;
1881 }
1882
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001883 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001884 public void run() throws RemoteException {
1885 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1886 }
1887
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001888 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001889 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001890 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001891 if (result != null) {
1892 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1893 Bundle bundle = new Bundle();
1894 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1895 super.onResult(bundle);
1896 return;
1897 } else {
1898 super.onResult(result);
1899 }
1900 }
1901 }.bind();
1902 } finally {
1903 restoreCallingIdentity(identityToken);
1904 }
1905 }
1906
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001907 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07001908 public void getAuthToken(
1909 IAccountManagerResponse response,
1910 final Account account,
1911 final String authTokenType,
1912 final boolean notifyOnAuthFailure,
1913 final boolean expectActivityLaunch,
1914 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001915 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08001916 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1917 Log.v(TAG, "getAuthToken: " + account
1918 + ", response " + response
1919 + ", authTokenType " + authTokenType
1920 + ", notifyOnAuthFailure " + notifyOnAuthFailure
1921 + ", expectActivityLaunch " + expectActivityLaunch
1922 + ", caller's uid " + Binder.getCallingUid()
1923 + ", pid " + Binder.getCallingPid());
1924 }
Fred Quintana382601f2010-03-25 12:25:10 -07001925 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001926 try {
1927 if (account == null) {
1928 Slog.w(TAG, "getAuthToken called with null account");
1929 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
1930 return;
1931 }
1932 if (authTokenType == null) {
1933 Slog.w(TAG, "getAuthToken called with null authTokenType");
1934 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
1935 return;
1936 }
1937 } catch (RemoteException e) {
1938 Slog.w(TAG, "Failed to report error back to the client." + e);
1939 return;
1940 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001941 int userId = UserHandle.getCallingUserId();
1942 long ident = Binder.clearCallingIdentity();
1943 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001944 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001945 try {
1946 accounts = getUserAccounts(userId);
1947 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1948 AuthenticatorDescription.newKey(account.type), accounts.userId);
1949 } finally {
1950 Binder.restoreCallingIdentity(ident);
1951 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001952
Costin Manolachea40c6302010-12-13 14:50:45 -08001953 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07001954 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08001955
1956 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001957 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001958 final boolean permissionGranted =
1959 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08001960
Carlos Valdivia91979be2015-05-22 14:11:35 -07001961 // Get the calling package. We will use it for the purpose of caching.
1962 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07001963 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001964 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07001965 try {
1966 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
1967 } finally {
1968 Binder.restoreCallingIdentity(ident);
1969 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001970 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
1971 String msg = String.format(
1972 "Uid %s is attempting to illegally masquerade as package %s!",
1973 callerUid,
1974 callerPkg);
1975 throw new SecurityException(msg);
1976 }
1977
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001978 // let authenticator know the identity of the caller
1979 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1980 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07001981
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001982 if (notifyOnAuthFailure) {
1983 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08001984 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001985
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001986 long identityToken = clearCallingIdentity();
1987 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07001988 // Distill the caller's package signatures into a single digest.
1989 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
1990
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001991 // if the caller has permission, do the peek. otherwise go the more expensive
1992 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08001993 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001994 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001995 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001996 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001997 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1998 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1999 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002000 onResult(response, result);
2001 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002002 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002003 }
2004
Carlos Valdivia91979be2015-05-22 14:11:35 -07002005 if (customTokens) {
2006 /*
2007 * Look up tokens in the new cache only if the loginOptions don't have parameters
2008 * outside of those expected to be injected by the AccountManager, e.g.
2009 * ANDORID_PACKAGE_NAME.
2010 */
2011 String token = readCachedTokenInternal(
2012 accounts,
2013 account,
2014 authTokenType,
2015 callerPkg,
2016 callerPkgSigDigest);
2017 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002018 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2019 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2020 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002021 Bundle result = new Bundle();
2022 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2023 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2024 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2025 onResult(response, result);
2026 return;
2027 }
2028 }
2029
Amith Yamasani04e0d262012-02-14 11:50:53 -08002030 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002031 false /* stripAuthTokenFromResult */, account.name,
2032 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002033 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002034 protected String toDebugString(long now) {
2035 if (loginOptions != null) loginOptions.keySet();
2036 return super.toDebugString(now) + ", getAuthToken"
2037 + ", " + account
2038 + ", authTokenType " + authTokenType
2039 + ", loginOptions " + loginOptions
2040 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2041 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002042
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002043 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002044 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002045 // If the caller doesn't have permission then create and return the
2046 // "grant permission" intent instead of the "getAuthToken" intent.
2047 if (!permissionGranted) {
2048 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2049 } else {
2050 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2051 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002052 }
2053
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002054 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002055 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002056 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002057 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002058 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002059 Intent intent = newGrantCredentialsPermissionIntent(
2060 account,
2061 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002062 new AccountAuthenticatorResponse(this),
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002063 authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002064 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002065 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002066 onResult(bundle);
2067 return;
2068 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002069 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002070 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002071 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2072 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002073 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002074 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002075 "the type and name should not be empty");
2076 return;
2077 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002078 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002079 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002080 saveAuthTokenToDatabase(
2081 mAccounts,
2082 resultAccount,
2083 authTokenType,
2084 authToken);
2085 }
2086 long expiryMillis = result.getLong(
2087 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2088 if (customTokens
2089 && expiryMillis > System.currentTimeMillis()) {
2090 saveCachedToken(
2091 mAccounts,
2092 account,
2093 callerPkg,
2094 callerPkgSigDigest,
2095 authTokenType,
2096 authToken,
2097 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002098 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002099 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002100
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002101 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002102 if (intent != null && notifyOnAuthFailure && !customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002103 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002104 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Dianne Hackborn41203752012-08-31 14:05:51 -07002105 intent, accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002106 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002107 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002108 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002109 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002110 }.bind();
2111 } finally {
2112 restoreCallingIdentity(identityToken);
2113 }
Fred Quintana60307342009-03-24 22:48:12 -07002114 }
2115
Carlos Valdivia91979be2015-05-22 14:11:35 -07002116 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2117 MessageDigest digester;
2118 try {
2119 digester = MessageDigest.getInstance("SHA-256");
2120 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2121 callerPkg, PackageManager.GET_SIGNATURES);
2122 for (Signature sig : pkgInfo.signatures) {
2123 digester.update(sig.toByteArray());
2124 }
2125 } catch (NoSuchAlgorithmException x) {
2126 Log.wtf(TAG, "SHA-256 should be available", x);
2127 digester = null;
2128 } catch (NameNotFoundException e) {
2129 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2130 digester = null;
2131 }
2132 return (digester == null) ? null : digester.digest();
2133 }
2134
Dianne Hackborn41203752012-08-31 14:05:51 -07002135 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
2136 int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002137 int uid = intent.getIntExtra(
2138 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2139 String authTokenType = intent.getStringExtra(
2140 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002141 final String titleAndSubtitle =
2142 mContext.getString(R.string.permission_request_notification_with_subtitle,
2143 account.name);
2144 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07002145 String title = titleAndSubtitle;
2146 String subtitle = "";
2147 if (index > 0) {
2148 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002149 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07002150 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002151 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01002152 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002153 Notification n = new Notification.Builder(contextForUser)
2154 .setSmallIcon(android.R.drawable.stat_sys_warning)
2155 .setWhen(0)
2156 .setColor(contextForUser.getColor(
2157 com.android.internal.R.color.system_notification_accent_color))
2158 .setContentTitle(title)
2159 .setContentText(subtitle)
2160 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2161 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2162 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002163 installNotification(getCredentialPermissionNotificationId(
2164 account, authTokenType, uid), n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002165 }
2166
Costin Manolache5f383ad92010-12-02 16:44:46 -08002167 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002168 AccountAuthenticatorResponse response, String authTokenType) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002169
2170 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07002171 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08002172 // Since it was set in Eclair+ we can't change it without breaking apps using
2173 // the intent from a non-Activity context.
2174 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002175 intent.addCategory(
2176 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08002177
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002178 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002179 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2180 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002181 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002182
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002183 return intent;
2184 }
2185
2186 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2187 int uid) {
2188 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002189 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002190 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002191 final Pair<Pair<Account, String>, Integer> key =
2192 new Pair<Pair<Account, String>, Integer>(
2193 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002194 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002195 if (id == null) {
2196 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002197 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002198 }
2199 }
2200 return id;
2201 }
2202
Amith Yamasani04e0d262012-02-14 11:50:53 -08002203 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002204 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002205 synchronized (accounts.signinRequiredNotificationIds) {
2206 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002207 if (id == null) {
2208 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002209 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002210 }
2211 }
2212 return id;
2213 }
2214
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002215 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07002216 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07002217 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002218 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002219 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002220 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2221 Log.v(TAG, "addAccount: accountType " + accountType
2222 + ", response " + response
2223 + ", authTokenType " + authTokenType
2224 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2225 + ", expectActivityLaunch " + expectActivityLaunch
2226 + ", caller's uid " + Binder.getCallingUid()
2227 + ", pid " + Binder.getCallingPid());
2228 }
Fred Quintana382601f2010-03-25 12:25:10 -07002229 if (response == null) throw new IllegalArgumentException("response is null");
2230 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002231
Amith Yamasani71e6c692013-03-24 17:39:28 -07002232 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002233 final int uid = Binder.getCallingUid();
2234 final int userId = UserHandle.getUserId(uid);
2235 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002236 try {
2237 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2238 "User is not allowed to add an account!");
2239 } catch (RemoteException re) {
2240 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002241 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002242 return;
2243 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002244 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002245 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002246 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2247 "User cannot modify accounts of this type (policy).");
2248 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002249 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002250 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2251 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002252 return;
2253 }
2254
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002255 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002256 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2257 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2258 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2259
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002260 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002261 long identityToken = clearCallingIdentity();
2262 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002263 UserAccounts accounts = getUserAccounts(usrId);
2264 logRecordWithUid(
2265 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002266 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002267 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002268 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002269 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002270 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07002271 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07002272 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002273 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002274
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002275 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002276 protected String toDebugString(long now) {
2277 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07002278 + ", accountType " + accountType
2279 + ", requiredFeatures "
2280 + (requiredFeatures != null
2281 ? TextUtils.join(",", requiredFeatures)
2282 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002283 }
2284 }.bind();
2285 } finally {
2286 restoreCallingIdentity(identityToken);
2287 }
Fred Quintana60307342009-03-24 22:48:12 -07002288 }
2289
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002290 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002291 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2292 final String authTokenType, final String[] requiredFeatures,
2293 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002294 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002295 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002296 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2297 Log.v(TAG, "addAccount: accountType " + accountType
2298 + ", response " + response
2299 + ", authTokenType " + authTokenType
2300 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2301 + ", expectActivityLaunch " + expectActivityLaunch
2302 + ", caller's uid " + Binder.getCallingUid()
2303 + ", pid " + Binder.getCallingPid()
2304 + ", for user id " + userId);
2305 }
2306 if (response == null) throw new IllegalArgumentException("response is null");
2307 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002308 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002309 if (isCrossUser(callingUid, userId)) {
2310 throw new SecurityException(
2311 String.format(
2312 "User %s trying to add account for %s" ,
2313 UserHandle.getCallingUserId(),
2314 userId));
2315 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002316
2317 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002318 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002319 try {
2320 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2321 "User is not allowed to add an account!");
2322 } catch (RemoteException re) {
2323 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002324 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002325 return;
2326 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002327 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002328 try {
2329 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2330 "User cannot modify accounts of this type (policy).");
2331 } catch (RemoteException re) {
2332 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002333 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2334 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002335 return;
2336 }
2337
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002338 final int pid = Binder.getCallingPid();
2339 final int uid = Binder.getCallingUid();
2340 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2341 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2342 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2343
2344 long identityToken = clearCallingIdentity();
2345 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002346 UserAccounts accounts = getUserAccounts(userId);
2347 logRecordWithUid(
2348 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002349 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002350 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002351 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002352 @Override
2353 public void run() throws RemoteException {
2354 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2355 options);
2356 }
2357
2358 @Override
2359 protected String toDebugString(long now) {
2360 return super.toDebugString(now) + ", addAccount"
2361 + ", accountType " + accountType
2362 + ", requiredFeatures "
2363 + (requiredFeatures != null
2364 ? TextUtils.join(",", requiredFeatures)
2365 : null);
2366 }
2367 }.bind();
2368 } finally {
2369 restoreCallingIdentity(identityToken);
2370 }
2371 }
2372
Sandra Kwan78812282015-11-04 11:19:47 -08002373 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08002374 public void startAddAccountSession(
2375 final IAccountManagerResponse response,
2376 final String accountType,
2377 final String authTokenType,
2378 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08002379 final boolean expectActivityLaunch,
2380 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002381 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08002382 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2383 Log.v(TAG,
2384 "startAddAccountSession: accountType " + accountType
2385 + ", response " + response
2386 + ", authTokenType " + authTokenType
2387 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2388 + ", expectActivityLaunch " + expectActivityLaunch
2389 + ", caller's uid " + Binder.getCallingUid()
2390 + ", pid " + Binder.getCallingPid());
2391 }
2392 if (response == null) {
2393 throw new IllegalArgumentException("response is null");
2394 }
2395 if (accountType == null) {
2396 throw new IllegalArgumentException("accountType is null");
2397 }
2398
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002399 final int uid = Binder.getCallingUid();
Sandra Kwana578d112015-12-16 16:01:43 -08002400 // Only allow system to start session
2401 if (!isSystemUid(uid)) {
2402 String msg = String.format(
2403 "uid %s cannot stat add account session.",
2404 uid);
2405 throw new SecurityException(msg);
2406 }
2407
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002408 final int userId = UserHandle.getUserId(uid);
2409 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002410 try {
2411 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2412 "User is not allowed to add an account!");
2413 } catch (RemoteException re) {
2414 }
2415 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2416 return;
2417 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002418 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002419 try {
2420 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2421 "User cannot modify accounts of this type (policy).");
2422 } catch (RemoteException re) {
2423 }
2424 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2425 userId);
2426 return;
2427 }
2428
2429 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08002430 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2431 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2432 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2433
2434 int usrId = UserHandle.getCallingUserId();
2435 long identityToken = clearCallingIdentity();
2436 try {
2437 UserAccounts accounts = getUserAccounts(usrId);
2438 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2439 TABLE_ACCOUNTS, uid);
2440 new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
2441 null /* accountName */, false /* authDetailsRequired */,
2442 true /* updateLastAuthenticationTime */) {
2443 @Override
2444 public void run() throws RemoteException {
2445 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2446 requiredFeatures, options);
2447 }
2448
2449 @Override
2450 protected String toDebugString(long now) {
2451 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2452 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2453 + accountType + ", requiredFeatures "
2454 + (requiredFeatures != null ? requiredFeaturesStr : null);
2455 }
2456 }.bind();
2457 } finally {
2458 restoreCallingIdentity(identityToken);
2459 }
2460 }
2461
2462 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2463 private abstract class StartAccountSession extends Session {
2464
2465 public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
2466 String accountType, boolean expectActivityLaunch, String accountName,
2467 boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
2468 super(accounts, response, accountType, expectActivityLaunch,
2469 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2470 updateLastAuthenticationTime);
2471 }
2472
2473 @Override
2474 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002475 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08002476 mNumResults++;
2477 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08002478 if (result != null
2479 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08002480 checkKeyIntent(
2481 Binder.getCallingUid(),
2482 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08002483 }
Sandra Kwan78812282015-11-04 11:19:47 -08002484 IAccountManagerResponse response;
2485 if (mExpectActivityLaunch && result != null
2486 && result.containsKey(AccountManager.KEY_INTENT)) {
2487 response = mResponse;
2488 } else {
2489 response = getResponseAndClose();
2490 }
2491 if (response == null) {
2492 return;
2493 }
2494 if (result == null) {
2495 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2496 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2497 + response);
2498 }
2499 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2500 "null bundle returned");
2501 return;
2502 }
2503
2504 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2505 // All AccountManager error codes are greater
2506 // than 0
2507 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2508 result.getString(AccountManager.KEY_ERROR_MESSAGE));
2509 return;
2510 }
2511
2512 // Strip auth token from result.
2513 result.remove(AccountManager.KEY_AUTHTOKEN);
2514
2515 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2516 Log.v(TAG,
2517 getClass().getSimpleName() + " calling onResult() on response " + response);
2518 }
2519
2520 // Get the session bundle created by authenticator. The
2521 // bundle contains data necessary for finishing the session
2522 // later. The session bundle will be encrypted here and
2523 // decrypted later when trying to finish the session.
2524 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
2525 if (sessionBundle != null) {
2526 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2527 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08002528 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002529 Log.w(TAG, "Account type in session bundle doesn't match request.");
2530 }
2531 // Add accountType info to session bundle. This will
2532 // override any value set by authenticator.
2533 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
2534
2535 // Encrypt session bundle before returning to caller.
2536 try {
2537 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2538 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
2539 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
2540 } catch (GeneralSecurityException e) {
2541 if (Log.isLoggable(TAG, Log.DEBUG)) {
2542 Log.v(TAG, "Failed to encrypt session bundle!", e);
2543 }
2544 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2545 "failed to encrypt session bundle");
2546 return;
2547 }
2548 }
2549
2550 sendResponse(response, result);
2551 }
2552 }
2553
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002554 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08002555 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002556 @NonNull Bundle sessionBundle,
2557 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002558 Bundle appInfo,
2559 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002560 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08002561 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002562 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2563 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002564 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002565 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08002566 + ", caller's uid " + callingUid
2567 + ", caller's user id " + UserHandle.getCallingUserId()
2568 + ", pid " + Binder.getCallingPid()
2569 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002570 }
2571 if (response == null) {
2572 throw new IllegalArgumentException("response is null");
2573 }
2574
2575 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
2576 // Account type is added to it before encryption.
2577 if (sessionBundle == null || sessionBundle.size() == 0) {
2578 throw new IllegalArgumentException("sessionBundle is empty");
2579 }
2580
Sandra Kwan0b84b452016-01-20 15:25:42 -08002581 // Only allow the system process to finish session for other users
2582 if (isCrossUser(callingUid, userId)) {
2583 throw new SecurityException(
2584 String.format(
2585 "User %s trying to finish session for %s without cross user permission",
2586 UserHandle.getCallingUserId(),
2587 userId));
2588 }
2589
Sandra Kwana578d112015-12-16 16:01:43 -08002590 // Only allow system to finish session
Sandra Kwan0b84b452016-01-20 15:25:42 -08002591 if (!isSystemUid(callingUid)) {
Sandra Kwana578d112015-12-16 16:01:43 -08002592 String msg = String.format(
Sandra Kwan0b84b452016-01-20 15:25:42 -08002593 "uid %s cannot finish session because it's not system uid.",
2594 callingUid);
Sandra Kwana578d112015-12-16 16:01:43 -08002595 throw new SecurityException(msg);
2596 }
2597
Sandra Kwan0b84b452016-01-20 15:25:42 -08002598 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002599 sendErrorResponse(response,
2600 AccountManager.ERROR_CODE_USER_RESTRICTED,
2601 "User is not allowed to add an account!");
2602 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2603 return;
2604 }
2605
2606 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002607 final Bundle decryptedBundle;
2608 final String accountType;
2609 // First decrypt session bundle to get account type for checking permission.
2610 try {
2611 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2612 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
2613 if (decryptedBundle == null) {
2614 sendErrorResponse(
2615 response,
2616 AccountManager.ERROR_CODE_BAD_REQUEST,
2617 "failed to decrypt session bundle");
2618 return;
2619 }
2620 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2621 // Account type cannot be null. This should not happen if session bundle was created
2622 // properly by #StartAccountSession.
2623 if (TextUtils.isEmpty(accountType)) {
2624 sendErrorResponse(
2625 response,
2626 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
2627 "accountType is empty");
2628 return;
2629 }
2630
2631 // If by any chances, decryptedBundle contains colliding keys with
2632 // system info
2633 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
2634 // update credentials flow, we should replace with the new values of the current call.
2635 if (appInfo != null) {
2636 decryptedBundle.putAll(appInfo);
2637 }
2638
2639 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08002640 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002641 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
2642 } catch (GeneralSecurityException e) {
2643 if (Log.isLoggable(TAG, Log.DEBUG)) {
2644 Log.v(TAG, "Failed to decrypt session bundle!", e);
2645 }
2646 sendErrorResponse(
2647 response,
2648 AccountManager.ERROR_CODE_BAD_REQUEST,
2649 "failed to decrypt session bundle");
2650 return;
2651 }
2652
Sandra Kwan0b84b452016-01-20 15:25:42 -08002653 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002654 sendErrorResponse(
2655 response,
2656 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2657 "User cannot modify accounts of this type (policy).");
2658 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2659 userId);
2660 return;
2661 }
2662
2663 long identityToken = clearCallingIdentity();
2664 try {
2665 UserAccounts accounts = getUserAccounts(userId);
2666 logRecordWithUid(
2667 accounts,
2668 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
2669 TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002670 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002671 new Session(
2672 accounts,
2673 response,
2674 accountType,
2675 expectActivityLaunch,
2676 true /* stripAuthTokenFromResult */,
2677 null /* accountName */,
2678 false /* authDetailsRequired */,
2679 true /* updateLastAuthenticationTime */) {
2680 @Override
2681 public void run() throws RemoteException {
2682 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
2683 }
2684
2685 @Override
2686 protected String toDebugString(long now) {
2687 return super.toDebugString(now)
2688 + ", finishSession"
2689 + ", accountType " + accountType;
2690 }
2691 }.bind();
2692 } finally {
2693 restoreCallingIdentity(identityToken);
2694 }
2695 }
2696
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002697 private void showCantAddAccount(int errorCode, int userId) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002698 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2699 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2700 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2701 long identityToken = clearCallingIdentity();
2702 try {
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002703 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002704 } finally {
2705 restoreCallingIdentity(identityToken);
2706 }
2707 }
2708
2709 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002710 public void confirmCredentialsAsUser(
2711 IAccountManagerResponse response,
2712 final Account account,
2713 final Bundle options,
2714 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002715 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002716 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002717 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002718 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2719 Log.v(TAG, "confirmCredentials: " + account
2720 + ", response " + response
2721 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002722 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002723 + ", pid " + Binder.getCallingPid());
2724 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002725 // Only allow the system process to read accounts of other users
2726 if (isCrossUser(callingUid, userId)) {
2727 throw new SecurityException(
2728 String.format(
2729 "User %s trying to confirm account credentials for %s" ,
2730 UserHandle.getCallingUserId(),
2731 userId));
2732 }
Fred Quintana382601f2010-03-25 12:25:10 -07002733 if (response == null) throw new IllegalArgumentException("response is null");
2734 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002735 long identityToken = clearCallingIdentity();
2736 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002737 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002738 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002739 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002740 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002741 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002742 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002743 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002744 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002745 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002746 protected String toDebugString(long now) {
2747 return super.toDebugString(now) + ", confirmCredentials"
2748 + ", " + account;
2749 }
2750 }.bind();
2751 } finally {
2752 restoreCallingIdentity(identityToken);
2753 }
Fred Quintana60307342009-03-24 22:48:12 -07002754 }
2755
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002756 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002757 public void updateCredentials(IAccountManagerResponse response, final Account account,
2758 final String authTokenType, final boolean expectActivityLaunch,
2759 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002760 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002761 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2762 Log.v(TAG, "updateCredentials: " + account
2763 + ", response " + response
2764 + ", authTokenType " + authTokenType
2765 + ", expectActivityLaunch " + expectActivityLaunch
2766 + ", caller's uid " + Binder.getCallingUid()
2767 + ", pid " + Binder.getCallingPid());
2768 }
Fred Quintana382601f2010-03-25 12:25:10 -07002769 if (response == null) throw new IllegalArgumentException("response is null");
2770 if (account == null) throw new IllegalArgumentException("account is null");
2771 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002772 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002773 long identityToken = clearCallingIdentity();
2774 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002775 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002776 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002777 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002778 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002779 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002780 public void run() throws RemoteException {
2781 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
2782 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002783 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002784 protected String toDebugString(long now) {
2785 if (loginOptions != null) loginOptions.keySet();
2786 return super.toDebugString(now) + ", updateCredentials"
2787 + ", " + account
2788 + ", authTokenType " + authTokenType
2789 + ", loginOptions " + loginOptions;
2790 }
2791 }.bind();
2792 } finally {
2793 restoreCallingIdentity(identityToken);
2794 }
Fred Quintana60307342009-03-24 22:48:12 -07002795 }
2796
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002797 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08002798 public void startUpdateCredentialsSession(
2799 IAccountManagerResponse response,
2800 final Account account,
2801 final String authTokenType,
2802 final boolean expectActivityLaunch,
2803 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002804 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08002805 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2806 Log.v(TAG,
2807 "startUpdateCredentialsSession: " + account + ", response " + response
2808 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
2809 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
2810 + ", pid " + Binder.getCallingPid());
2811 }
2812 if (response == null) {
2813 throw new IllegalArgumentException("response is null");
2814 }
2815 if (account == null) {
2816 throw new IllegalArgumentException("account is null");
2817 }
Sandra Kwana578d112015-12-16 16:01:43 -08002818
2819 final int uid = Binder.getCallingUid();
2820 // Only allow system to start session
2821 if (!isSystemUid(uid)) {
2822 String msg = String.format(
2823 "uid %s cannot start update credentials session.",
2824 uid);
2825 throw new SecurityException(msg);
2826 }
2827
Sandra Kwane68c37e2015-11-12 17:11:49 -08002828 int userId = UserHandle.getCallingUserId();
2829 long identityToken = clearCallingIdentity();
2830 try {
2831 UserAccounts accounts = getUserAccounts(userId);
2832 new StartAccountSession(
2833 accounts,
2834 response,
2835 account.type,
2836 expectActivityLaunch,
2837 account.name,
2838 false /* authDetailsRequired */,
2839 true /* updateLastCredentialTime */) {
2840 @Override
2841 public void run() throws RemoteException {
2842 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
2843 loginOptions);
2844 }
2845
2846 @Override
2847 protected String toDebugString(long now) {
2848 if (loginOptions != null)
2849 loginOptions.keySet();
2850 return super.toDebugString(now)
2851 + ", startUpdateCredentialsSession"
2852 + ", " + account
2853 + ", authTokenType " + authTokenType
2854 + ", loginOptions " + loginOptions;
2855 }
2856 }.bind();
2857 } finally {
2858 restoreCallingIdentity(identityToken);
2859 }
2860 }
2861
2862 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08002863 public void isCredentialsUpdateSuggested(
2864 IAccountManagerResponse response,
2865 final Account account,
2866 final String statusToken) {
2867 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2868 Log.v(TAG,
2869 "isCredentialsUpdateSuggested: " + account + ", response " + response
2870 + ", caller's uid " + Binder.getCallingUid()
2871 + ", pid " + Binder.getCallingPid());
2872 }
2873 if (response == null) {
2874 throw new IllegalArgumentException("response is null");
2875 }
2876 if (account == null) {
2877 throw new IllegalArgumentException("account is null");
2878 }
2879 if (TextUtils.isEmpty(statusToken)) {
2880 throw new IllegalArgumentException("status token is empty");
2881 }
2882
2883 int uid = Binder.getCallingUid();
2884 // Only allow system to start session
2885 if (!isSystemUid(uid)) {
2886 String msg = String.format(
2887 "uid %s cannot stat add account session.",
2888 uid);
2889 throw new SecurityException(msg);
2890 }
2891
2892 int usrId = UserHandle.getCallingUserId();
2893 long identityToken = clearCallingIdentity();
2894 try {
2895 UserAccounts accounts = getUserAccounts(usrId);
2896 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
2897 false /* stripAuthTokenFromResult */, account.name,
2898 false /* authDetailsRequired */) {
2899 @Override
2900 protected String toDebugString(long now) {
2901 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
2902 + ", " + account;
2903 }
2904
2905 @Override
2906 public void run() throws RemoteException {
2907 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
2908 }
2909
2910 @Override
2911 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002912 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08002913 IAccountManagerResponse response = getResponseAndClose();
2914 if (response == null) {
2915 return;
2916 }
2917
2918 if (result == null) {
2919 sendErrorResponse(
2920 response,
2921 AccountManager.ERROR_CODE_INVALID_RESPONSE,
2922 "null bundle");
2923 return;
2924 }
2925
2926 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2927 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2928 + response);
2929 }
2930 // Check to see if an error occurred. We know if an error occurred because all
2931 // error codes are greater than 0.
2932 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
2933 sendErrorResponse(response,
2934 result.getInt(AccountManager.KEY_ERROR_CODE),
2935 result.getString(AccountManager.KEY_ERROR_MESSAGE));
2936 return;
2937 }
2938 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
2939 sendErrorResponse(
2940 response,
2941 AccountManager.ERROR_CODE_INVALID_RESPONSE,
2942 "no result in response");
2943 return;
2944 }
2945 final Bundle newResult = new Bundle();
2946 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
2947 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
2948 sendResponse(response, newResult);
2949 }
2950 }.bind();
2951 } finally {
2952 restoreCallingIdentity(identityToken);
2953 }
2954 }
2955
2956 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002957 public void editProperties(IAccountManagerResponse response, final String accountType,
2958 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002959 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002960 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2961 Log.v(TAG, "editProperties: accountType " + accountType
2962 + ", response " + response
2963 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002964 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002965 + ", pid " + Binder.getCallingPid());
2966 }
Fred Quintana382601f2010-03-25 12:25:10 -07002967 if (response == null) throw new IllegalArgumentException("response is null");
2968 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002969 int userId = UserHandle.getCallingUserId();
2970 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002971 String msg = String.format(
2972 "uid %s cannot edit authenticator properites for account type: %s",
2973 callingUid,
2974 accountType);
2975 throw new SecurityException(msg);
2976 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002977 long identityToken = clearCallingIdentity();
2978 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002979 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002980 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002981 true /* stripAuthTokenFromResult */, null /* accountName */,
2982 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002983 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002984 public void run() throws RemoteException {
2985 mAuthenticator.editProperties(this, mAccountType);
2986 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002987 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002988 protected String toDebugString(long now) {
2989 return super.toDebugString(now) + ", editProperties"
2990 + ", accountType " + accountType;
2991 }
2992 }.bind();
2993 } finally {
2994 restoreCallingIdentity(identityToken);
2995 }
Fred Quintana60307342009-03-24 22:48:12 -07002996 }
2997
Amith Yamasani12747872015-12-07 14:19:49 -08002998 @Override
2999 public boolean someUserHasAccount(@NonNull final Account account) {
3000 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3001 throw new SecurityException("Only system can check for accounts across users");
3002 }
3003 final long token = Binder.clearCallingIdentity();
3004 try {
3005 AccountAndUser[] allAccounts = getAllAccounts();
3006 for (int i = allAccounts.length - 1; i >= 0; i--) {
3007 if (allAccounts[i].account.equals(account)) {
3008 return true;
3009 }
3010 }
3011 return false;
3012 } finally {
3013 Binder.restoreCallingIdentity(token);
3014 }
3015 }
3016
Fred Quintana33269202009-04-20 16:05:10 -07003017 private class GetAccountsByTypeAndFeatureSession extends Session {
3018 private final String[] mFeatures;
3019 private volatile Account[] mAccountsOfType = null;
3020 private volatile ArrayList<Account> mAccountsWithFeatures = null;
3021 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003022 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003023
Amith Yamasani04e0d262012-02-14 11:50:53 -08003024 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003025 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003026 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003027 true /* stripAuthTokenFromResult */, null /* accountName */,
3028 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003029 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003030 mFeatures = features;
3031 }
3032
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003033 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003034 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003035 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003036 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3037 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003038 }
Fred Quintana33269202009-04-20 16:05:10 -07003039 // check whether each account matches the requested features
3040 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
3041 mCurrentAccount = 0;
3042
3043 checkAccount();
3044 }
3045
3046 public void checkAccount() {
3047 if (mCurrentAccount >= mAccountsOfType.length) {
3048 sendResult();
3049 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07003050 }
Fred Quintana33269202009-04-20 16:05:10 -07003051
Fred Quintana29e94b82010-03-10 12:11:51 -08003052 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3053 if (accountAuthenticator == null) {
3054 // It is possible that the authenticator has died, which is indicated by
3055 // mAuthenticator being set to null. If this happens then just abort.
3056 // There is no need to send back a result or error in this case since
3057 // that already happened when mAuthenticator was cleared.
3058 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3059 Log.v(TAG, "checkAccount: aborting session since we are no longer"
3060 + " connected to the authenticator, " + toDebugString());
3061 }
3062 return;
3063 }
Fred Quintana33269202009-04-20 16:05:10 -07003064 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08003065 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07003066 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003067 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07003068 }
3069 }
3070
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003071 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003072 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003073 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07003074 mNumResults++;
3075 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003076 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07003077 return;
3078 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003079 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07003080 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3081 }
3082 mCurrentAccount++;
3083 checkAccount();
3084 }
3085
3086 public void sendResult() {
3087 IAccountManagerResponse response = getResponseAndClose();
3088 if (response != null) {
3089 try {
3090 Account[] accounts = new Account[mAccountsWithFeatures.size()];
3091 for (int i = 0; i < accounts.length; i++) {
3092 accounts[i] = mAccountsWithFeatures.get(i);
3093 }
Fred Quintana56285a62010-12-02 14:20:51 -08003094 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3095 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3096 + response);
3097 }
Fred Quintana33269202009-04-20 16:05:10 -07003098 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003099 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07003100 response.onResult(result);
3101 } catch (RemoteException e) {
3102 // if the caller is dead then there is no one to care about remote exceptions
3103 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3104 Log.v(TAG, "failure while notifying response", e);
3105 }
3106 }
3107 }
3108 }
3109
3110
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003111 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003112 protected String toDebugString(long now) {
3113 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3114 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3115 }
3116 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003117
Amith Yamasani04e0d262012-02-14 11:50:53 -08003118 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003119 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08003120 * @hide
3121 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003122 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003123 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003124 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07003125 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3126 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003127 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003128 return new Account[0];
3129 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003130 long identityToken = clearCallingIdentity();
3131 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003132 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003133 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003134 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003135 callingUid,
3136 null, // packageName
3137 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003138 } finally {
3139 restoreCallingIdentity(identityToken);
3140 }
3141 }
3142
Amith Yamasanif29f2362012-04-05 18:29:52 -07003143 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003144 * Returns accounts for all running users.
3145 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07003146 * @hide
3147 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003148 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003149 public AccountAndUser[] getRunningAccounts() {
3150 final int[] runningUserIds;
3151 try {
3152 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3153 } catch (RemoteException e) {
3154 // Running in system_server; should never happen
3155 throw new RuntimeException(e);
3156 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003157 return getAccounts(runningUserIds);
3158 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07003159
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003160 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003161 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003162 public AccountAndUser[] getAllAccounts() {
3163 final List<UserInfo> users = getUserManager().getUsers();
3164 final int[] userIds = new int[users.size()];
3165 for (int i = 0; i < userIds.length; i++) {
3166 userIds[i] = users.get(i).id;
3167 }
3168 return getAccounts(userIds);
3169 }
3170
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003171 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003172 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003173 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07003174 for (int userId : userIds) {
3175 UserAccounts userAccounts = getUserAccounts(userId);
3176 if (userAccounts == null) continue;
3177 synchronized (userAccounts.cacheLock) {
3178 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3179 Binder.getCallingUid(), null);
3180 for (int a = 0; a < accounts.length; a++) {
3181 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07003182 }
3183 }
3184 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003185
3186 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3187 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07003188 }
3189
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003190 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003191 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003192 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3193 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07003194 }
3195
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003196 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003197 private Account[] getAccountsAsUser(
3198 String type,
3199 int userId,
3200 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07003201 int packageUid,
3202 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003203 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003204 // Only allow the system process to read accounts of other users
3205 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07003206 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08003207 && mContext.checkCallingOrSelfPermission(
3208 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3209 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003210 throw new SecurityException("User " + UserHandle.getCallingUserId()
3211 + " trying to get account for " + userId);
3212 }
3213
Fred Quintana56285a62010-12-02 14:20:51 -08003214 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3215 Log.v(TAG, "getAccounts: accountType " + type
3216 + ", caller's uid " + Binder.getCallingUid()
3217 + ", pid " + Binder.getCallingPid());
3218 }
Amith Yamasani27db4682013-03-30 17:07:47 -07003219 // If the original calling app was using the framework account chooser activity, we'll
3220 // be passed in the original caller's uid here, which is what should be used for filtering.
3221 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3222 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07003223 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07003224 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003225
Svetoslavf3f02ac2015-09-08 14:36:35 -07003226 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3227 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003228 if (visibleAccountTypes.isEmpty()
3229 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003230 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003231 } else if (visibleAccountTypes.contains(type)) {
3232 // Prune the list down to just the requested type.
3233 visibleAccountTypes = new ArrayList<>();
3234 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07003235 } // else aggregate all the visible accounts (it won't matter if the
3236 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003237
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003238 long identityToken = clearCallingIdentity();
3239 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003240 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003241 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003242 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003243 callingUid,
3244 callingPackage,
3245 visibleAccountTypes);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003246 } finally {
3247 restoreCallingIdentity(identityToken);
3248 }
3249 }
3250
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003251 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003252 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003253 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003254 int callingUid,
3255 String callingPackage,
3256 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003257 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003258 ArrayList<Account> visibleAccounts = new ArrayList<>();
3259 for (String visibleType : visibleAccountTypes) {
3260 Account[] accountsForType = getAccountsFromCacheLocked(
3261 userAccounts, visibleType, callingUid, callingPackage);
3262 if (accountsForType != null) {
3263 visibleAccounts.addAll(Arrays.asList(accountsForType));
3264 }
3265 }
3266 Account[] result = new Account[visibleAccounts.size()];
3267 for (int i = 0; i < visibleAccounts.size(); i++) {
3268 result[i] = visibleAccounts.get(i);
3269 }
3270 return result;
3271 }
3272 }
3273
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003274 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003275 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3276 checkManageUsersPermission("addSharedAccountsFromParentUser");
3277 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3278 for (Account account : accounts) {
3279 addSharedAccountAsUser(account, userId);
3280 }
3281 }
3282
3283 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08003284 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003285 UserAccounts accounts = getUserAccounts(userId);
3286 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Amith Yamasani67df64b2012-12-14 12:09:36 -08003287 ContentValues values = new ContentValues();
3288 values.put(ACCOUNTS_NAME, account.name);
3289 values.put(ACCOUNTS_TYPE, account.type);
3290 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3291 new String[] {account.name, account.type});
3292 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3293 if (accountId < 0) {
3294 Log.w(TAG, "insertAccountIntoDatabase: " + account
3295 + ", skipping the DB insert failed");
3296 return false;
3297 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003298 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003299 return true;
3300 }
3301
3302 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003303 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3304 userId = handleIncomingUser(userId);
3305 UserAccounts accounts = getUserAccounts(userId);
3306 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003307 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003308 final ContentValues values = new ContentValues();
3309 values.put(ACCOUNTS_NAME, newName);
3310 values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
3311 int r = db.update(
3312 TABLE_SHARED_ACCOUNTS,
3313 values,
3314 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3315 new String[] { account.name, account.type });
3316 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003317 int callingUid = getCallingUid();
3318 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3319 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003320 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003321 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003322 }
3323 return r > 0;
3324 }
3325
3326 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08003327 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003328 return removeSharedAccountAsUser(account, userId, getCallingUid());
3329 }
3330
3331 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08003332 userId = handleIncomingUser(userId);
3333 UserAccounts accounts = getUserAccounts(userId);
3334 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003335 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003336 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3337 new String[] {account.name, account.type});
3338 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003339 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3340 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07003341 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003342 }
3343 return r > 0;
3344 }
3345
3346 @Override
3347 public Account[] getSharedAccountsAsUser(int userId) {
3348 userId = handleIncomingUser(userId);
3349 UserAccounts accounts = getUserAccounts(userId);
3350 ArrayList<Account> accountList = new ArrayList<Account>();
3351 Cursor cursor = null;
3352 try {
3353 cursor = accounts.openHelper.getReadableDatabase()
3354 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3355 null, null, null, null, null);
3356 if (cursor != null && cursor.moveToFirst()) {
3357 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3358 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3359 do {
3360 accountList.add(new Account(cursor.getString(nameIndex),
3361 cursor.getString(typeIndex)));
3362 } while (cursor.moveToNext());
3363 }
3364 } finally {
3365 if (cursor != null) {
3366 cursor.close();
3367 }
3368 }
3369 Account[] accountArray = new Account[accountList.size()];
3370 accountList.toArray(accountArray);
3371 return accountArray;
3372 }
3373
3374 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003375 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003376 public Account[] getAccounts(String type, String opPackageName) {
3377 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003378 }
3379
Amith Yamasani27db4682013-03-30 17:07:47 -07003380 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003381 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003382 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003383 int callingUid = Binder.getCallingUid();
3384 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3385 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3386 + callingUid + " with uid=" + uid);
3387 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07003388 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3389 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07003390 }
3391
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003392 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003393 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003394 public Account[] getAccountsByTypeForPackage(String type, String packageName,
3395 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003396 int packageUid = -1;
3397 try {
3398 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07003399 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3400 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003401 } catch (RemoteException re) {
3402 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3403 return new Account[0];
3404 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07003405 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3406 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003407 }
3408
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003409 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003410 public void getAccountsByFeatures(
3411 IAccountManagerResponse response,
3412 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07003413 String[] features,
3414 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003415 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003416 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3417 Log.v(TAG, "getAccounts: accountType " + type
3418 + ", response " + response
3419 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003420 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003421 + ", pid " + Binder.getCallingPid());
3422 }
Fred Quintana382601f2010-03-25 12:25:10 -07003423 if (response == null) throw new IllegalArgumentException("response is null");
3424 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003425 int userId = UserHandle.getCallingUserId();
3426
Svetoslavf3f02ac2015-09-08 14:36:35 -07003427 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3428 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003429 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003430 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003431 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003432 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
3433 try {
3434 response.onResult(result);
3435 } catch (RemoteException e) {
3436 Log.e(TAG, "Cannot respond to caller do to exception." , e);
3437 }
3438 return;
3439 }
Fred Quintana33269202009-04-20 16:05:10 -07003440 long identityToken = clearCallingIdentity();
3441 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003442 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003443 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003444 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003445 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003446 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003447 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08003448 Bundle result = new Bundle();
3449 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3450 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003451 return;
3452 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003453 new GetAccountsByTypeAndFeatureSession(
3454 userAccounts,
3455 response,
3456 type,
3457 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003458 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07003459 } finally {
3460 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07003461 }
3462 }
3463
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003464 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
3465 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
3466 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3467 try {
3468 if (cursor.moveToNext()) {
3469 return cursor.getLong(0);
3470 }
3471 return -1;
3472 } finally {
3473 cursor.close();
3474 }
3475 }
3476
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003477 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07003478 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003479 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07003480 try {
3481 if (cursor.moveToNext()) {
3482 return cursor.getLong(0);
3483 }
3484 return -1;
3485 } finally {
3486 cursor.close();
3487 }
3488 }
3489
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003490 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07003491 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
3492 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
3493 new String[]{key}, null, null, null);
3494 try {
3495 if (cursor.moveToNext()) {
3496 return cursor.getLong(0);
3497 }
3498 return -1;
3499 } finally {
3500 cursor.close();
3501 }
3502 }
3503
Fred Quintanaa698f422009-04-08 19:14:54 -07003504 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07003505 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07003506 IAccountManagerResponse mResponse;
3507 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07003508 final boolean mExpectActivityLaunch;
3509 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003510 final String mAccountName;
3511 // Indicates if we need to add auth details(like last credential time)
3512 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003513 // If set, we need to update the last authenticated time. This is
3514 // currently
3515 // used on
3516 // successful confirming credentials.
3517 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07003518
Fred Quintana33269202009-04-20 16:05:10 -07003519 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07003520 private int mNumRequestContinued = 0;
3521 private int mNumErrors = 0;
3522
Fred Quintana60307342009-03-24 22:48:12 -07003523 IAccountAuthenticator mAuthenticator = null;
3524
Fred Quintana8570f742010-02-18 10:32:54 -08003525 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003526 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08003527
Amith Yamasani04e0d262012-02-14 11:50:53 -08003528 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003529 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3530 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003531 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
3532 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
3533 }
3534
3535 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3536 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3537 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07003538 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08003539 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07003540 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08003541 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08003542 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07003543 mResponse = response;
3544 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07003545 mExpectActivityLaunch = expectActivityLaunch;
3546 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003547 mAccountName = accountName;
3548 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003549 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003550
Fred Quintanaa698f422009-04-08 19:14:54 -07003551 synchronized (mSessions) {
3552 mSessions.put(toString(), this);
3553 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08003554 if (response != null) {
3555 try {
3556 response.asBinder().linkToDeath(this, 0 /* flags */);
3557 } catch (RemoteException e) {
3558 mResponse = null;
3559 binderDied();
3560 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003561 }
Fred Quintana60307342009-03-24 22:48:12 -07003562 }
3563
Fred Quintanaa698f422009-04-08 19:14:54 -07003564 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07003565 if (mResponse == null) {
3566 // this session has already been closed
3567 return null;
3568 }
Fred Quintana60307342009-03-24 22:48:12 -07003569 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07003570 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07003571 return response;
3572 }
3573
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003574 /**
3575 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
3576 * security policy.
3577 *
3578 * In particular we want to make sure that the Authenticator doesn't try to trick users
3579 * into launching aribtrary intents on the device via by tricking to click authenticator
3580 * supplied entries in the system Settings app.
3581 */
3582 protected void checkKeyIntent(
3583 int authUid,
3584 Intent intent) throws SecurityException {
3585 long bid = Binder.clearCallingIdentity();
3586 try {
3587 PackageManager pm = mContext.getPackageManager();
3588 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
3589 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
3590 int targetUid = targetActivityInfo.applicationInfo.uid;
3591 if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) {
3592 String pkgName = targetActivityInfo.packageName;
3593 String activityName = targetActivityInfo.name;
3594 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
3595 + "does not share a signature with the supplying authenticator (%s).";
3596 throw new SecurityException(
3597 String.format(tmpl, activityName, pkgName, mAccountType));
3598 }
3599 } finally {
3600 Binder.restoreCallingIdentity(bid);
3601 }
3602 }
3603
Fred Quintanaa698f422009-04-08 19:14:54 -07003604 private void close() {
3605 synchronized (mSessions) {
3606 if (mSessions.remove(toString()) == null) {
3607 // the session was already closed, so bail out now
3608 return;
3609 }
3610 }
3611 if (mResponse != null) {
3612 // stop listening for response deaths
3613 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
3614
3615 // clear this so that we don't accidentally send any further results
3616 mResponse = null;
3617 }
3618 cancelTimeout();
3619 unbind();
3620 }
3621
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003622 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003623 public void binderDied() {
3624 mResponse = null;
3625 close();
3626 }
3627
3628 protected String toDebugString() {
3629 return toDebugString(SystemClock.elapsedRealtime());
3630 }
3631
3632 protected String toDebugString(long now) {
3633 return "Session: expectLaunch " + mExpectActivityLaunch
3634 + ", connected " + (mAuthenticator != null)
3635 + ", stats (" + mNumResults + "/" + mNumRequestContinued
3636 + "/" + mNumErrors + ")"
3637 + ", lifetime " + ((now - mCreationTime) / 1000.0);
3638 }
3639
Fred Quintana60307342009-03-24 22:48:12 -07003640 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07003641 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3642 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
3643 }
Fred Quintanab839afc2009-10-14 15:57:28 -07003644 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003645 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003646 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07003647 }
3648 }
3649
3650 private void unbind() {
3651 if (mAuthenticator != null) {
3652 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07003653 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07003654 }
3655 }
3656
Fred Quintana60307342009-03-24 22:48:12 -07003657 public void cancelTimeout() {
3658 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
3659 }
3660
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003661 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07003662 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07003663 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07003664 try {
3665 run();
3666 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003667 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07003668 "remote exception");
3669 }
Fred Quintana60307342009-03-24 22:48:12 -07003670 }
3671
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003672 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07003673 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003674 mAuthenticator = null;
3675 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003676 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07003677 try {
3678 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3679 "disconnected");
3680 } catch (RemoteException e) {
3681 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3682 Log.v(TAG, "Session.onServiceDisconnected: "
3683 + "caught RemoteException while responding", e);
3684 }
3685 }
Fred Quintana60307342009-03-24 22:48:12 -07003686 }
3687 }
3688
Fred Quintanab839afc2009-10-14 15:57:28 -07003689 public abstract void run() throws RemoteException;
3690
Fred Quintana60307342009-03-24 22:48:12 -07003691 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07003692 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003693 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07003694 try {
3695 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3696 "timeout");
3697 } catch (RemoteException e) {
3698 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3699 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
3700 e);
3701 }
3702 }
Fred Quintana60307342009-03-24 22:48:12 -07003703 }
3704 }
3705
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003706 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003707 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003708 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07003709 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003710 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003711 if (result != null) {
3712 boolean isSuccessfulConfirmCreds = result.getBoolean(
3713 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003714 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003715 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
3716 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07003717 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003718 // mUpdateLastAuthenticatedTime is true and the confirmRequest
3719 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07003720 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003721 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003722 if (needUpdate || mAuthDetailsRequired) {
3723 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
3724 if (needUpdate && accountPresent) {
3725 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
3726 }
3727 if (mAuthDetailsRequired) {
3728 long lastAuthenticatedTime = -1;
3729 if (accountPresent) {
3730 lastAuthenticatedTime = DatabaseUtils.longForQuery(
3731 mAccounts.openHelper.getReadableDatabase(),
3732 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
3733 + " from " +
3734 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
3735 + ACCOUNTS_TYPE + "=?",
3736 new String[] {
3737 mAccountName, mAccountType
3738 });
3739 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07003740 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003741 lastAuthenticatedTime);
3742 }
3743 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003744 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003745 if (result != null
3746 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003747 checkKeyIntent(
3748 Binder.getCallingUid(),
3749 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003750 }
3751 if (result != null
3752 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003753 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
3754 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003755 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
3756 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003757 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
3758 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003759 }
Fred Quintana60307342009-03-24 22:48:12 -07003760 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003761 IAccountManagerResponse response;
3762 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003763 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003764 response = mResponse;
3765 } else {
3766 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003767 }
Fred Quintana60307342009-03-24 22:48:12 -07003768 if (response != null) {
3769 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07003770 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08003771 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3772 Log.v(TAG, getClass().getSimpleName()
3773 + " calling onError() on response " + response);
3774 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003775 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07003776 "null bundle returned");
3777 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08003778 if (mStripAuthTokenFromResult) {
3779 result.remove(AccountManager.KEY_AUTHTOKEN);
3780 }
Fred Quintana56285a62010-12-02 14:20:51 -08003781 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3782 Log.v(TAG, getClass().getSimpleName()
3783 + " calling onResult() on response " + response);
3784 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003785 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
3786 (intent == null)) {
3787 // All AccountManager error codes are greater than 0
3788 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
3789 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3790 } else {
3791 response.onResult(result);
3792 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003793 }
Fred Quintana60307342009-03-24 22:48:12 -07003794 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003795 // if the caller is dead then there is no one to care about remote exceptions
3796 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3797 Log.v(TAG, "failure while notifying response", e);
3798 }
Fred Quintana60307342009-03-24 22:48:12 -07003799 }
3800 }
3801 }
Fred Quintana60307342009-03-24 22:48:12 -07003802
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003803 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003804 public void onRequestContinued() {
3805 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07003806 }
3807
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003808 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003809 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003810 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07003811 IAccountManagerResponse response = getResponseAndClose();
3812 if (response != null) {
3813 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08003814 Log.v(TAG, getClass().getSimpleName()
3815 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07003816 }
3817 try {
3818 response.onError(errorCode, errorMessage);
3819 } catch (RemoteException e) {
3820 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3821 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
3822 }
3823 }
3824 } else {
3825 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3826 Log.v(TAG, "Session.onError: already closed");
3827 }
Fred Quintana60307342009-03-24 22:48:12 -07003828 }
3829 }
Fred Quintanab839afc2009-10-14 15:57:28 -07003830
3831 /**
3832 * find the component name for the authenticator and initiate a bind
3833 * if no authenticator or the bind fails then return false, otherwise return true
3834 */
3835 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003836 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
3837 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
3838 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07003839 if (authenticatorInfo == null) {
3840 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3841 Log.v(TAG, "there is no authenticator for " + authenticatorType
3842 + ", bailing out");
3843 }
3844 return false;
3845 }
3846
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07003847 final ActivityManager am = mContext.getSystemService(ActivityManager.class);
3848 if (am.isUserRunningAndLocked(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06003849 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07003850 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
3851 + " which isn't encryption aware");
3852 return false;
3853 }
3854
Fred Quintanab839afc2009-10-14 15:57:28 -07003855 Intent intent = new Intent();
3856 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
3857 intent.setComponent(authenticatorInfo.componentName);
3858 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3859 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
3860 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08003861 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
3862 new UserHandle(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07003863 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3864 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
3865 }
3866 return false;
3867 }
3868
Fred Quintanab839afc2009-10-14 15:57:28 -07003869 return true;
3870 }
Fred Quintana60307342009-03-24 22:48:12 -07003871 }
3872
3873 private class MessageHandler extends Handler {
3874 MessageHandler(Looper looper) {
3875 super(looper);
3876 }
Costin Manolache3348f142009-09-29 18:58:36 -07003877
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003878 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003879 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07003880 switch (msg.what) {
3881 case MESSAGE_TIMED_OUT:
3882 Session session = (Session)msg.obj;
3883 session.onTimedOut();
3884 break;
3885
Amith Yamasani5be347b2013-03-31 17:44:31 -07003886 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00003887 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07003888 break;
3889
Fred Quintana60307342009-03-24 22:48:12 -07003890 default:
3891 throw new IllegalStateException("unhandled message: " + msg.what);
3892 }
3893 }
3894 }
3895
Amith Yamasani04e0d262012-02-14 11:50:53 -08003896 private static String getDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07003897 File systemDir = Environment.getDataSystemDirectory();
Amith Yamasani61f57372012-08-31 12:12:28 -07003898 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003899 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07003900 // Migrate old file, if it exists, to the new location.
3901 // Make sure the new file doesn't already exist. A dummy file could have been
3902 // accidentally created in the old location, causing the new one to become corrupted
3903 // as well.
Amith Yamasani04e0d262012-02-14 11:50:53 -08003904 File oldFile = new File(systemDir, DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07003905 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07003906 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07003907 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07003908 if (!userDir.exists()) {
3909 if (!userDir.mkdirs()) {
3910 throw new IllegalStateException("User dir cannot be created: " + userDir);
3911 }
3912 }
3913 if (!oldFile.renameTo(databaseFile)) {
3914 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
3915 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003916 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08003917 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003918 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08003919 }
3920
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003921 private static class DebugDbHelper{
3922 private DebugDbHelper() {
3923 }
3924
3925 private static String TABLE_DEBUG = "debug_table";
3926
3927 // Columns for the table
3928 private static String ACTION_TYPE = "action_type";
3929 private static String TIMESTAMP = "time";
3930 private static String CALLER_UID = "caller_uid";
3931 private static String TABLE_NAME = "table_name";
3932 private static String KEY = "primary_key";
3933
3934 // These actions correspond to the occurrence of real actions. Since
3935 // these are called by the authenticators, the uid associated will be
3936 // of the authenticator.
3937 private static String ACTION_SET_PASSWORD = "action_set_password";
3938 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
3939 private static String ACTION_ACCOUNT_ADD = "action_account_add";
3940 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
3941 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
3942 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
3943
3944 // These actions don't necessarily correspond to any action on
3945 // accountDb taking place. As an example, there might be a request for
3946 // addingAccount, which might not lead to addition of account on grounds
3947 // of bad authentication. We will still be logging it to keep track of
3948 // who called.
3949 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
3950 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003951
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003952 //This action doesn't add account to accountdb. Account is only
3953 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08003954 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003955 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
3956 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08003957
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003958 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3959
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003960 private static void createDebugTable(SQLiteDatabase db) {
3961 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
3962 + ACCOUNTS_ID + " INTEGER,"
3963 + ACTION_TYPE + " TEXT NOT NULL, "
3964 + TIMESTAMP + " DATETIME,"
3965 + CALLER_UID + " INTEGER NOT NULL,"
3966 + TABLE_NAME + " TEXT NOT NULL,"
3967 + KEY + " INTEGER PRIMARY KEY)");
3968 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
3969 }
3970 }
3971
3972 private void logRecord(UserAccounts accounts, String action, String tableName) {
3973 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3974 logRecord(db, action, tableName, -1, accounts);
3975 }
3976
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003977 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
3978 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3979 logRecord(db, action, tableName, -1, accounts, uid);
3980 }
3981
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003982 /*
3983 * This function receives an opened writable database.
3984 */
3985 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3986 UserAccounts userAccount) {
3987 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
3988 }
3989
3990 /*
3991 * This function receives an opened writable database.
3992 */
3993 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3994 UserAccounts userAccount, int callingUid) {
3995 SQLiteStatement logStatement = userAccount.statementForLogging;
3996 logStatement.bindLong(1, accountId);
3997 logStatement.bindString(2, action);
3998 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
3999 logStatement.bindLong(4, callingUid);
4000 logStatement.bindString(5, tableName);
4001 logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
4002 logStatement.execute();
4003 logStatement.clearBindings();
4004 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
4005 % MAX_DEBUG_DB_SIZE;
4006 }
4007
4008 /*
4009 * This should only be called once to compile the sql statement for logging
4010 * and to find the insertion point.
4011 */
4012 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
4013 UserAccounts userAccount) {
4014 // Initialize the count if not done earlier.
4015 int size = (int) getDebugTableRowCount(db);
4016 if (size >= MAX_DEBUG_DB_SIZE) {
4017 // Table is full, and we need to find the point where to insert.
4018 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
4019 } else {
4020 userAccount.debugDbInsertionPoint = size;
4021 }
4022 compileSqlStatementForLogging(db, userAccount);
4023 }
4024
4025 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
4026 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
4027 + " VALUES (?,?,?,?,?,?)";
4028 userAccount.statementForLogging = db.compileStatement(sql);
4029 }
4030
4031 private long getDebugTableRowCount(SQLiteDatabase db) {
4032 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
4033 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4034 }
4035
4036 /*
4037 * Finds the row key where the next insertion should take place. This should
4038 * be invoked only if the table has reached its full capacity.
4039 */
4040 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
4041 // This query finds the smallest timestamp value (and if 2 records have
4042 // same timestamp, the choose the lower id).
4043 String queryCountDebugDbRows = new StringBuilder()
4044 .append("SELECT ").append(DebugDbHelper.KEY)
4045 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
4046 .append(" ORDER BY ")
4047 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
4048 .append(" LIMIT 1")
4049 .toString();
4050 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4051 }
4052
Amith Yamasani04e0d262012-02-14 11:50:53 -08004053 static class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08004054
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004055 private final Context mContext;
4056 private final int mUserId;
4057
Amith Yamasani04e0d262012-02-14 11:50:53 -08004058 public DatabaseHelper(Context context, int userId) {
4059 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004060 mContext = context;
4061 mUserId = userId;
Fred Quintana60307342009-03-24 22:48:12 -07004062 }
4063
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004064 /**
4065 * This call needs to be made while the mCacheLock is held. The way to
4066 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
4067 * @param db The database.
4068 */
Fred Quintana60307342009-03-24 22:48:12 -07004069 @Override
4070 public void onCreate(SQLiteDatabase db) {
4071 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4072 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4073 + ACCOUNTS_NAME + " TEXT NOT NULL, "
4074 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4075 + ACCOUNTS_PASSWORD + " TEXT, "
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004076 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004077 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
Fred Quintana60307342009-03-24 22:48:12 -07004078 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4079
4080 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
4081 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4082 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4083 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
4084 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
4085 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
4086
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004087 createGrantsTable(db);
4088
Fred Quintana60307342009-03-24 22:48:12 -07004089 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
4090 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4091 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
4092 + EXTRAS_KEY + " TEXT NOT NULL, "
4093 + EXTRAS_VALUE + " TEXT, "
4094 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
4095
4096 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4097 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4098 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07004099
Amith Yamasani67df64b2012-12-14 12:09:36 -08004100 createSharedAccountsTable(db);
4101
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004102 createAccountsDeletionTrigger(db);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004103
4104 DebugDbHelper.createDebugTable(db);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004105 }
4106
Amith Yamasani67df64b2012-12-14 12:09:36 -08004107 private void createSharedAccountsTable(SQLiteDatabase db) {
4108 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4109 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4110 + ACCOUNTS_NAME + " TEXT NOT NULL, "
4111 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4112 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4113 }
4114
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004115 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4116 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4117 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4118 }
4119
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004120 private void addOldAccountNameColumn(SQLiteDatabase db) {
4121 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4122 }
4123
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004124 private void addDebugTable(SQLiteDatabase db) {
4125 DebugDbHelper.createDebugTable(db);
4126 }
4127
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004128 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004129 db.execSQL(""
4130 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4131 + " BEGIN"
4132 + " DELETE FROM " + TABLE_AUTHTOKENS
4133 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4134 + " DELETE FROM " + TABLE_EXTRAS
4135 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004136 + " DELETE FROM " + TABLE_GRANTS
4137 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07004138 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07004139 }
4140
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004141 private void createGrantsTable(SQLiteDatabase db) {
4142 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
4143 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4144 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
4145 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
4146 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4147 + "," + GRANTS_GRANTEE_UID + "))");
4148 }
4149
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004150 private void populateMetaTableWithAuthTypeAndUID(
4151 SQLiteDatabase db,
4152 Map<String, Integer> authTypeAndUIDMap) {
4153 Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator();
4154 while (iterator.hasNext()) {
4155 Entry<String, Integer> entry = iterator.next();
4156 ContentValues values = new ContentValues();
4157 values.put(META_KEY,
4158 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
4159 values.put(META_VALUE, entry.getValue());
4160 db.insert(TABLE_META, null, values);
4161 }
4162 }
4163
Fred Quintana60307342009-03-24 22:48:12 -07004164 @Override
4165 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004166 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07004167
Fred Quintanaa698f422009-04-08 19:14:54 -07004168 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004169 // no longer need to do anything since the work is done
4170 // when upgrading from version 2
4171 oldVersion++;
4172 }
4173
4174 if (oldVersion == 2) {
4175 createGrantsTable(db);
4176 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4177 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07004178 oldVersion++;
4179 }
Costin Manolache3348f142009-09-29 18:58:36 -07004180
4181 if (oldVersion == 3) {
4182 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4183 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4184 oldVersion++;
4185 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004186
4187 if (oldVersion == 4) {
4188 createSharedAccountsTable(db);
4189 oldVersion++;
4190 }
4191
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004192 if (oldVersion == 5) {
4193 addOldAccountNameColumn(db);
4194 oldVersion++;
4195 }
4196
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004197 if (oldVersion == 6) {
4198 addLastSuccessfullAuthenticatedTimeColumn(db);
4199 oldVersion++;
4200 }
4201
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004202 if (oldVersion == 7) {
4203 addDebugTable(db);
4204 oldVersion++;
4205 }
4206
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004207 if (oldVersion == 8) {
4208 populateMetaTableWithAuthTypeAndUID(
4209 db,
4210 AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
4211 oldVersion++;
4212 }
4213
Amith Yamasani67df64b2012-12-14 12:09:36 -08004214 if (oldVersion != newVersion) {
4215 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4216 }
Fred Quintana60307342009-03-24 22:48:12 -07004217 }
4218
4219 @Override
4220 public void onOpen(SQLiteDatabase db) {
4221 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4222 }
4223 }
4224
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004225 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07004226 return asBinder();
4227 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004228
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004229 /**
4230 * Searches array of arguments for the specified string
4231 * @param args array of argument strings
4232 * @param value value to search for
4233 * @return true if the value is contained in the array
4234 */
4235 private static boolean scanArgs(String[] args, String value) {
4236 if (args != null) {
4237 for (String arg : args) {
4238 if (value.equals(arg)) {
4239 return true;
4240 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004241 }
4242 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004243 return false;
4244 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004245
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004246 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004247 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07004248 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4249 != PackageManager.PERMISSION_GRANTED) {
4250 fout.println("Permission Denial: can't dump AccountsManager from from pid="
4251 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4252 + " without permission " + android.Manifest.permission.DUMP);
4253 return;
4254 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004255 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004256 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07004257
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004258 final List<UserInfo> users = getUserManager().getUsers();
4259 for (UserInfo user : users) {
4260 ipw.println("User " + user + ":");
4261 ipw.increaseIndent();
4262 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4263 ipw.println();
4264 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08004265 }
4266 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004267
Amith Yamasani04e0d262012-02-14 11:50:53 -08004268 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4269 String[] args, boolean isCheckinRequest) {
4270 synchronized (userAccounts.cacheLock) {
4271 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004272
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004273 if (isCheckinRequest) {
4274 // This is a checkin request. *Only* upload the account types and the count of each.
4275 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
4276 null, null, ACCOUNTS_TYPE, null, null);
4277 try {
4278 while (cursor.moveToNext()) {
4279 // print type,count
4280 fout.println(cursor.getString(0) + "," + cursor.getString(1));
4281 }
4282 } finally {
4283 if (cursor != null) {
4284 cursor.close();
4285 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004286 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004287 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004288 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07004289 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004290 fout.println("Accounts: " + accounts.length);
4291 for (Account account : accounts) {
4292 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004293 }
Fred Quintana307da1a2010-01-21 14:24:20 -08004294
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004295 // Add debug information.
4296 fout.println();
4297 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
4298 null, null, null, null, DebugDbHelper.TIMESTAMP);
4299 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
4300 fout.println("Accounts History");
4301 try {
4302 while (cursor.moveToNext()) {
4303 // print type,count
4304 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
4305 cursor.getString(2) + "," + cursor.getString(3) + ","
4306 + cursor.getString(4) + "," + cursor.getString(5));
4307 }
4308 } finally {
4309 cursor.close();
4310 }
4311
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004312 fout.println();
4313 synchronized (mSessions) {
4314 final long now = SystemClock.elapsedRealtime();
4315 fout.println("Active Sessions: " + mSessions.size());
4316 for (Session session : mSessions.values()) {
4317 fout.println(" " + session.toDebugString(now));
4318 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004319 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004320
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004321 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004322 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004323 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004324 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004325 }
4326
Amith Yamasani04e0d262012-02-14 11:50:53 -08004327 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Dianne Hackborn41203752012-08-31 14:05:51 -07004328 Intent intent, int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004329 long identityToken = clearCallingIdentity();
4330 try {
4331 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4332 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4333 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004334
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004335 if (intent.getComponent() != null &&
4336 GrantCredentialsPermissionActivity.class.getName().equals(
4337 intent.getComponent().getClassName())) {
Dianne Hackborn41203752012-08-31 14:05:51 -07004338 createNoCredentialsPermissionNotification(account, intent, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004339 } else {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004340 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07004341 intent.addCategory(String.valueOf(notificationId));
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004342 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004343 Context contextForUser = getContextForUser(user);
Fred Quintana33f889a2009-09-14 17:31:26 -07004344 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004345 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004346 Notification n = new Notification.Builder(contextForUser)
4347 .setWhen(0)
4348 .setSmallIcon(android.R.drawable.stat_sys_warning)
4349 .setColor(contextForUser.getColor(
4350 com.android.internal.R.color.system_notification_accent_color))
4351 .setContentTitle(String.format(notificationTitleFormat, account.name))
4352 .setContentText(message)
4353 .setContentIntent(PendingIntent.getActivityAsUser(
4354 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
4355 null, user))
4356 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004357 installNotification(notificationId, n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004358 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004359 } finally {
4360 restoreCallingIdentity(identityToken);
4361 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004362 }
4363
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004364 protected void installNotification(final int notificationId, final Notification n,
4365 UserHandle user) {
Fred Quintana56285a62010-12-02 14:20:51 -08004366 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004367 .notifyAsUser(null, notificationId, n, user);
Fred Quintana56285a62010-12-02 14:20:51 -08004368 }
4369
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004370 protected void cancelNotification(int id, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004371 long identityToken = clearCallingIdentity();
4372 try {
4373 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004374 .cancelAsUser(null, id, user);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004375 } finally {
4376 restoreCallingIdentity(identityToken);
4377 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004378 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004379
Ian Pedowitz358e51f2016-03-15 17:08:27 +00004380 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4381 for (String perm : permissions) {
4382 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4383 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4384 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
4385 }
4386 final int opCode = AppOpsManager.permissionToOpCode(perm);
4387 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4388 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4389 return true;
4390 }
4391 }
4392 }
4393 return false;
4394 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004395
Amith Yamasani67df64b2012-12-14 12:09:36 -08004396 private int handleIncomingUser(int userId) {
4397 try {
4398 return ActivityManagerNative.getDefault().handleIncomingUser(
4399 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4400 } catch (RemoteException re) {
4401 // Shouldn't happen, local.
4402 }
4403 return userId;
4404 }
4405
Christopher Tateccbf84f2013-05-08 15:25:41 -07004406 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004407 final int callingUserId = UserHandle.getUserId(callingUid);
4408
4409 final PackageManager userPackageManager;
4410 try {
4411 userPackageManager = mContext.createPackageContextAsUser(
4412 "android", 0, new UserHandle(callingUserId)).getPackageManager();
4413 } catch (NameNotFoundException e) {
4414 return false;
4415 }
4416
4417 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07004418 for (String name : packages) {
4419 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004420 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08004421 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08004422 && (packageInfo.applicationInfo.privateFlags
4423 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07004424 return true;
4425 }
4426 } catch (PackageManager.NameNotFoundException e) {
4427 return false;
4428 }
4429 }
4430 return false;
4431 }
4432
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004433 private boolean permissionIsGranted(
4434 Account account, String authTokenType, int callerUid, int userId) {
Christopher Tateccbf84f2013-05-08 15:25:41 -07004435 final boolean isPrivileged = isPrivileged(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07004436 final boolean fromAuthenticator = account != null
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004437 && isAccountManagedByCaller(account.type, callerUid, userId);
Fred Quintana31957f12009-10-21 13:43:10 -07004438 final boolean hasExplicitGrants = account != null
Amith Yamasani04e0d262012-02-14 11:50:53 -08004439 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004440 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4441 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08004442 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004443 + ": is authenticator? " + fromAuthenticator
4444 + ", has explicit permission? " + hasExplicitGrants);
4445 }
Christopher Tateccbf84f2013-05-08 15:25:41 -07004446 return fromAuthenticator || hasExplicitGrants || isPrivileged;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004447 }
4448
Svetoslavf3f02ac2015-09-08 14:36:35 -07004449 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
4450 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004451 if (accountType == null) {
4452 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004453 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07004454 return getTypesVisibleToCaller(callingUid, userId,
4455 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004456 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004457 }
4458
4459 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
4460 if (accountType == null) {
4461 return false;
4462 } else {
4463 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
4464 }
4465 }
4466
Svetoslavf3f02ac2015-09-08 14:36:35 -07004467 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
4468 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00004469 boolean isPermitted =
4470 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
4471 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004472 return getTypesForCaller(callingUid, userId, isPermitted);
4473 }
4474
4475 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
4476 return getTypesForCaller(callingUid, userId, false);
4477 }
4478
4479 private List<String> getTypesForCaller(
4480 int callingUid, int userId, boolean isOtherwisePermitted) {
4481 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004482 long identityToken = Binder.clearCallingIdentity();
4483 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
4484 try {
4485 serviceInfos = mAuthenticatorCache.getAllServices(userId);
4486 } finally {
4487 Binder.restoreCallingIdentity(identityToken);
4488 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004489 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004490 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004491 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
4492 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
4493 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004494 }
4495 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004496 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004497 }
4498
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004499 private boolean isAccountPresentForCaller(String accountName, String accountType) {
4500 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
4501 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
4502 if (account.name.equals(accountName)) {
4503 return true;
4504 }
4505 }
4506 }
4507 return false;
4508 }
4509
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004510 private static void checkManageUsersPermission(String message) {
4511 if (ActivityManager.checkComponentPermission(
4512 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
4513 != PackageManager.PERMISSION_GRANTED) {
4514 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
4515 }
4516 }
4517
Amith Yamasani04e0d262012-02-14 11:50:53 -08004518 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
4519 int callerUid) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004520 if (callerUid == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004521 return true;
4522 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004523 UserAccounts accounts = getUserAccountsForCaller();
4524 synchronized (accounts.cacheLock) {
4525 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4526 String[] args = { String.valueOf(callerUid), authTokenType,
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004527 account.name, account.type};
4528 final boolean permissionGranted =
4529 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
4530 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
4531 // TODO: Skip this check when running automated tests. Replace this
4532 // with a more general solution.
4533 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08004534 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004535 + " but ignoring since device is in test harness.");
4536 return true;
4537 }
4538 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004539 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004540 }
4541
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004542 private boolean isSystemUid(int callingUid) {
4543 String[] packages = null;
4544 long ident = Binder.clearCallingIdentity();
4545 try {
4546 packages = mPackageManager.getPackagesForUid(callingUid);
4547 } finally {
4548 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07004549 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004550 if (packages != null) {
4551 for (String name : packages) {
4552 try {
4553 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
4554 if (packageInfo != null
4555 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
4556 != 0) {
4557 return true;
4558 }
4559 } catch (PackageManager.NameNotFoundException e) {
4560 Log.w(TAG, String.format("Could not find package [%s]", name), e);
4561 }
4562 }
4563 } else {
4564 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07004565 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004566 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00004567 }
4568
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004569 /** Succeeds if any of the specified permissions are granted. */
4570 private void checkReadAccountsPermitted(
4571 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004572 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004573 int userId,
4574 String opPackageName) {
4575 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004576 String msg = String.format(
4577 "caller uid %s cannot access %s accounts",
4578 callingUid,
4579 accountType);
4580 Log.w(TAG, " " + msg);
4581 throw new SecurityException(msg);
4582 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004583 }
4584
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004585 private boolean canUserModifyAccounts(int userId, int callingUid) {
4586 // the managing app can always modify accounts
4587 if (isProfileOwner(callingUid)) {
4588 return true;
4589 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004590 if (getUserManager().getUserRestrictions(new UserHandle(userId))
4591 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
4592 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08004593 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004594 return true;
4595 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004596
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004597 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
4598 // the managing app can always modify accounts
4599 if (isProfileOwner(callingUid)) {
4600 return true;
4601 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004602 DevicePolicyManager dpm = (DevicePolicyManager) mContext
4603 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004604 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02004605 if (typesArray == null) {
4606 return true;
4607 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004608 for (String forbiddenType : typesArray) {
4609 if (forbiddenType.equals(accountType)) {
4610 return false;
4611 }
4612 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08004613 return true;
4614 }
4615
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004616 private boolean isProfileOwner(int uid) {
4617 final DevicePolicyManagerInternal dpmi =
4618 LocalServices.getService(DevicePolicyManagerInternal.class);
4619 return (dpmi != null)
4620 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
4621 }
4622
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004623 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07004624 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
4625 throws RemoteException {
4626 final int callingUid = getCallingUid();
4627
Amith Yamasani27db4682013-03-30 17:07:47 -07004628 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07004629 throw new SecurityException();
4630 }
4631
4632 if (value) {
4633 grantAppPermission(account, authTokenType, uid);
4634 } else {
4635 revokeAppPermission(account, authTokenType, uid);
4636 }
4637 }
4638
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004639 /**
4640 * Allow callers with the given uid permission to get credentials for account/authTokenType.
4641 * <p>
4642 * Although this is public it can only be accessed via the AccountManagerService object
4643 * which is in the system. This means we don't need to protect it with permissions.
4644 * @hide
4645 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07004646 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07004647 if (account == null || authTokenType == null) {
4648 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07004649 return;
4650 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07004651 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004652 synchronized (accounts.cacheLock) {
4653 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004654 db.beginTransaction();
4655 try {
4656 long accountId = getAccountIdLocked(db, account);
4657 if (accountId >= 0) {
4658 ContentValues values = new ContentValues();
4659 values.put(GRANTS_ACCOUNTS_ID, accountId);
4660 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
4661 values.put(GRANTS_GRANTEE_UID, uid);
4662 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
4663 db.setTransactionSuccessful();
4664 }
4665 } finally {
4666 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004667 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004668 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4669 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004670 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004671 }
4672
4673 /**
4674 * Don't allow callers with the given uid permission to get credentials for
4675 * account/authTokenType.
4676 * <p>
4677 * Although this is public it can only be accessed via the AccountManagerService object
4678 * which is in the system. This means we don't need to protect it with permissions.
4679 * @hide
4680 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07004681 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07004682 if (account == null || authTokenType == null) {
4683 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07004684 return;
4685 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07004686 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004687 synchronized (accounts.cacheLock) {
4688 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004689 db.beginTransaction();
4690 try {
4691 long accountId = getAccountIdLocked(db, account);
4692 if (accountId >= 0) {
4693 db.delete(TABLE_GRANTS,
4694 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
4695 + GRANTS_GRANTEE_UID + "=?",
4696 new String[]{String.valueOf(accountId), authTokenType,
4697 String.valueOf(uid)});
4698 db.setTransactionSuccessful();
4699 }
4700 } finally {
4701 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004702 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004703 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4704 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004705 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004706 }
Fred Quintana56285a62010-12-02 14:20:51 -08004707
4708 static final private String stringArrayToString(String[] value) {
4709 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
4710 }
4711
Amith Yamasani04e0d262012-02-14 11:50:53 -08004712 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
4713 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004714 if (oldAccountsForType != null) {
4715 ArrayList<Account> newAccountsList = new ArrayList<Account>();
4716 for (Account curAccount : oldAccountsForType) {
4717 if (!curAccount.equals(account)) {
4718 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004719 }
4720 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004721 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004722 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004723 } else {
4724 Account[] newAccountsForType = new Account[newAccountsList.size()];
4725 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004726 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004727 }
Fred Quintana56285a62010-12-02 14:20:51 -08004728 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004729 accounts.userDataCache.remove(account);
4730 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004731 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004732 }
4733
4734 /**
4735 * This assumes that the caller has already checked that the account is not already present.
4736 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08004737 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
4738 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004739 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
4740 Account[] newAccountsForType = new Account[oldLength + 1];
4741 if (accountsForType != null) {
4742 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08004743 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004744 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004745 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08004746 }
4747
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004748 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07004749 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004750 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07004751 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004752 return unfiltered;
4753 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07004754 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004755 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004756 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004757 // If any of the packages is a white listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004758 // otherwise return non-shared accounts only.
4759 // This might be a temporary way to specify a whitelist
4760 String whiteList = mContext.getResources().getString(
4761 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
4762 for (String packageName : packages) {
4763 if (whiteList.contains(";" + packageName + ";")) {
4764 return unfiltered;
4765 }
4766 }
4767 ArrayList<Account> allowed = new ArrayList<Account>();
4768 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
4769 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004770 String requiredAccountType = "";
4771 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07004772 // If there's an explicit callingPackage specified, check if that package
4773 // opted in to see restricted accounts.
4774 if (callingPackage != null) {
4775 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004776 if (pi != null && pi.restrictedAccountType != null) {
4777 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07004778 }
4779 } else {
4780 // Otherwise check if the callingUid has a package that has opted in
4781 for (String packageName : packages) {
4782 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
4783 if (pi != null && pi.restrictedAccountType != null) {
4784 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07004785 break;
4786 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004787 }
4788 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004789 } catch (NameNotFoundException nnfe) {
4790 }
4791 for (Account account : unfiltered) {
4792 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004793 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004794 } else {
4795 boolean found = false;
4796 for (Account shared : sharedAccounts) {
4797 if (shared.equals(account)) {
4798 found = true;
4799 break;
4800 }
4801 }
4802 if (!found) {
4803 allowed.add(account);
4804 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004805 }
4806 }
4807 Account[] filtered = new Account[allowed.size()];
4808 allowed.toArray(filtered);
4809 return filtered;
4810 } else {
4811 return unfiltered;
4812 }
4813 }
4814
Amith Yamasani27db4682013-03-30 17:07:47 -07004815 /*
4816 * packageName can be null. If not null, it should be used to filter out restricted accounts
4817 * that the package is not allowed to access.
4818 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004819 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07004820 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004821 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004822 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004823 if (accounts == null) {
4824 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08004825 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004826 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07004827 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08004828 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004829 } else {
4830 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004831 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004832 totalLength += accounts.length;
4833 }
4834 if (totalLength == 0) {
4835 return EMPTY_ACCOUNT_ARRAY;
4836 }
4837 Account[] accounts = new Account[totalLength];
4838 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004839 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004840 System.arraycopy(accountsOfType, 0, accounts, totalLength,
4841 accountsOfType.length);
4842 totalLength += accountsOfType.length;
4843 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004844 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08004845 }
4846 }
4847
Amith Yamasani04e0d262012-02-14 11:50:53 -08004848 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4849 Account account, String key, String value) {
4850 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004851 if (userDataForAccount == null) {
4852 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004853 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004854 }
4855 if (value == null) {
4856 userDataForAccount.remove(key);
4857 } else {
4858 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004859 }
4860 }
4861
Carlos Valdivia91979be2015-05-22 14:11:35 -07004862 protected String readCachedTokenInternal(
4863 UserAccounts accounts,
4864 Account account,
4865 String tokenType,
4866 String callingPackage,
4867 byte[] pkgSigDigest) {
4868 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004869 return accounts.accountTokenCaches.get(
4870 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004871 }
4872 }
4873
Amith Yamasani04e0d262012-02-14 11:50:53 -08004874 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4875 Account account, String key, String value) {
4876 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004877 if (authTokensForAccount == null) {
4878 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004879 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004880 }
4881 if (value == null) {
4882 authTokensForAccount.remove(key);
4883 } else {
4884 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004885 }
4886 }
4887
Amith Yamasani04e0d262012-02-14 11:50:53 -08004888 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
4889 String authTokenType) {
4890 synchronized (accounts.cacheLock) {
4891 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004892 if (authTokensForAccount == null) {
4893 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08004894 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004895 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004896 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004897 }
4898 return authTokensForAccount.get(authTokenType);
4899 }
4900 }
4901
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00004902 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
4903 synchronized (accounts.cacheLock) {
4904 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
4905 if (userDataForAccount == null) {
4906 // need to populate the cache for this account
4907 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4908 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
4909 accounts.userDataCache.put(account, userDataForAccount);
4910 }
4911 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08004912 }
4913 }
4914
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004915 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
4916 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004917 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004918 Cursor cursor = db.query(TABLE_EXTRAS,
4919 COLUMNS_EXTRAS_KEY_AND_VALUE,
4920 SELECTION_USERDATA_BY_ACCOUNT,
4921 new String[]{account.name, account.type},
4922 null, null, null);
4923 try {
4924 while (cursor.moveToNext()) {
4925 final String tmpkey = cursor.getString(0);
4926 final String value = cursor.getString(1);
4927 userDataForAccount.put(tmpkey, value);
4928 }
4929 } finally {
4930 cursor.close();
4931 }
4932 return userDataForAccount;
4933 }
4934
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004935 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
4936 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004937 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004938 Cursor cursor = db.query(TABLE_AUTHTOKENS,
4939 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
4940 SELECTION_AUTHTOKENS_BY_ACCOUNT,
4941 new String[]{account.name, account.type},
4942 null, null, null);
4943 try {
4944 while (cursor.moveToNext()) {
4945 final String type = cursor.getString(0);
4946 final String authToken = cursor.getString(1);
4947 authTokensForAccount.put(type, authToken);
4948 }
4949 } finally {
4950 cursor.close();
4951 }
4952 return authTokensForAccount;
4953 }
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004954
4955 private Context getContextForUser(UserHandle user) {
4956 try {
4957 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4958 } catch (NameNotFoundException e) {
4959 // Default to mContext, not finding the package system is running as is unlikely.
4960 return mContext;
4961 }
4962 }
Sandra Kwan78812282015-11-04 11:19:47 -08004963
4964 private void sendResponse(IAccountManagerResponse response, Bundle result) {
4965 try {
4966 response.onResult(result);
4967 } catch (RemoteException e) {
4968 // if the caller is dead then there is no one to care about remote
4969 // exceptions
4970 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4971 Log.v(TAG, "failure while notifying response", e);
4972 }
4973 }
4974 }
4975
4976 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
4977 String errorMessage) {
4978 try {
4979 response.onError(errorCode, errorMessage);
4980 } catch (RemoteException e) {
4981 // if the caller is dead then there is no one to care about remote
4982 // exceptions
4983 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4984 Log.v(TAG, "failure while notifying response", e);
4985 }
4986 }
4987 }
Fred Quintana60307342009-03-24 22:48:12 -07004988}