blob: a63faf1b301182e2c328b157023b2e85bb79d32b [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;
Doug Zongker885cfc232009-10-21 16:52:44 -070050import android.content.pm.ApplicationInfo;
51import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070052import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070053import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070054import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070055import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070056import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070057import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070058import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070059import android.database.Cursor;
60import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070061import android.database.sqlite.SQLiteDatabase;
62import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070063import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070064import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070065import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080066import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070067import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070068import android.os.IBinder;
69import android.os.Looper;
70import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070071import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070072import android.os.Process;
Fred Quintanaa698f422009-04-08 19:14:54 -070073import android.os.RemoteException;
74import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070075import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070076import android.os.UserManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070077import android.text.TextUtils;
78import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070079import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070080import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080081import android.util.SparseArray;
Fred Quintana60307342009-03-24 22:48:12 -070082
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070083import com.android.internal.R;
Amith Yamasani67df64b2012-12-14 12:09:36 -080084import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080085import com.android.internal.util.IndentingPrintWriter;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070086import com.android.server.FgThread;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000087import com.android.server.LocalServices;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070088import com.google.android.collect.Lists;
89import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070090
Oscar Montemayora8529f62009-11-18 10:14:20 -080091import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -070092import java.io.FileDescriptor;
93import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -080094import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -070095import java.security.MessageDigest;
96import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070097import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -070098import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -080099import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700100import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700101import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700102import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700103import java.util.HashSet;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800104import java.util.Iterator;
Fred Quintana56285a62010-12-02 14:20:51 -0800105import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700106import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800107import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800108import java.util.Map.Entry;
109import java.util.Set;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700110import java.util.concurrent.atomic.AtomicInteger;
111import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700112
Fred Quintana60307342009-03-24 22:48:12 -0700113/**
114 * A system service that provides account, password, and authtoken management for all
115 * accounts on the device. Some of these calls are implemented with the help of the corresponding
116 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
117 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700118 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700119 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700120 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700121public class AccountManagerService
122 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800123 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Sandra Kwan78812282015-11-04 11:19:47 -0800124
Fred Quintana60307342009-03-24 22:48:12 -0700125 private static final String TAG = "AccountManagerService";
126
Fred Quintana60307342009-03-24 22:48:12 -0700127 private static final String DATABASE_NAME = "accounts.db";
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800128 private static final int DATABASE_VERSION = 9;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700129
130 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700131
132 private final Context mContext;
133
Fred Quintana56285a62010-12-02 14:20:51 -0800134 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700135 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700136 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800137
Fred Quintana60307342009-03-24 22:48:12 -0700138 private final MessageHandler mMessageHandler;
139
140 // Messages that can be sent on mHandler
141 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700142 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700143
Fred Quintana56285a62010-12-02 14:20:51 -0800144 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700145
146 private static final String TABLE_ACCOUNTS = "accounts";
147 private static final String ACCOUNTS_ID = "_id";
148 private static final String ACCOUNTS_NAME = "name";
149 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700150 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700151 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700152 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800153 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
154 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700155
156 private static final String TABLE_AUTHTOKENS = "authtokens";
157 private static final String AUTHTOKENS_ID = "_id";
158 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
159 private static final String AUTHTOKENS_TYPE = "type";
160 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
161
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700162 private static final String TABLE_GRANTS = "grants";
163 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
164 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
165 private static final String GRANTS_GRANTEE_UID = "uid";
166
Fred Quintana60307342009-03-24 22:48:12 -0700167 private static final String TABLE_EXTRAS = "extras";
168 private static final String EXTRAS_ID = "_id";
169 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
170 private static final String EXTRAS_KEY = "key";
171 private static final String EXTRAS_VALUE = "value";
172
173 private static final String TABLE_META = "meta";
174 private static final String META_KEY = "key";
175 private static final String META_VALUE = "value";
176
Amith Yamasani67df64b2012-12-14 12:09:36 -0800177 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
178
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700179 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
180 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700181 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800182
Carlos Valdivia91979be2015-05-22 14:11:35 -0700183 static {
184 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
185 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
186 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700187
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700188 private static final String COUNT_OF_MATCHING_GRANTS = ""
189 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
190 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
191 + " AND " + GRANTS_GRANTEE_UID + "=?"
192 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
193 + " AND " + ACCOUNTS_NAME + "=?"
194 + " AND " + ACCOUNTS_TYPE + "=?";
195
Fred Quintana56285a62010-12-02 14:20:51 -0800196 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
197 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700198
Fred Quintana56285a62010-12-02 14:20:51 -0800199 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
200 AUTHTOKENS_AUTHTOKEN};
201
202 private static final String SELECTION_USERDATA_BY_ACCOUNT =
203 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
204 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
205
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800206 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
207 "auth_uid_for_type:";
208 private static final String META_KEY_DELIMITER = ":";
209 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
210
Fred Quintanaa698f422009-04-08 19:14:54 -0700211 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700212 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
213
Amith Yamasani04e0d262012-02-14 11:50:53 -0800214 static class UserAccounts {
215 private final int userId;
216 private final DatabaseHelper openHelper;
217 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
218 credentialsPermissionNotificationIds =
219 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
220 private final HashMap<Account, Integer> signinRequiredNotificationIds =
221 new HashMap<Account, Integer>();
222 private final Object cacheLock = new Object();
223 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700224 private final HashMap<String, Account[]> accountCache =
225 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800226 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800227 private final HashMap<Account, HashMap<String, String>> userDataCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800228 new HashMap<Account, HashMap<String, String>>();
229 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800230 private final HashMap<Account, HashMap<String, String>> authTokenCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800231 new HashMap<Account, HashMap<String, String>>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700232
233 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700234 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700235
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700236 /**
237 * protected by the {@link #cacheLock}
238 *
239 * Caches the previous names associated with an account. Previous names
240 * should be cached because we expect that when an Account is renamed,
241 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
242 * want to know if the accounts they care about have been renamed.
243 *
244 * The previous names are wrapped in an {@link AtomicReference} so that
245 * we can distinguish between those accounts with no previous names and
246 * those whose previous names haven't been cached (yet).
247 */
248 private final HashMap<Account, AtomicReference<String>> previousNameCache =
249 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800250
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700251 private int debugDbInsertionPoint = -1;
252 private SQLiteStatement statementForLogging;
253
Amith Yamasani04e0d262012-02-14 11:50:53 -0800254 UserAccounts(Context context, int userId) {
255 this.userId = userId;
256 synchronized (cacheLock) {
257 openHelper = new DatabaseHelper(context, userId);
258 }
259 }
260 }
261
262 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
263
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700264 private static AtomicReference<AccountManagerService> sThis =
265 new AtomicReference<AccountManagerService>();
Fred Quintana31957f12009-10-21 13:43:10 -0700266 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700267
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700268 /**
269 * This should only be called by system code. One should only call this after the service
270 * has started.
271 * @return a reference to the AccountManagerService instance
272 * @hide
273 */
274 public static AccountManagerService getSingleton() {
275 return sThis.get();
276 }
Fred Quintana60307342009-03-24 22:48:12 -0700277
Fred Quintana56285a62010-12-02 14:20:51 -0800278 public AccountManagerService(Context context) {
279 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700280 }
281
Fred Quintana56285a62010-12-02 14:20:51 -0800282 public AccountManagerService(Context context, PackageManager packageManager,
283 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700284 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800285 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700286 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700287
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700288 mMessageHandler = new MessageHandler(FgThread.get().getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700289
Fred Quintana56285a62010-12-02 14:20:51 -0800290 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800291 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700292
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700293 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800294
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800295 IntentFilter intentFilter = new IntentFilter();
296 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
297 intentFilter.addDataScheme("package");
298 mContext.registerReceiver(new BroadcastReceiver() {
299 @Override
300 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700301 // Don't delete accounts when updating a authenticator's
302 // package.
303 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700304 /* Purging data requires file io, don't block the main thread. This is probably
305 * less than ideal because we are introducing a race condition where old grants
306 * could be exercised until they are purged. But that race condition existed
307 * anyway with the broadcast receiver.
308 *
309 * Ideally, we would completely clear the cache, purge data from the database,
310 * and then rebuild the cache. All under the cache lock. But that change is too
311 * large at this point.
312 */
313 Runnable r = new Runnable() {
314 @Override
315 public void run() {
316 purgeOldGrantsAll();
317 }
318 };
319 new Thread(r).start();
Carlos Valdivia23f58262014-09-05 10:52:41 -0700320 }
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800321 }
322 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800323
Amith Yamasani13593602012-03-22 16:16:17 -0700324 IntentFilter userFilter = new IntentFilter();
325 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800326 userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800327 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700328 @Override
329 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800330 String action = intent.getAction();
331 if (Intent.ACTION_USER_REMOVED.equals(action)) {
332 onUserRemoved(intent);
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800333 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
334 onUserUnlocked(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800335 }
Amith Yamasani13593602012-03-22 16:16:17 -0700336 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800337 }, UserHandle.ALL, userFilter, null, null);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800338 }
339
Dianne Hackborn164371f2013-10-01 19:10:13 -0700340 @Override
341 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
342 throws RemoteException {
343 try {
344 return super.onTransact(code, data, reply, flags);
345 } catch (RuntimeException e) {
346 // The account manager only throws security exceptions, so let's
347 // log all others.
348 if (!(e instanceof SecurityException)) {
349 Slog.wtf(TAG, "Account Manager Crash", e);
350 }
351 throw e;
352 }
353 }
354
Kenny Root26ff6622012-07-30 12:58:03 -0700355 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -0700356 }
357
Amith Yamasani258848d2012-08-10 17:06:33 -0700358 private UserManager getUserManager() {
359 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700360 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700361 }
362 return mUserManager;
363 }
364
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700365 /**
366 * Validate internal set of accounts against installed authenticators for
367 * given user. Clears cached authenticators before validating.
368 */
369 public void validateAccounts(int userId) {
370 final UserAccounts accounts = getUserAccounts(userId);
371
372 // Invalidate user-specific cache to make sure we catch any
373 // removed authenticators.
374 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
375 }
376
377 /**
378 * Validate internal set of accounts against installed authenticators for
379 * given user. Clear cached authenticators before validating when requested.
380 */
381 private void validateAccountsInternal(
382 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
383 if (invalidateAuthenticatorCache) {
384 mAuthenticatorCache.invalidateCache(accounts.userId);
385 }
386
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800387 final HashMap<String, Integer> knownAuth = new HashMap<>();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700388 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
389 mAuthenticatorCache.getAllServices(accounts.userId)) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800390 knownAuth.put(service.type.type, service.uid);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700391 }
392
Amith Yamasani04e0d262012-02-14 11:50:53 -0800393 synchronized (accounts.cacheLock) {
394 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800395 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800396
397 // Get a list of stored authenticator type and UID
398 Cursor metaCursor = db.query(
399 TABLE_META,
400 new String[] {META_KEY, META_VALUE},
401 SELECTION_META_BY_AUTHENTICATOR_TYPE,
402 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
403 null /* groupBy */,
404 null /* having */,
405 META_KEY);
406 // Create a list of authenticator type whose previous uid no longer exists
407 HashSet<String> obsoleteAuthType = Sets.newHashSet();
408 try {
409 while (metaCursor.moveToNext()) {
410 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
411 String uid = metaCursor.getString(1);
412 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uid)) {
413 // Should never happen.
414 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
415 + ", uid empty: " + TextUtils.isEmpty(uid));
416 continue;
417 }
418 Integer knownUid = knownAuth.get(type);
419 if (knownUid != null && uid.equals(knownUid.toString())) {
420 // Remove it from the knownAuth list if it's unchanged.
421 knownAuth.remove(type);
422 } else {
423 // Only add it to the list if it no longer exists or uid different
424 obsoleteAuthType.add(type);
425 // And delete it from the TABLE_META
426 db.delete(
427 TABLE_META,
428 META_KEY + "=? AND " + META_VALUE + "=?",
429 new String[] {
430 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
431 uid}
432 );
433 }
434 }
435 } finally {
436 metaCursor.close();
437 }
438
439 // Add the newly registered authenticator to TABLE_META
440 Iterator<Entry<String, Integer>> iterator = knownAuth.entrySet().iterator();
441 while (iterator.hasNext()) {
442 Entry<String, Integer> entry = iterator.next();
443 ContentValues values = new ContentValues();
444 values.put(META_KEY,
445 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
446 values.put(META_VALUE, entry.getValue());
447 db.insert(TABLE_META, null, values);
448 }
449
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800450 Cursor cursor = db.query(TABLE_ACCOUNTS,
451 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
Marvin Paul48fcd4e2014-12-01 18:26:07 -0800452 null, null, null, null, ACCOUNTS_ID);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800453 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800454 accounts.accountCache.clear();
Fred Quintana56285a62010-12-02 14:20:51 -0800455 final HashMap<String, ArrayList<String>> accountNamesByType =
Amith Yamasanib483a992012-05-22 13:14:25 -0700456 new LinkedHashMap<String, ArrayList<String>>();
Fred Quintana56285a62010-12-02 14:20:51 -0800457 while (cursor.moveToNext()) {
458 final long accountId = cursor.getLong(0);
459 final String accountType = cursor.getString(1);
460 final String accountName = cursor.getString(2);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700461
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800462 if (obsoleteAuthType.contains(accountType)) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700463 Slog.w(TAG, "deleting account " + accountName + " because type "
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800464 + accountType + "'s registered authenticator no longer exist.");
Fred Quintana56285a62010-12-02 14:20:51 -0800465 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
466 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700467
468 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
469 accountId, accounts);
470
Fred Quintana56285a62010-12-02 14:20:51 -0800471 final Account account = new Account(accountName, accountType);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800472 accounts.userDataCache.remove(account);
473 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -0700474 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -0800475 } else {
476 ArrayList<String> accountNames = accountNamesByType.get(accountType);
477 if (accountNames == null) {
478 accountNames = new ArrayList<String>();
479 accountNamesByType.put(accountType, accountNames);
480 }
481 accountNames.add(accountName);
482 }
483 }
Andy McFadden2f362292012-01-20 14:43:38 -0800484 for (Map.Entry<String, ArrayList<String>> cur
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800485 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800486 final String accountType = cur.getKey();
487 final ArrayList<String> accountNames = cur.getValue();
488 final Account[] accountsForType = new Account[accountNames.size()];
489 int i = 0;
490 for (String accountName : accountNames) {
491 accountsForType[i] = new Account(accountName, accountType);
492 ++i;
493 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800494 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800495 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800496 } finally {
497 cursor.close();
498 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800499 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800500 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800501 }
502 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700503 }
504
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800505 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
506 Context context,
507 int userId) {
508 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
509 HashMap<String, Integer> knownAuth = new HashMap<>();
510 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
511 .getAllServices(userId)) {
512 knownAuth.put(service.type.type, service.uid);
513 }
514 return knownAuth;
515 }
516
Amith Yamasani04e0d262012-02-14 11:50:53 -0800517 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700518 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -0800519 }
520
521 protected UserAccounts getUserAccounts(int userId) {
522 synchronized (mUsers) {
523 UserAccounts accounts = mUsers.get(userId);
524 if (accounts == null) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700525 accounts = new UserAccounts(mContext, userId);
526 initializeDebugDbSizeAndCompileSqlStatementForLogging(
527 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800528 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700529 purgeOldGrants(accounts);
530 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800531 }
532 return accounts;
533 }
534 }
535
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700536 private void purgeOldGrantsAll() {
537 synchronized (mUsers) {
538 for (int i = 0; i < mUsers.size(); i++) {
539 purgeOldGrants(mUsers.valueAt(i));
540 }
541 }
542 }
543
544 private void purgeOldGrants(UserAccounts accounts) {
545 synchronized (accounts.cacheLock) {
546 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
547 final Cursor cursor = db.query(TABLE_GRANTS,
548 new String[]{GRANTS_GRANTEE_UID},
549 null, null, GRANTS_GRANTEE_UID, null, null);
550 try {
551 while (cursor.moveToNext()) {
552 final int uid = cursor.getInt(0);
553 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
554 if (packageExists) {
555 continue;
556 }
557 Log.d(TAG, "deleting grants for UID " + uid
558 + " because its package is no longer installed");
559 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
560 new String[]{Integer.toString(uid)});
561 }
562 } finally {
563 cursor.close();
564 }
565 }
566 }
567
Amith Yamasani13593602012-03-22 16:16:17 -0700568 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700569 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -0700570 if (userId < 1) return;
571
572 UserAccounts accounts;
573 synchronized (mUsers) {
574 accounts = mUsers.get(userId);
575 mUsers.remove(userId);
576 }
577 if (accounts == null) {
578 File dbFile = new File(getDatabaseName(userId));
579 dbFile.delete();
580 return;
581 }
582
583 synchronized (accounts.cacheLock) {
584 accounts.openHelper.close();
585 File dbFile = new File(getDatabaseName(userId));
586 dbFile.delete();
587 }
588 }
589
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800590 private void onUserUnlocked(Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800591 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
592 if (userId < 1) return;
593
594 // Check if there's a shared account that needs to be created as an account
595 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
596 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700597 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700598 int parentUserId = UserManager.isSplitSystemUser()
599 ? mUserManager.getUserInfo(userId).restrictedProfileParentId
600 : UserHandle.USER_SYSTEM;
601 if (parentUserId < 0) {
602 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
603 return;
604 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800605 for (Account sa : sharedAccounts) {
606 if (ArrayUtils.contains(accounts, sa)) continue;
607 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700608 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800609 }
610 }
611
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700612 @Override
613 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700614 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -0700615 }
616
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800617 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -0700618 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700619 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800620 if (Log.isLoggable(TAG, Log.VERBOSE)) {
621 Log.v(TAG, "getPassword: " + account
622 + ", caller's uid " + Binder.getCallingUid()
623 + ", pid " + Binder.getCallingPid());
624 }
Fred Quintana382601f2010-03-25 12:25:10 -0700625 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000626 int userId = UserHandle.getCallingUserId();
627 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700628 String msg = String.format(
629 "uid %s cannot get secrets for accounts of type: %s",
630 callingUid,
631 account.type);
632 throw new SecurityException(msg);
633 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700634 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700635 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700636 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800637 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700638 } finally {
639 restoreCallingIdentity(identityToken);
640 }
641 }
642
Amith Yamasani04e0d262012-02-14 11:50:53 -0800643 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700644 if (account == null) {
645 return null;
646 }
647
Amith Yamasani04e0d262012-02-14 11:50:53 -0800648 synchronized (accounts.cacheLock) {
649 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800650 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
651 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
652 new String[]{account.name, account.type}, null, null, null);
653 try {
654 if (cursor.moveToNext()) {
655 return cursor.getString(0);
656 }
657 return null;
658 } finally {
659 cursor.close();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700660 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700661 }
662 }
663
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800664 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700665 public String getPreviousName(Account account) {
666 if (Log.isLoggable(TAG, Log.VERBOSE)) {
667 Log.v(TAG, "getPreviousName: " + account
668 + ", caller's uid " + Binder.getCallingUid()
669 + ", pid " + Binder.getCallingPid());
670 }
671 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700672 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700673 long identityToken = clearCallingIdentity();
674 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700675 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700676 return readPreviousNameInternal(accounts, account);
677 } finally {
678 restoreCallingIdentity(identityToken);
679 }
680 }
681
682 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
683 if (account == null) {
684 return null;
685 }
686 synchronized (accounts.cacheLock) {
687 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
688 if (previousNameRef == null) {
689 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
690 Cursor cursor = db.query(
691 TABLE_ACCOUNTS,
692 new String[]{ ACCOUNTS_PREVIOUS_NAME },
693 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
694 new String[] { account.name, account.type },
695 null,
696 null,
697 null);
698 try {
699 if (cursor.moveToNext()) {
700 String previousName = cursor.getString(0);
701 previousNameRef = new AtomicReference<String>(previousName);
702 accounts.previousNameCache.put(account, previousNameRef);
703 return previousName;
704 } else {
705 return null;
706 }
707 } finally {
708 cursor.close();
709 }
710 } else {
711 return previousNameRef.get();
712 }
713 }
714 }
715
716 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700717 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700718 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800719 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700720 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
721 account, key, callingUid, Binder.getCallingPid());
722 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -0800723 }
Fred Quintana382601f2010-03-25 12:25:10 -0700724 if (account == null) throw new IllegalArgumentException("account is null");
725 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000726 int userId = UserHandle.getCallingUserId();
727 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700728 String msg = String.format(
729 "uid %s cannot get user data for accounts of type: %s",
730 callingUid,
731 account.type);
732 throw new SecurityException(msg);
733 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700734 long identityToken = clearCallingIdentity();
735 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700736 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +0000737 return readUserDataInternal(accounts, account, key);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700738 } finally {
739 restoreCallingIdentity(identityToken);
740 }
741 }
742
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800743 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100744 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700745 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800746 if (Log.isLoggable(TAG, Log.VERBOSE)) {
747 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100748 + "for user id " + userId
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700749 + "caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -0800750 + ", pid " + Binder.getCallingPid());
751 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100752 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700753 if (isCrossUser(callingUid, userId)) {
754 throw new SecurityException(
755 String.format(
756 "User %s tying to get authenticator types for %s" ,
757 UserHandle.getCallingUserId(),
758 userId));
759 }
760
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700761 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700762 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000763 return getAuthenticatorTypesInternal(userId);
764
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700765 } finally {
766 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700767 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700768 }
769
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000770 /**
771 * Should only be called inside of a clearCallingIdentity block.
772 */
773 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
774 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
775 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
776 AuthenticatorDescription[] types =
777 new AuthenticatorDescription[authenticatorCollection.size()];
778 int i = 0;
779 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
780 : authenticatorCollection) {
781 types[i] = authenticator.type;
782 i++;
783 }
784 return types;
785 }
786
787
788
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700789 private boolean isCrossUser(int callingUid, int userId) {
790 return (userId != UserHandle.getCallingUserId()
791 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100792 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700793 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
794 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100795 }
796
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700797 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -0700798 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700799 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800800 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700801 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700802 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -0800803 + ", pid " + Binder.getCallingPid());
804 }
Fred Quintana382601f2010-03-25 12:25:10 -0700805 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000806 int userId = UserHandle.getCallingUserId();
807 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700808 String msg = String.format(
809 "uid %s cannot explicitly add accounts of type: %s",
810 callingUid,
811 account.type);
812 throw new SecurityException(msg);
813 }
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700814 /*
815 * Child users are not allowed to add accounts. Only the accounts that are
816 * shared by the parent profile can be added to child profile.
817 *
818 * TODO: Only allow accounts that were shared to be added by
819 * a limited user.
820 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700821
Fred Quintana60307342009-03-24 22:48:12 -0700822 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700823 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700824 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700825 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000826 return addAccountInternal(accounts, account, password, extras, false, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -0700827 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700828 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700829 }
830 }
831
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000832 @Override
833 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700834 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700835 int callingUid = Binder.getCallingUid();
836 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
837 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000838 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700839 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800840 final UserAccounts fromAccounts = getUserAccounts(userFrom);
841 final UserAccounts toAccounts = getUserAccounts(userTo);
842 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000843 if (response != null) {
844 Bundle result = new Bundle();
845 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
846 try {
847 response.onResult(result);
848 } catch (RemoteException e) {
849 Slog.w(TAG, "Failed to report error back to the client." + e);
850 }
851 }
852 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800853 }
854
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000855 Slog.d(TAG, "Copying account " + account.name
856 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800857 long identityToken = clearCallingIdentity();
858 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000859 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800860 false /* stripAuthTokenFromResult */, account.name,
861 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700862 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800863 protected String toDebugString(long now) {
864 return super.toDebugString(now) + ", getAccountCredentialsForClone"
865 + ", " + account.type;
866 }
867
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700868 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800869 public void run() throws RemoteException {
870 mAuthenticator.getAccountCredentialsForCloning(this, account);
871 }
872
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700873 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800874 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000875 if (result != null
876 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
877 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700878 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800879 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800880 super.onResult(result);
881 }
882 }
883 }.bind();
884 } finally {
885 restoreCallingIdentity(identityToken);
886 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800887 }
888
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800889 @Override
890 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700891 final int callingUid = Binder.getCallingUid();
892 if (Log.isLoggable(TAG, Log.VERBOSE)) {
893 String msg = String.format(
894 "accountAuthenticated( account: %s, callerUid: %s)",
895 account,
896 callingUid);
897 Log.v(TAG, msg);
898 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800899 if (account == null) {
900 throw new IllegalArgumentException("account is null");
901 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000902 int userId = UserHandle.getCallingUserId();
903 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700904 String msg = String.format(
905 "uid %s cannot notify authentication for accounts of type: %s",
906 callingUid,
907 account.type);
908 throw new SecurityException(msg);
909 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000910
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000911 if (!canUserModifyAccounts(userId, callingUid) ||
912 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800913 return false;
914 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000915
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700916 long identityToken = clearCallingIdentity();
917 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000918 UserAccounts accounts = getUserAccounts(userId);
919 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700920 } finally {
921 restoreCallingIdentity(identityToken);
922 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -0700923 }
924
925 private boolean updateLastAuthenticatedTime(Account account) {
926 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800927 synchronized (accounts.cacheLock) {
928 final ContentValues values = new ContentValues();
929 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
930 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
931 int i = db.update(
932 TABLE_ACCOUNTS,
933 values,
934 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
935 new String[] {
936 account.name, account.type
937 });
938 if (i > 0) {
939 return true;
940 }
941 }
942 return false;
943 }
944
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000945 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700946 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
947 final int parentUserId){
Amith Yamasani67df64b2012-12-14 12:09:36 -0800948 long id = clearCallingIdentity();
949 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000950 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800951 false /* stripAuthTokenFromResult */, account.name,
952 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700953 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800954 protected String toDebugString(long now) {
955 return super.toDebugString(now) + ", getAccountCredentialsForClone"
956 + ", " + account.type;
957 }
958
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700959 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800960 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700961 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700962 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700963 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -0700964 for (Account acc : getAccounts(parentUserId,
965 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700966 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000967 mAuthenticator.addAccountFromCredentials(
968 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700969 break;
970 }
971 }
972 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800973 }
974
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700975 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800976 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000977 // TODO: Anything to do if if succedded?
978 // TODO: If it failed: Show error notification? Should we remove the shadow
979 // account to avoid retries?
980 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800981 }
982
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700983 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800984 public void onError(int errorCode, String errorMessage) {
985 super.onError(errorCode, errorMessage);
986 // TODO: Show error notification to user
987 // TODO: Should we remove the shadow account so that it doesn't keep trying?
988 }
989
990 }.bind();
991 } finally {
992 restoreCallingIdentity(id);
993 }
994 }
995
Amith Yamasani04e0d262012-02-14 11:50:53 -0800996 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700997 Bundle extras, boolean restricted, int callingUid) {
Fred Quintana743dfad2010-07-15 10:59:25 -0700998 if (account == null) {
999 return false;
1000 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001001 synchronized (accounts.cacheLock) {
1002 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001003 db.beginTransaction();
1004 try {
1005 long numMatches = DatabaseUtils.longForQuery(db,
1006 "select count(*) from " + TABLE_ACCOUNTS
1007 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1008 new String[]{account.name, account.type});
1009 if (numMatches > 0) {
1010 Log.w(TAG, "insertAccountIntoDatabase: " + account
1011 + ", skipping since the account already exists");
1012 return false;
1013 }
1014 ContentValues values = new ContentValues();
1015 values.put(ACCOUNTS_NAME, account.name);
1016 values.put(ACCOUNTS_TYPE, account.type);
1017 values.put(ACCOUNTS_PASSWORD, password);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001018 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001019 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
1020 if (accountId < 0) {
1021 Log.w(TAG, "insertAccountIntoDatabase: " + account
1022 + ", skipping the DB insert failed");
1023 return false;
1024 }
1025 if (extras != null) {
1026 for (String key : extras.keySet()) {
1027 final String value = extras.getString(key);
1028 if (insertExtraLocked(db, accountId, key, value) < 0) {
1029 Log.w(TAG, "insertAccountIntoDatabase: " + account
1030 + ", skipping since insertExtra failed for key " + key);
1031 return false;
1032 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001033 }
1034 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001035 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001036
1037 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1038 accounts, callingUid);
1039
Amith Yamasani04e0d262012-02-14 11:50:53 -08001040 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001041 } finally {
1042 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001043 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001044 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001045 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001046 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1047 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001048 }
1049 return true;
1050 }
1051
1052 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001053 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001054 * running, then clone the account too.
1055 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001056 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001057 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001058 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001059 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001060 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001061 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001062 addSharedAccountAsUser(account, user.id);
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001063 if (mUserManager.isUserUnlocked(user.id)) {
1064 mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
1065 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001066 }
1067 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001068 }
1069 }
1070
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001071 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -07001072 ContentValues values = new ContentValues();
1073 values.put(EXTRAS_KEY, key);
1074 values.put(EXTRAS_ACCOUNTS_ID, accountId);
1075 values.put(EXTRAS_VALUE, value);
1076 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
1077 }
1078
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001079 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001080 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001081 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001082 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001083 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1084 Log.v(TAG, "hasFeatures: " + account
1085 + ", response " + response
1086 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001087 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001088 + ", pid " + Binder.getCallingPid());
1089 }
Fred Quintana382601f2010-03-25 12:25:10 -07001090 if (response == null) throw new IllegalArgumentException("response is null");
1091 if (account == null) throw new IllegalArgumentException("account is null");
1092 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001093 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001094 checkReadAccountsPermitted(callingUid, account.type, userId,
1095 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001096
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001097 long identityToken = clearCallingIdentity();
1098 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001099 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001100 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001101 } finally {
1102 restoreCallingIdentity(identityToken);
1103 }
1104 }
1105
1106 private class TestFeaturesSession extends Session {
1107 private final String[] mFeatures;
1108 private final Account mAccount;
1109
Amith Yamasani04e0d262012-02-14 11:50:53 -08001110 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001111 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001112 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001113 true /* stripAuthTokenFromResult */, account.name,
1114 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001115 mFeatures = features;
1116 mAccount = account;
1117 }
1118
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001119 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001120 public void run() throws RemoteException {
1121 try {
1122 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1123 } catch (RemoteException e) {
1124 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1125 }
1126 }
1127
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001128 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001129 public void onResult(Bundle result) {
1130 IAccountManagerResponse response = getResponseAndClose();
1131 if (response != null) {
1132 try {
1133 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001134 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001135 return;
1136 }
Fred Quintana56285a62010-12-02 14:20:51 -08001137 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1138 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1139 + response);
1140 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001141 final Bundle newResult = new Bundle();
1142 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1143 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1144 response.onResult(newResult);
1145 } catch (RemoteException e) {
1146 // if the caller is dead then there is no one to care about remote exceptions
1147 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1148 Log.v(TAG, "failure while notifying response", e);
1149 }
1150 }
1151 }
1152 }
1153
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001154 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001155 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001156 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001157 + ", " + mAccount
1158 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1159 }
1160 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001161
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001162 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001163 public void renameAccount(
1164 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001165 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001166 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1167 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001168 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001169 + ", pid " + Binder.getCallingPid());
1170 }
1171 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001172 int userId = UserHandle.getCallingUserId();
1173 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001174 String msg = String.format(
1175 "uid %s cannot rename accounts of type: %s",
1176 callingUid,
1177 accountToRename.type);
1178 throw new SecurityException(msg);
1179 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001180 long identityToken = clearCallingIdentity();
1181 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001182 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001183 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001184 Bundle result = new Bundle();
1185 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1186 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1187 try {
1188 response.onResult(result);
1189 } catch (RemoteException e) {
1190 Log.w(TAG, e.getMessage());
1191 }
1192 } finally {
1193 restoreCallingIdentity(identityToken);
1194 }
1195 }
1196
1197 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001198 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001199 Account resultAccount = null;
1200 /*
1201 * Cancel existing notifications. Let authenticators
1202 * re-post notifications as required. But we don't know if
1203 * the authenticators have bound their notifications to
1204 * now stale account name data.
1205 *
1206 * With a rename api, we might not need to do this anymore but it
1207 * shouldn't hurt.
1208 */
1209 cancelNotification(
1210 getSigninRequiredNotificationId(accounts, accountToRename),
1211 new UserHandle(accounts.userId));
1212 synchronized(accounts.credentialsPermissionNotificationIds) {
1213 for (Pair<Pair<Account, String>, Integer> pair:
1214 accounts.credentialsPermissionNotificationIds.keySet()) {
1215 if (accountToRename.equals(pair.first.first)) {
1216 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1217 cancelNotification(id, new UserHandle(accounts.userId));
1218 }
1219 }
1220 }
1221 synchronized (accounts.cacheLock) {
1222 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1223 db.beginTransaction();
1224 boolean isSuccessful = false;
1225 Account renamedAccount = new Account(newName, accountToRename.type);
1226 try {
1227 final ContentValues values = new ContentValues();
1228 values.put(ACCOUNTS_NAME, newName);
1229 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1230 final long accountId = getAccountIdLocked(db, accountToRename);
1231 if (accountId >= 0) {
1232 final String[] argsAccountId = { String.valueOf(accountId) };
1233 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1234 db.setTransactionSuccessful();
1235 isSuccessful = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001236 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1237 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001238 }
1239 } finally {
1240 db.endTransaction();
1241 if (isSuccessful) {
1242 /*
1243 * Database transaction was successful. Clean up cached
1244 * data associated with the account in the user profile.
1245 */
1246 insertAccountIntoCacheLocked(accounts, renamedAccount);
1247 /*
1248 * Extract the data and token caches before removing the
1249 * old account to preserve the user data associated with
1250 * the account.
1251 */
1252 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1253 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1254 removeAccountFromCacheLocked(accounts, accountToRename);
1255 /*
1256 * Update the cached data associated with the renamed
1257 * account.
1258 */
1259 accounts.userDataCache.put(renamedAccount, tmpData);
1260 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1261 accounts.previousNameCache.put(
1262 renamedAccount,
1263 new AtomicReference<String>(accountToRename.name));
1264 resultAccount = renamedAccount;
1265
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001266 int parentUserId = accounts.userId;
1267 if (canHaveProfile(parentUserId)) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001268 /*
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001269 * Owner or system user account was renamed, rename the account for
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001270 * those users with which the account was shared.
1271 */
1272 List<UserInfo> users = mUserManager.getUsers(true);
1273 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001274 if (user.isRestricted()
1275 && (user.restrictedProfileParentId == parentUserId)) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001276 renameSharedAccountAsUser(accountToRename, newName, user.id);
1277 }
1278 }
1279 }
1280 sendAccountsChangedBroadcast(accounts.userId);
1281 }
1282 }
1283 }
1284 return resultAccount;
1285 }
1286
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001287 private boolean canHaveProfile(final int parentUserId) {
1288 final UserInfo userInfo = mUserManager.getUserInfo(parentUserId);
1289 return userInfo != null && userInfo.canHaveProfile();
1290 }
1291
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001292 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001293 public void removeAccount(IAccountManagerResponse response, Account account,
1294 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001295 removeAccountAsUser(
1296 response,
1297 account,
1298 expectActivityLaunch,
1299 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001300 }
1301
1302 @Override
1303 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001304 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001305 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001306 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1307 Log.v(TAG, "removeAccount: " + account
1308 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001309 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001310 + ", pid " + Binder.getCallingPid()
1311 + ", for user id " + userId);
1312 }
1313 if (response == null) throw new IllegalArgumentException("response is null");
1314 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001315 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001316 if (isCrossUser(callingUid, userId)) {
1317 throw new SecurityException(
1318 String.format(
1319 "User %s tying remove account for %s" ,
1320 UserHandle.getCallingUserId(),
1321 userId));
1322 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001323 /*
1324 * Only the system or authenticator should be allowed to remove accounts for that
1325 * authenticator. This will let users remove accounts (via Settings in the system) but not
1326 * arbitrary applications (like competing authenticators).
1327 */
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001328 UserHandle user = new UserHandle(userId);
1329 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1330 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001331 String msg = String.format(
1332 "uid %s cannot remove accounts of type: %s",
1333 callingUid,
1334 account.type);
1335 throw new SecurityException(msg);
1336 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001337 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001338 try {
1339 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1340 "User cannot modify accounts");
1341 } catch (RemoteException re) {
1342 }
1343 return;
1344 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001345 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001346 try {
1347 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1348 "User cannot modify accounts of this type (policy).");
1349 } catch (RemoteException re) {
1350 }
1351 return;
1352 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001353 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001354 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001355 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001356 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001357 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08001358 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001359 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001360 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001361 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001362 }
1363 }
1364 }
1365
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001366 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1367
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001368 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001369 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1370 } finally {
1371 restoreCallingIdentity(identityToken);
1372 }
1373 }
1374
1375 @Override
1376 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001377 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001378 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1379 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001380 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001381 + ", pid " + Binder.getCallingPid());
1382 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001383 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001384 if (account == null) {
1385 /*
1386 * Null accounts should result in returning false, as per
1387 * AccountManage.addAccountExplicitly(...) java doc.
1388 */
1389 Log.e(TAG, "account is null");
1390 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001391 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001392 String msg = String.format(
1393 "uid %s cannot explicitly add accounts of type: %s",
1394 callingUid,
1395 account.type);
1396 throw new SecurityException(msg);
1397 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001398 UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001399 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001400 long identityToken = clearCallingIdentity();
1401 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001402 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001403 } finally {
1404 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001405 }
Fred Quintana60307342009-03-24 22:48:12 -07001406 }
1407
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001408 private class RemoveAccountSession extends Session {
1409 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001410 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001411 Account account, boolean expectActivityLaunch) {
1412 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001413 true /* stripAuthTokenFromResult */, account.name,
1414 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001415 mAccount = account;
1416 }
1417
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001418 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001419 protected String toDebugString(long now) {
1420 return super.toDebugString(now) + ", removeAccount"
1421 + ", account " + mAccount;
1422 }
1423
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001424 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001425 public void run() throws RemoteException {
1426 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1427 }
1428
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001429 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001430 public void onResult(Bundle result) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001431 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1432 && !result.containsKey(AccountManager.KEY_INTENT)) {
1433 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001434 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001435 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001436 }
1437 IAccountManagerResponse response = getResponseAndClose();
1438 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001439 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1440 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1441 + response);
1442 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001443 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001444 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001445 try {
1446 response.onResult(result2);
1447 } catch (RemoteException e) {
1448 // ignore
1449 }
1450 }
1451 }
1452 super.onResult(result);
1453 }
1454 }
1455
Amith Yamasani04e0d262012-02-14 11:50:53 -08001456 /* For testing */
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001457 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001458 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001459 }
1460
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001461 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001462 int deleted;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001463 synchronized (accounts.cacheLock) {
1464 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001465 final long accountId = getAccountIdLocked(db, account);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001466 deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
1467 + "=?",
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001468 new String[]{account.name, account.type});
Amith Yamasani04e0d262012-02-14 11:50:53 -08001469 removeAccountFromCacheLocked(accounts, account);
1470 sendAccountsChangedBroadcast(accounts.userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001471
1472 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001473 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001474 long id = Binder.clearCallingIdentity();
1475 try {
1476 int parentUserId = accounts.userId;
1477 if (canHaveProfile(parentUserId)) {
1478 // Remove from any restricted profiles that are sharing this account.
Amith Yamasani67df64b2012-12-14 12:09:36 -08001479 List<UserInfo> users = mUserManager.getUsers(true);
1480 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001481 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001482 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001483 }
1484 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001485 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001486 } finally {
1487 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001488 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001489 return (deleted > 0);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001490 }
1491
Maggie Benthalla12fccf2013-03-14 18:02:12 -04001492 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001493 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07001494 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001495 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1496 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07001497 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08001498 + ", pid " + Binder.getCallingPid());
1499 }
Fred Quintana382601f2010-03-25 12:25:10 -07001500 if (accountType == null) throw new IllegalArgumentException("accountType is null");
1501 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001502 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001503 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001504 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001505 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001506 synchronized (accounts.cacheLock) {
1507 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001508 db.beginTransaction();
1509 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001510 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001511 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001512 db.setTransactionSuccessful();
1513 } finally {
1514 db.endTransaction();
1515 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001516 }
Fred Quintana60307342009-03-24 22:48:12 -07001517 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001518 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001519 }
1520 }
1521
Carlos Valdivia91979be2015-05-22 14:11:35 -07001522 private void invalidateCustomTokenLocked(
1523 UserAccounts accounts,
1524 String accountType,
1525 String authToken) {
1526 if (authToken == null || accountType == null) {
1527 return;
1528 }
1529 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001530 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001531 }
1532
Amith Yamasani04e0d262012-02-14 11:50:53 -08001533 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1534 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001535 if (authToken == null || accountType == null) {
1536 return;
1537 }
Fred Quintana33269202009-04-20 16:05:10 -07001538 Cursor cursor = db.rawQuery(
1539 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1540 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1541 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1542 + " FROM " + TABLE_ACCOUNTS
1543 + " JOIN " + TABLE_AUTHTOKENS
1544 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1545 + " = " + AUTHTOKENS_ACCOUNTS_ID
1546 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
1547 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1548 new String[]{authToken, accountType});
1549 try {
1550 while (cursor.moveToNext()) {
1551 long authTokenId = cursor.getLong(0);
1552 String accountName = cursor.getString(1);
1553 String authTokenType = cursor.getString(2);
1554 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001555 writeAuthTokenIntoCacheLocked(
1556 accounts,
1557 db,
1558 new Account(accountName, accountType),
1559 authTokenType,
1560 null);
Fred Quintana60307342009-03-24 22:48:12 -07001561 }
Fred Quintana33269202009-04-20 16:05:10 -07001562 } finally {
1563 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07001564 }
1565 }
1566
Carlos Valdivia91979be2015-05-22 14:11:35 -07001567 private void saveCachedToken(
1568 UserAccounts accounts,
1569 Account account,
1570 String callerPkg,
1571 byte[] callerSigDigest,
1572 String tokenType,
1573 String token,
1574 long expiryMillis) {
1575
1576 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1577 return;
1578 }
1579 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1580 new UserHandle(accounts.userId));
1581 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001582 accounts.accountTokenCaches.put(
1583 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001584 }
1585 }
1586
Amith Yamasani04e0d262012-02-14 11:50:53 -08001587 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1588 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07001589 if (account == null || type == null) {
1590 return false;
1591 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001592 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1593 new UserHandle(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001594 synchronized (accounts.cacheLock) {
1595 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001596 db.beginTransaction();
1597 try {
1598 long accountId = getAccountIdLocked(db, account);
1599 if (accountId < 0) {
1600 return false;
1601 }
1602 db.delete(TABLE_AUTHTOKENS,
1603 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1604 new String[]{type});
1605 ContentValues values = new ContentValues();
1606 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1607 values.put(AUTHTOKENS_TYPE, type);
1608 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1609 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1610 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001611 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001612 return true;
1613 }
Fred Quintana33269202009-04-20 16:05:10 -07001614 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001615 } finally {
1616 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07001617 }
Fred Quintana60307342009-03-24 22:48:12 -07001618 }
1619 }
1620
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001621 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001622 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001623 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001624 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1625 Log.v(TAG, "peekAuthToken: " + account
1626 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001627 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001628 + ", pid " + Binder.getCallingPid());
1629 }
Fred Quintana382601f2010-03-25 12:25:10 -07001630 if (account == null) throw new IllegalArgumentException("account is null");
1631 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001632 int userId = UserHandle.getCallingUserId();
1633 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001634 String msg = String.format(
1635 "uid %s cannot peek the authtokens associated with accounts of type: %s",
1636 callingUid,
1637 account.type);
1638 throw new SecurityException(msg);
1639 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001640 long identityToken = clearCallingIdentity();
1641 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001642 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001643 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001644 } finally {
1645 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001646 }
Fred Quintana60307342009-03-24 22:48:12 -07001647 }
1648
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001649 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001650 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001651 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001652 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1653 Log.v(TAG, "setAuthToken: " + account
1654 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001655 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001656 + ", pid " + Binder.getCallingPid());
1657 }
Fred Quintana382601f2010-03-25 12:25:10 -07001658 if (account == null) throw new IllegalArgumentException("account is null");
1659 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001660 int userId = UserHandle.getCallingUserId();
1661 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001662 String msg = String.format(
1663 "uid %s cannot set auth tokens associated with accounts of type: %s",
1664 callingUid,
1665 account.type);
1666 throw new SecurityException(msg);
1667 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001668 long identityToken = clearCallingIdentity();
1669 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001670 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001671 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001672 } finally {
1673 restoreCallingIdentity(identityToken);
1674 }
Fred Quintana60307342009-03-24 22:48:12 -07001675 }
1676
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001677 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001678 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001679 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001680 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1681 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001682 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001683 + ", pid " + Binder.getCallingPid());
1684 }
Fred Quintana382601f2010-03-25 12:25:10 -07001685 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001686 int userId = UserHandle.getCallingUserId();
1687 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001688 String msg = String.format(
1689 "uid %s cannot set secrets for accounts of type: %s",
1690 callingUid,
1691 account.type);
1692 throw new SecurityException(msg);
1693 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001694 long identityToken = clearCallingIdentity();
1695 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001696 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001697 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001698 } finally {
1699 restoreCallingIdentity(identityToken);
1700 }
Fred Quintana60307342009-03-24 22:48:12 -07001701 }
1702
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001703 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1704 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07001705 if (account == null) {
1706 return;
1707 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001708 synchronized (accounts.cacheLock) {
1709 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001710 db.beginTransaction();
1711 try {
1712 final ContentValues values = new ContentValues();
1713 values.put(ACCOUNTS_PASSWORD, password);
1714 final long accountId = getAccountIdLocked(db, account);
1715 if (accountId >= 0) {
1716 final String[] argsAccountId = {String.valueOf(accountId)};
1717 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1718 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001719 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001720 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001721 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001722
1723 String action = (password == null || password.length() == 0) ?
1724 DebugDbHelper.ACTION_CLEAR_PASSWORD
1725 : DebugDbHelper.ACTION_SET_PASSWORD;
1726 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08001727 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001728 } finally {
1729 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001730 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001731 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001732 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001733 }
1734
Amith Yamasani04e0d262012-02-14 11:50:53 -08001735 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08001736 Log.i(TAG, "the accounts changed, sending broadcast of "
1737 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001738 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07001739 }
1740
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001741 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001742 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001743 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001744 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1745 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001746 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001747 + ", pid " + Binder.getCallingPid());
1748 }
Fred Quintana382601f2010-03-25 12:25:10 -07001749 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001750 int userId = UserHandle.getCallingUserId();
1751 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001752 String msg = String.format(
1753 "uid %s cannot clear passwords for accounts of type: %s",
1754 callingUid,
1755 account.type);
1756 throw new SecurityException(msg);
1757 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001758 long identityToken = clearCallingIdentity();
1759 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001760 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001761 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001762 } finally {
1763 restoreCallingIdentity(identityToken);
1764 }
Fred Quintana60307342009-03-24 22:48:12 -07001765 }
1766
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001767 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001768 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001769 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001770 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1771 Log.v(TAG, "setUserData: " + account
1772 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001773 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001774 + ", pid " + Binder.getCallingPid());
1775 }
Fred Quintana382601f2010-03-25 12:25:10 -07001776 if (key == null) throw new IllegalArgumentException("key is null");
1777 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001778 int userId = UserHandle.getCallingUserId();
1779 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001780 String msg = String.format(
1781 "uid %s cannot set user data for accounts of type: %s",
1782 callingUid,
1783 account.type);
1784 throw new SecurityException(msg);
1785 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001786 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001787 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001788 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001789 setUserdataInternal(accounts, account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -07001790 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001791 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001792 }
1793 }
1794
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001795 private void setUserdataInternal(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08001796 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07001797 if (account == null || key == null) {
1798 return;
1799 }
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001800 synchronized (accounts.cacheLock) {
1801 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1802 db.beginTransaction();
1803 try {
1804 long accountId = getAccountIdLocked(db, account);
1805 if (accountId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001806 return;
1807 }
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001808 long extrasId = getExtrasIdLocked(db, accountId, key);
1809 if (extrasId < 0 ) {
1810 extrasId = insertExtraLocked(db, accountId, key, value);
1811 if (extrasId < 0) {
1812 return;
1813 }
1814 } else {
1815 ContentValues values = new ContentValues();
1816 values.put(EXTRAS_VALUE, value);
1817 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1818 return;
1819 }
Simranjit Singh Kohlia666d742015-08-07 13:34:09 -07001820
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001821 }
1822 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
1823 db.setTransactionSuccessful();
1824 } finally {
1825 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001826 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001827 }
1828 }
1829
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001830 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08001831 if (result == null) {
1832 Log.e(TAG, "the result is unexpectedly null", new Exception());
1833 }
1834 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1835 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1836 + response);
1837 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001838 try {
1839 response.onResult(result);
1840 } catch (RemoteException e) {
1841 // if the caller is dead then there is no one to care about remote
1842 // exceptions
1843 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1844 Log.v(TAG, "failure while notifying response", e);
1845 }
1846 }
1847 }
1848
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001849 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07001850 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1851 final String authTokenType)
1852 throws RemoteException {
1853 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08001854 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1855
Fred Quintanad9640ec2012-05-23 12:37:00 -07001856 final int callingUid = getCallingUid();
1857 clearCallingIdentity();
Amith Yamasani27db4682013-03-30 17:07:47 -07001858 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07001859 throw new SecurityException("can only call from system");
1860 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001861 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001862 long identityToken = clearCallingIdentity();
1863 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001864 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001865 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
1866 false /* stripAuthTokenFromResult */, null /* accountName */,
1867 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001868 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001869 protected String toDebugString(long now) {
1870 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07001871 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08001872 + ", authTokenType " + authTokenType;
1873 }
1874
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001875 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001876 public void run() throws RemoteException {
1877 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1878 }
1879
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001880 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001881 public void onResult(Bundle result) {
1882 if (result != null) {
1883 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1884 Bundle bundle = new Bundle();
1885 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1886 super.onResult(bundle);
1887 return;
1888 } else {
1889 super.onResult(result);
1890 }
1891 }
1892 }.bind();
1893 } finally {
1894 restoreCallingIdentity(identityToken);
1895 }
1896 }
1897
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001898 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07001899 public void getAuthToken(
1900 IAccountManagerResponse response,
1901 final Account account,
1902 final String authTokenType,
1903 final boolean notifyOnAuthFailure,
1904 final boolean expectActivityLaunch,
1905 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08001906 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1907 Log.v(TAG, "getAuthToken: " + account
1908 + ", response " + response
1909 + ", authTokenType " + authTokenType
1910 + ", notifyOnAuthFailure " + notifyOnAuthFailure
1911 + ", expectActivityLaunch " + expectActivityLaunch
1912 + ", caller's uid " + Binder.getCallingUid()
1913 + ", pid " + Binder.getCallingPid());
1914 }
Fred Quintana382601f2010-03-25 12:25:10 -07001915 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001916 try {
1917 if (account == null) {
1918 Slog.w(TAG, "getAuthToken called with null account");
1919 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
1920 return;
1921 }
1922 if (authTokenType == null) {
1923 Slog.w(TAG, "getAuthToken called with null authTokenType");
1924 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
1925 return;
1926 }
1927 } catch (RemoteException e) {
1928 Slog.w(TAG, "Failed to report error back to the client." + e);
1929 return;
1930 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001931 int userId = UserHandle.getCallingUserId();
1932 long ident = Binder.clearCallingIdentity();
1933 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001934 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001935 try {
1936 accounts = getUserAccounts(userId);
1937 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1938 AuthenticatorDescription.newKey(account.type), accounts.userId);
1939 } finally {
1940 Binder.restoreCallingIdentity(ident);
1941 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001942
Costin Manolachea40c6302010-12-13 14:50:45 -08001943 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07001944 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08001945
1946 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001947 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001948 final boolean permissionGranted =
1949 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08001950
Carlos Valdivia91979be2015-05-22 14:11:35 -07001951 // Get the calling package. We will use it for the purpose of caching.
1952 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07001953 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001954 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07001955 try {
1956 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
1957 } finally {
1958 Binder.restoreCallingIdentity(ident);
1959 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001960 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
1961 String msg = String.format(
1962 "Uid %s is attempting to illegally masquerade as package %s!",
1963 callerUid,
1964 callerPkg);
1965 throw new SecurityException(msg);
1966 }
1967
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001968 // let authenticator know the identity of the caller
1969 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1970 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07001971
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001972 if (notifyOnAuthFailure) {
1973 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08001974 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001975
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001976 long identityToken = clearCallingIdentity();
1977 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07001978 // Distill the caller's package signatures into a single digest.
1979 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
1980
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001981 // if the caller has permission, do the peek. otherwise go the more expensive
1982 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08001983 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001984 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001985 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001986 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001987 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1988 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1989 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001990 onResult(response, result);
1991 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001992 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001993 }
1994
Carlos Valdivia91979be2015-05-22 14:11:35 -07001995 if (customTokens) {
1996 /*
1997 * Look up tokens in the new cache only if the loginOptions don't have parameters
1998 * outside of those expected to be injected by the AccountManager, e.g.
1999 * ANDORID_PACKAGE_NAME.
2000 */
2001 String token = readCachedTokenInternal(
2002 accounts,
2003 account,
2004 authTokenType,
2005 callerPkg,
2006 callerPkgSigDigest);
2007 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002008 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2009 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2010 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002011 Bundle result = new Bundle();
2012 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2013 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2014 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2015 onResult(response, result);
2016 return;
2017 }
2018 }
2019
Amith Yamasani04e0d262012-02-14 11:50:53 -08002020 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002021 false /* stripAuthTokenFromResult */, account.name,
2022 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002023 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002024 protected String toDebugString(long now) {
2025 if (loginOptions != null) loginOptions.keySet();
2026 return super.toDebugString(now) + ", getAuthToken"
2027 + ", " + account
2028 + ", authTokenType " + authTokenType
2029 + ", loginOptions " + loginOptions
2030 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2031 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002032
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002033 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002034 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002035 // If the caller doesn't have permission then create and return the
2036 // "grant permission" intent instead of the "getAuthToken" intent.
2037 if (!permissionGranted) {
2038 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2039 } else {
2040 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2041 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002042 }
2043
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002044 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002045 public void onResult(Bundle result) {
2046 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002047 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002048 Intent intent = newGrantCredentialsPermissionIntent(
2049 account,
2050 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002051 new AccountAuthenticatorResponse(this),
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002052 authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002053 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002054 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002055 onResult(bundle);
2056 return;
2057 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002058 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002059 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002060 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2061 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002062 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002063 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002064 "the type and name should not be empty");
2065 return;
2066 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002067 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002068 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002069 saveAuthTokenToDatabase(
2070 mAccounts,
2071 resultAccount,
2072 authTokenType,
2073 authToken);
2074 }
2075 long expiryMillis = result.getLong(
2076 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2077 if (customTokens
2078 && expiryMillis > System.currentTimeMillis()) {
2079 saveCachedToken(
2080 mAccounts,
2081 account,
2082 callerPkg,
2083 callerPkgSigDigest,
2084 authTokenType,
2085 authToken,
2086 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002087 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002088 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002089
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002090 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002091 if (intent != null && notifyOnAuthFailure && !customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002092 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002093 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Dianne Hackborn41203752012-08-31 14:05:51 -07002094 intent, accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002095 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002096 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002097 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002098 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002099 }.bind();
2100 } finally {
2101 restoreCallingIdentity(identityToken);
2102 }
Fred Quintana60307342009-03-24 22:48:12 -07002103 }
2104
Carlos Valdivia91979be2015-05-22 14:11:35 -07002105 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2106 MessageDigest digester;
2107 try {
2108 digester = MessageDigest.getInstance("SHA-256");
2109 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2110 callerPkg, PackageManager.GET_SIGNATURES);
2111 for (Signature sig : pkgInfo.signatures) {
2112 digester.update(sig.toByteArray());
2113 }
2114 } catch (NoSuchAlgorithmException x) {
2115 Log.wtf(TAG, "SHA-256 should be available", x);
2116 digester = null;
2117 } catch (NameNotFoundException e) {
2118 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2119 digester = null;
2120 }
2121 return (digester == null) ? null : digester.digest();
2122 }
2123
Dianne Hackborn41203752012-08-31 14:05:51 -07002124 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
2125 int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002126 int uid = intent.getIntExtra(
2127 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2128 String authTokenType = intent.getStringExtra(
2129 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002130 final String titleAndSubtitle =
2131 mContext.getString(R.string.permission_request_notification_with_subtitle,
2132 account.name);
2133 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07002134 String title = titleAndSubtitle;
2135 String subtitle = "";
2136 if (index > 0) {
2137 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002138 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07002139 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002140 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01002141 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002142 Notification n = new Notification.Builder(contextForUser)
2143 .setSmallIcon(android.R.drawable.stat_sys_warning)
2144 .setWhen(0)
2145 .setColor(contextForUser.getColor(
2146 com.android.internal.R.color.system_notification_accent_color))
2147 .setContentTitle(title)
2148 .setContentText(subtitle)
2149 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2150 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2151 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002152 installNotification(getCredentialPermissionNotificationId(
2153 account, authTokenType, uid), n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002154 }
2155
Costin Manolache5f383ad92010-12-02 16:44:46 -08002156 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002157 AccountAuthenticatorResponse response, String authTokenType) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002158
2159 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07002160 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08002161 // Since it was set in Eclair+ we can't change it without breaking apps using
2162 // the intent from a non-Activity context.
2163 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002164 intent.addCategory(
2165 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08002166
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002167 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002168 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2169 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002170 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002171
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002172 return intent;
2173 }
2174
2175 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2176 int uid) {
2177 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002178 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002179 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002180 final Pair<Pair<Account, String>, Integer> key =
2181 new Pair<Pair<Account, String>, Integer>(
2182 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002183 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002184 if (id == null) {
2185 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002186 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002187 }
2188 }
2189 return id;
2190 }
2191
Amith Yamasani04e0d262012-02-14 11:50:53 -08002192 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002193 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002194 synchronized (accounts.signinRequiredNotificationIds) {
2195 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002196 if (id == null) {
2197 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002198 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002199 }
2200 }
2201 return id;
2202 }
2203
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002204 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07002205 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07002206 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002207 final boolean expectActivityLaunch, final Bundle optionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08002208 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2209 Log.v(TAG, "addAccount: accountType " + accountType
2210 + ", response " + response
2211 + ", authTokenType " + authTokenType
2212 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2213 + ", expectActivityLaunch " + expectActivityLaunch
2214 + ", caller's uid " + Binder.getCallingUid()
2215 + ", pid " + Binder.getCallingPid());
2216 }
Fred Quintana382601f2010-03-25 12:25:10 -07002217 if (response == null) throw new IllegalArgumentException("response is null");
2218 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002219
Amith Yamasani71e6c692013-03-24 17:39:28 -07002220 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002221 final int uid = Binder.getCallingUid();
2222 final int userId = UserHandle.getUserId(uid);
2223 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002224 try {
2225 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2226 "User is not allowed to add an account!");
2227 } catch (RemoteException re) {
2228 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002229 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002230 return;
2231 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002232 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002233 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002234 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2235 "User cannot modify accounts of this type (policy).");
2236 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002237 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002238 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2239 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002240 return;
2241 }
2242
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002243 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002244 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2245 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2246 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2247
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002248 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002249 long identityToken = clearCallingIdentity();
2250 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002251 UserAccounts accounts = getUserAccounts(usrId);
2252 logRecordWithUid(
2253 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002254 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002255 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002256 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002257 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002258 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07002259 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07002260 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002261 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002262
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002263 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002264 protected String toDebugString(long now) {
2265 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07002266 + ", accountType " + accountType
2267 + ", requiredFeatures "
2268 + (requiredFeatures != null
2269 ? TextUtils.join(",", requiredFeatures)
2270 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002271 }
2272 }.bind();
2273 } finally {
2274 restoreCallingIdentity(identityToken);
2275 }
Fred Quintana60307342009-03-24 22:48:12 -07002276 }
2277
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002278 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002279 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2280 final String authTokenType, final String[] requiredFeatures,
2281 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002282 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002283 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2284 Log.v(TAG, "addAccount: accountType " + accountType
2285 + ", response " + response
2286 + ", authTokenType " + authTokenType
2287 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2288 + ", expectActivityLaunch " + expectActivityLaunch
2289 + ", caller's uid " + Binder.getCallingUid()
2290 + ", pid " + Binder.getCallingPid()
2291 + ", for user id " + userId);
2292 }
2293 if (response == null) throw new IllegalArgumentException("response is null");
2294 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002295 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002296 if (isCrossUser(callingUid, userId)) {
2297 throw new SecurityException(
2298 String.format(
2299 "User %s trying to add account for %s" ,
2300 UserHandle.getCallingUserId(),
2301 userId));
2302 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002303
2304 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002305 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002306 try {
2307 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2308 "User is not allowed to add an account!");
2309 } catch (RemoteException re) {
2310 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002311 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002312 return;
2313 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002314 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002315 try {
2316 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2317 "User cannot modify accounts of this type (policy).");
2318 } catch (RemoteException re) {
2319 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002320 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2321 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002322 return;
2323 }
2324
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002325 final int pid = Binder.getCallingPid();
2326 final int uid = Binder.getCallingUid();
2327 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2328 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2329 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2330
2331 long identityToken = clearCallingIdentity();
2332 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002333 UserAccounts accounts = getUserAccounts(userId);
2334 logRecordWithUid(
2335 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002336 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002337 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002338 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002339 @Override
2340 public void run() throws RemoteException {
2341 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2342 options);
2343 }
2344
2345 @Override
2346 protected String toDebugString(long now) {
2347 return super.toDebugString(now) + ", addAccount"
2348 + ", accountType " + accountType
2349 + ", requiredFeatures "
2350 + (requiredFeatures != null
2351 ? TextUtils.join(",", requiredFeatures)
2352 : null);
2353 }
2354 }.bind();
2355 } finally {
2356 restoreCallingIdentity(identityToken);
2357 }
2358 }
2359
Sandra Kwan78812282015-11-04 11:19:47 -08002360 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08002361 public void startAddAccountSession(
2362 final IAccountManagerResponse response,
2363 final String accountType,
2364 final String authTokenType,
2365 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08002366 final boolean expectActivityLaunch,
2367 final Bundle optionsIn) {
2368 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2369 Log.v(TAG,
2370 "startAddAccountSession: accountType " + accountType
2371 + ", response " + response
2372 + ", authTokenType " + authTokenType
2373 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2374 + ", expectActivityLaunch " + expectActivityLaunch
2375 + ", caller's uid " + Binder.getCallingUid()
2376 + ", pid " + Binder.getCallingPid());
2377 }
2378 if (response == null) {
2379 throw new IllegalArgumentException("response is null");
2380 }
2381 if (accountType == null) {
2382 throw new IllegalArgumentException("accountType is null");
2383 }
2384
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002385 final int uid = Binder.getCallingUid();
Sandra Kwana578d112015-12-16 16:01:43 -08002386 // Only allow system to start session
2387 if (!isSystemUid(uid)) {
2388 String msg = String.format(
2389 "uid %s cannot stat add account session.",
2390 uid);
2391 throw new SecurityException(msg);
2392 }
2393
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002394 final int userId = UserHandle.getUserId(uid);
2395 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002396 try {
2397 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2398 "User is not allowed to add an account!");
2399 } catch (RemoteException re) {
2400 }
2401 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2402 return;
2403 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002404 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002405 try {
2406 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2407 "User cannot modify accounts of this type (policy).");
2408 } catch (RemoteException re) {
2409 }
2410 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2411 userId);
2412 return;
2413 }
2414
2415 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08002416 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2417 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2418 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2419
2420 int usrId = UserHandle.getCallingUserId();
2421 long identityToken = clearCallingIdentity();
2422 try {
2423 UserAccounts accounts = getUserAccounts(usrId);
2424 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2425 TABLE_ACCOUNTS, uid);
2426 new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
2427 null /* accountName */, false /* authDetailsRequired */,
2428 true /* updateLastAuthenticationTime */) {
2429 @Override
2430 public void run() throws RemoteException {
2431 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2432 requiredFeatures, options);
2433 }
2434
2435 @Override
2436 protected String toDebugString(long now) {
2437 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2438 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2439 + accountType + ", requiredFeatures "
2440 + (requiredFeatures != null ? requiredFeaturesStr : null);
2441 }
2442 }.bind();
2443 } finally {
2444 restoreCallingIdentity(identityToken);
2445 }
2446 }
2447
2448 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2449 private abstract class StartAccountSession extends Session {
2450
2451 public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
2452 String accountType, boolean expectActivityLaunch, String accountName,
2453 boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
2454 super(accounts, response, accountType, expectActivityLaunch,
2455 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2456 updateLastAuthenticationTime);
2457 }
2458
2459 @Override
2460 public void onResult(Bundle result) {
2461 mNumResults++;
2462 Intent intent = null;
2463
2464 if (result != null
2465 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2466 /*
2467 * The Authenticator API allows third party authenticators to
2468 * supply arbitrary intents to other apps that they can run,
2469 * this can be very bad when those apps are in the system like
2470 * the System Settings.
2471 */
2472 int authenticatorUid = Binder.getCallingUid();
2473 long bid = Binder.clearCallingIdentity();
2474 try {
2475 PackageManager pm = mContext.getPackageManager();
2476 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
2477 int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
2478 if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid,
2479 targetUid)) {
2480 throw new SecurityException("Activity to be started with KEY_INTENT must "
2481 + "share Authenticator's signatures");
2482 }
2483 } finally {
2484 Binder.restoreCallingIdentity(bid);
2485 }
2486 }
2487
2488 IAccountManagerResponse response;
2489 if (mExpectActivityLaunch && result != null
2490 && result.containsKey(AccountManager.KEY_INTENT)) {
2491 response = mResponse;
2492 } else {
2493 response = getResponseAndClose();
2494 }
2495 if (response == null) {
2496 return;
2497 }
2498 if (result == null) {
2499 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2500 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2501 + response);
2502 }
2503 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2504 "null bundle returned");
2505 return;
2506 }
2507
2508 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2509 // All AccountManager error codes are greater
2510 // than 0
2511 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2512 result.getString(AccountManager.KEY_ERROR_MESSAGE));
2513 return;
2514 }
2515
2516 // Strip auth token from result.
2517 result.remove(AccountManager.KEY_AUTHTOKEN);
2518
2519 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2520 Log.v(TAG,
2521 getClass().getSimpleName() + " calling onResult() on response " + response);
2522 }
2523
2524 // Get the session bundle created by authenticator. The
2525 // bundle contains data necessary for finishing the session
2526 // later. The session bundle will be encrypted here and
2527 // decrypted later when trying to finish the session.
2528 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
2529 if (sessionBundle != null) {
2530 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2531 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08002532 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002533 Log.w(TAG, "Account type in session bundle doesn't match request.");
2534 }
2535 // Add accountType info to session bundle. This will
2536 // override any value set by authenticator.
2537 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
2538
2539 // Encrypt session bundle before returning to caller.
2540 try {
2541 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2542 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
2543 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
2544 } catch (GeneralSecurityException e) {
2545 if (Log.isLoggable(TAG, Log.DEBUG)) {
2546 Log.v(TAG, "Failed to encrypt session bundle!", e);
2547 }
2548 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2549 "failed to encrypt session bundle");
2550 return;
2551 }
2552 }
2553
2554 sendResponse(response, result);
2555 }
2556 }
2557
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002558 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08002559 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002560 @NonNull Bundle sessionBundle,
2561 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002562 Bundle appInfo,
2563 int userId) {
2564 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002565 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2566 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002567 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002568 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08002569 + ", caller's uid " + callingUid
2570 + ", caller's user id " + UserHandle.getCallingUserId()
2571 + ", pid " + Binder.getCallingPid()
2572 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002573 }
2574 if (response == null) {
2575 throw new IllegalArgumentException("response is null");
2576 }
2577
2578 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
2579 // Account type is added to it before encryption.
2580 if (sessionBundle == null || sessionBundle.size() == 0) {
2581 throw new IllegalArgumentException("sessionBundle is empty");
2582 }
2583
Sandra Kwan0b84b452016-01-20 15:25:42 -08002584 // Only allow the system process to finish session for other users
2585 if (isCrossUser(callingUid, userId)) {
2586 throw new SecurityException(
2587 String.format(
2588 "User %s trying to finish session for %s without cross user permission",
2589 UserHandle.getCallingUserId(),
2590 userId));
2591 }
2592
Sandra Kwana578d112015-12-16 16:01:43 -08002593 // Only allow system to finish session
Sandra Kwan0b84b452016-01-20 15:25:42 -08002594 if (!isSystemUid(callingUid)) {
Sandra Kwana578d112015-12-16 16:01:43 -08002595 String msg = String.format(
Sandra Kwan0b84b452016-01-20 15:25:42 -08002596 "uid %s cannot finish session because it's not system uid.",
2597 callingUid);
Sandra Kwana578d112015-12-16 16:01:43 -08002598 throw new SecurityException(msg);
2599 }
2600
Sandra Kwan0b84b452016-01-20 15:25:42 -08002601 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002602 sendErrorResponse(response,
2603 AccountManager.ERROR_CODE_USER_RESTRICTED,
2604 "User is not allowed to add an account!");
2605 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2606 return;
2607 }
2608
2609 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002610 final Bundle decryptedBundle;
2611 final String accountType;
2612 // First decrypt session bundle to get account type for checking permission.
2613 try {
2614 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2615 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
2616 if (decryptedBundle == null) {
2617 sendErrorResponse(
2618 response,
2619 AccountManager.ERROR_CODE_BAD_REQUEST,
2620 "failed to decrypt session bundle");
2621 return;
2622 }
2623 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2624 // Account type cannot be null. This should not happen if session bundle was created
2625 // properly by #StartAccountSession.
2626 if (TextUtils.isEmpty(accountType)) {
2627 sendErrorResponse(
2628 response,
2629 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
2630 "accountType is empty");
2631 return;
2632 }
2633
2634 // If by any chances, decryptedBundle contains colliding keys with
2635 // system info
2636 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
2637 // update credentials flow, we should replace with the new values of the current call.
2638 if (appInfo != null) {
2639 decryptedBundle.putAll(appInfo);
2640 }
2641
2642 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08002643 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002644 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
2645 } catch (GeneralSecurityException e) {
2646 if (Log.isLoggable(TAG, Log.DEBUG)) {
2647 Log.v(TAG, "Failed to decrypt session bundle!", e);
2648 }
2649 sendErrorResponse(
2650 response,
2651 AccountManager.ERROR_CODE_BAD_REQUEST,
2652 "failed to decrypt session bundle");
2653 return;
2654 }
2655
Sandra Kwan0b84b452016-01-20 15:25:42 -08002656 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002657 sendErrorResponse(
2658 response,
2659 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2660 "User cannot modify accounts of this type (policy).");
2661 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2662 userId);
2663 return;
2664 }
2665
2666 long identityToken = clearCallingIdentity();
2667 try {
2668 UserAccounts accounts = getUserAccounts(userId);
2669 logRecordWithUid(
2670 accounts,
2671 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
2672 TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002673 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002674 new Session(
2675 accounts,
2676 response,
2677 accountType,
2678 expectActivityLaunch,
2679 true /* stripAuthTokenFromResult */,
2680 null /* accountName */,
2681 false /* authDetailsRequired */,
2682 true /* updateLastAuthenticationTime */) {
2683 @Override
2684 public void run() throws RemoteException {
2685 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
2686 }
2687
2688 @Override
2689 protected String toDebugString(long now) {
2690 return super.toDebugString(now)
2691 + ", finishSession"
2692 + ", accountType " + accountType;
2693 }
2694 }.bind();
2695 } finally {
2696 restoreCallingIdentity(identityToken);
2697 }
2698 }
2699
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002700 private void showCantAddAccount(int errorCode, int userId) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002701 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2702 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2703 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2704 long identityToken = clearCallingIdentity();
2705 try {
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002706 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002707 } finally {
2708 restoreCallingIdentity(identityToken);
2709 }
2710 }
2711
2712 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002713 public void confirmCredentialsAsUser(
2714 IAccountManagerResponse response,
2715 final Account account,
2716 final Bundle options,
2717 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002718 int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002719 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002720 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2721 Log.v(TAG, "confirmCredentials: " + account
2722 + ", response " + response
2723 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002724 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002725 + ", pid " + Binder.getCallingPid());
2726 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002727 // Only allow the system process to read accounts of other users
2728 if (isCrossUser(callingUid, userId)) {
2729 throw new SecurityException(
2730 String.format(
2731 "User %s trying to confirm account credentials for %s" ,
2732 UserHandle.getCallingUserId(),
2733 userId));
2734 }
Fred Quintana382601f2010-03-25 12:25:10 -07002735 if (response == null) throw new IllegalArgumentException("response is null");
2736 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002737 long identityToken = clearCallingIdentity();
2738 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002739 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002740 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002741 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002742 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002743 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002744 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002745 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002746 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002747 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002748 protected String toDebugString(long now) {
2749 return super.toDebugString(now) + ", confirmCredentials"
2750 + ", " + account;
2751 }
2752 }.bind();
2753 } finally {
2754 restoreCallingIdentity(identityToken);
2755 }
Fred Quintana60307342009-03-24 22:48:12 -07002756 }
2757
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002758 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002759 public void updateCredentials(IAccountManagerResponse response, final Account account,
2760 final String authTokenType, final boolean expectActivityLaunch,
2761 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08002762 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2763 Log.v(TAG, "updateCredentials: " + account
2764 + ", response " + response
2765 + ", authTokenType " + authTokenType
2766 + ", expectActivityLaunch " + expectActivityLaunch
2767 + ", caller's uid " + Binder.getCallingUid()
2768 + ", pid " + Binder.getCallingPid());
2769 }
Fred Quintana382601f2010-03-25 12:25:10 -07002770 if (response == null) throw new IllegalArgumentException("response is null");
2771 if (account == null) throw new IllegalArgumentException("account is null");
2772 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002773 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002774 long identityToken = clearCallingIdentity();
2775 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002776 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002777 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002778 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002779 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002780 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002781 public void run() throws RemoteException {
2782 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
2783 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002784 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002785 protected String toDebugString(long now) {
2786 if (loginOptions != null) loginOptions.keySet();
2787 return super.toDebugString(now) + ", updateCredentials"
2788 + ", " + account
2789 + ", authTokenType " + authTokenType
2790 + ", loginOptions " + loginOptions;
2791 }
2792 }.bind();
2793 } finally {
2794 restoreCallingIdentity(identityToken);
2795 }
Fred Quintana60307342009-03-24 22:48:12 -07002796 }
2797
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002798 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08002799 public void startUpdateCredentialsSession(
2800 IAccountManagerResponse response,
2801 final Account account,
2802 final String authTokenType,
2803 final boolean expectActivityLaunch,
2804 final Bundle loginOptions) {
2805 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) {
2912 IAccountManagerResponse response = getResponseAndClose();
2913 if (response == null) {
2914 return;
2915 }
2916
2917 if (result == null) {
2918 sendErrorResponse(
2919 response,
2920 AccountManager.ERROR_CODE_INVALID_RESPONSE,
2921 "null bundle");
2922 return;
2923 }
2924
2925 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2926 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2927 + response);
2928 }
2929 // Check to see if an error occurred. We know if an error occurred because all
2930 // error codes are greater than 0.
2931 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
2932 sendErrorResponse(response,
2933 result.getInt(AccountManager.KEY_ERROR_CODE),
2934 result.getString(AccountManager.KEY_ERROR_MESSAGE));
2935 return;
2936 }
2937 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
2938 sendErrorResponse(
2939 response,
2940 AccountManager.ERROR_CODE_INVALID_RESPONSE,
2941 "no result in response");
2942 return;
2943 }
2944 final Bundle newResult = new Bundle();
2945 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
2946 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
2947 sendResponse(response, newResult);
2948 }
2949 }.bind();
2950 } finally {
2951 restoreCallingIdentity(identityToken);
2952 }
2953 }
2954
2955 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002956 public void editProperties(IAccountManagerResponse response, final String accountType,
2957 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002958 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002959 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2960 Log.v(TAG, "editProperties: accountType " + accountType
2961 + ", response " + response
2962 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002963 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002964 + ", pid " + Binder.getCallingPid());
2965 }
Fred Quintana382601f2010-03-25 12:25:10 -07002966 if (response == null) throw new IllegalArgumentException("response is null");
2967 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002968 int userId = UserHandle.getCallingUserId();
2969 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002970 String msg = String.format(
2971 "uid %s cannot edit authenticator properites for account type: %s",
2972 callingUid,
2973 accountType);
2974 throw new SecurityException(msg);
2975 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002976 long identityToken = clearCallingIdentity();
2977 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002978 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002979 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002980 true /* stripAuthTokenFromResult */, null /* accountName */,
2981 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002982 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002983 public void run() throws RemoteException {
2984 mAuthenticator.editProperties(this, mAccountType);
2985 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002986 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002987 protected String toDebugString(long now) {
2988 return super.toDebugString(now) + ", editProperties"
2989 + ", accountType " + accountType;
2990 }
2991 }.bind();
2992 } finally {
2993 restoreCallingIdentity(identityToken);
2994 }
Fred Quintana60307342009-03-24 22:48:12 -07002995 }
2996
Amith Yamasani12747872015-12-07 14:19:49 -08002997 @Override
2998 public boolean someUserHasAccount(@NonNull final Account account) {
2999 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3000 throw new SecurityException("Only system can check for accounts across users");
3001 }
3002 final long token = Binder.clearCallingIdentity();
3003 try {
3004 AccountAndUser[] allAccounts = getAllAccounts();
3005 for (int i = allAccounts.length - 1; i >= 0; i--) {
3006 if (allAccounts[i].account.equals(account)) {
3007 return true;
3008 }
3009 }
3010 return false;
3011 } finally {
3012 Binder.restoreCallingIdentity(token);
3013 }
3014 }
3015
Fred Quintana33269202009-04-20 16:05:10 -07003016 private class GetAccountsByTypeAndFeatureSession extends Session {
3017 private final String[] mFeatures;
3018 private volatile Account[] mAccountsOfType = null;
3019 private volatile ArrayList<Account> mAccountsWithFeatures = null;
3020 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003021 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003022
Amith Yamasani04e0d262012-02-14 11:50:53 -08003023 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003024 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003025 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003026 true /* stripAuthTokenFromResult */, null /* accountName */,
3027 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003028 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003029 mFeatures = features;
3030 }
3031
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003032 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003033 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003034 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003035 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3036 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003037 }
Fred Quintana33269202009-04-20 16:05:10 -07003038 // check whether each account matches the requested features
3039 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
3040 mCurrentAccount = 0;
3041
3042 checkAccount();
3043 }
3044
3045 public void checkAccount() {
3046 if (mCurrentAccount >= mAccountsOfType.length) {
3047 sendResult();
3048 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07003049 }
Fred Quintana33269202009-04-20 16:05:10 -07003050
Fred Quintana29e94b82010-03-10 12:11:51 -08003051 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3052 if (accountAuthenticator == null) {
3053 // It is possible that the authenticator has died, which is indicated by
3054 // mAuthenticator being set to null. If this happens then just abort.
3055 // There is no need to send back a result or error in this case since
3056 // that already happened when mAuthenticator was cleared.
3057 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3058 Log.v(TAG, "checkAccount: aborting session since we are no longer"
3059 + " connected to the authenticator, " + toDebugString());
3060 }
3061 return;
3062 }
Fred Quintana33269202009-04-20 16:05:10 -07003063 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08003064 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07003065 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003066 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07003067 }
3068 }
3069
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003070 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003071 public void onResult(Bundle result) {
3072 mNumResults++;
3073 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003074 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07003075 return;
3076 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003077 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07003078 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3079 }
3080 mCurrentAccount++;
3081 checkAccount();
3082 }
3083
3084 public void sendResult() {
3085 IAccountManagerResponse response = getResponseAndClose();
3086 if (response != null) {
3087 try {
3088 Account[] accounts = new Account[mAccountsWithFeatures.size()];
3089 for (int i = 0; i < accounts.length; i++) {
3090 accounts[i] = mAccountsWithFeatures.get(i);
3091 }
Fred Quintana56285a62010-12-02 14:20:51 -08003092 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3093 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3094 + response);
3095 }
Fred Quintana33269202009-04-20 16:05:10 -07003096 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003097 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07003098 response.onResult(result);
3099 } catch (RemoteException e) {
3100 // if the caller is dead then there is no one to care about remote exceptions
3101 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3102 Log.v(TAG, "failure while notifying response", e);
3103 }
3104 }
3105 }
3106 }
3107
3108
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003109 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003110 protected String toDebugString(long now) {
3111 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3112 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3113 }
3114 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003115
Amith Yamasani04e0d262012-02-14 11:50:53 -08003116 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003117 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08003118 * @hide
3119 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003120 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003121 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003122 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07003123 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3124 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003125 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003126 return new Account[0];
3127 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003128 long identityToken = clearCallingIdentity();
3129 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003130 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003131 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003132 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003133 callingUid,
3134 null, // packageName
3135 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003136 } finally {
3137 restoreCallingIdentity(identityToken);
3138 }
3139 }
3140
Amith Yamasanif29f2362012-04-05 18:29:52 -07003141 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003142 * Returns accounts for all running users.
3143 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07003144 * @hide
3145 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003146 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003147 public AccountAndUser[] getRunningAccounts() {
3148 final int[] runningUserIds;
3149 try {
3150 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3151 } catch (RemoteException e) {
3152 // Running in system_server; should never happen
3153 throw new RuntimeException(e);
3154 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003155 return getAccounts(runningUserIds);
3156 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07003157
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003158 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003159 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003160 public AccountAndUser[] getAllAccounts() {
3161 final List<UserInfo> users = getUserManager().getUsers();
3162 final int[] userIds = new int[users.size()];
3163 for (int i = 0; i < userIds.length; i++) {
3164 userIds[i] = users.get(i).id;
3165 }
3166 return getAccounts(userIds);
3167 }
3168
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003169 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003170 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003171 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07003172 for (int userId : userIds) {
3173 UserAccounts userAccounts = getUserAccounts(userId);
3174 if (userAccounts == null) continue;
3175 synchronized (userAccounts.cacheLock) {
3176 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3177 Binder.getCallingUid(), null);
3178 for (int a = 0; a < accounts.length; a++) {
3179 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07003180 }
3181 }
3182 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003183
3184 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3185 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07003186 }
3187
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003188 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003189 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003190 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3191 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07003192 }
3193
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003194 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003195 private Account[] getAccountsAsUser(
3196 String type,
3197 int userId,
3198 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07003199 int packageUid,
3200 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003201 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003202 // Only allow the system process to read accounts of other users
3203 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07003204 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08003205 && mContext.checkCallingOrSelfPermission(
3206 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3207 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003208 throw new SecurityException("User " + UserHandle.getCallingUserId()
3209 + " trying to get account for " + userId);
3210 }
3211
Fred Quintana56285a62010-12-02 14:20:51 -08003212 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3213 Log.v(TAG, "getAccounts: accountType " + type
3214 + ", caller's uid " + Binder.getCallingUid()
3215 + ", pid " + Binder.getCallingPid());
3216 }
Amith Yamasani27db4682013-03-30 17:07:47 -07003217 // If the original calling app was using the framework account chooser activity, we'll
3218 // be passed in the original caller's uid here, which is what should be used for filtering.
3219 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3220 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07003221 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07003222 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003223
Svetoslavf3f02ac2015-09-08 14:36:35 -07003224 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3225 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003226 if (visibleAccountTypes.isEmpty()
3227 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003228 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003229 } else if (visibleAccountTypes.contains(type)) {
3230 // Prune the list down to just the requested type.
3231 visibleAccountTypes = new ArrayList<>();
3232 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07003233 } // else aggregate all the visible accounts (it won't matter if the
3234 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003235
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003236 long identityToken = clearCallingIdentity();
3237 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003238 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003239 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003240 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003241 callingUid,
3242 callingPackage,
3243 visibleAccountTypes);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003244 } finally {
3245 restoreCallingIdentity(identityToken);
3246 }
3247 }
3248
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003249 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003250 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003251 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003252 int callingUid,
3253 String callingPackage,
3254 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003255 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003256 ArrayList<Account> visibleAccounts = new ArrayList<>();
3257 for (String visibleType : visibleAccountTypes) {
3258 Account[] accountsForType = getAccountsFromCacheLocked(
3259 userAccounts, visibleType, callingUid, callingPackage);
3260 if (accountsForType != null) {
3261 visibleAccounts.addAll(Arrays.asList(accountsForType));
3262 }
3263 }
3264 Account[] result = new Account[visibleAccounts.size()];
3265 for (int i = 0; i < visibleAccounts.size(); i++) {
3266 result[i] = visibleAccounts.get(i);
3267 }
3268 return result;
3269 }
3270 }
3271
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003272 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003273 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3274 checkManageUsersPermission("addSharedAccountsFromParentUser");
3275 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3276 for (Account account : accounts) {
3277 addSharedAccountAsUser(account, userId);
3278 }
3279 }
3280
3281 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08003282 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003283 UserAccounts accounts = getUserAccounts(userId);
3284 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Amith Yamasani67df64b2012-12-14 12:09:36 -08003285 ContentValues values = new ContentValues();
3286 values.put(ACCOUNTS_NAME, account.name);
3287 values.put(ACCOUNTS_TYPE, account.type);
3288 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3289 new String[] {account.name, account.type});
3290 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3291 if (accountId < 0) {
3292 Log.w(TAG, "insertAccountIntoDatabase: " + account
3293 + ", skipping the DB insert failed");
3294 return false;
3295 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003296 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003297 return true;
3298 }
3299
3300 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003301 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3302 userId = handleIncomingUser(userId);
3303 UserAccounts accounts = getUserAccounts(userId);
3304 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003305 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003306 final ContentValues values = new ContentValues();
3307 values.put(ACCOUNTS_NAME, newName);
3308 values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
3309 int r = db.update(
3310 TABLE_SHARED_ACCOUNTS,
3311 values,
3312 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3313 new String[] { account.name, account.type });
3314 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003315 int callingUid = getCallingUid();
3316 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3317 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003318 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003319 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003320 }
3321 return r > 0;
3322 }
3323
3324 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08003325 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003326 return removeSharedAccountAsUser(account, userId, getCallingUid());
3327 }
3328
3329 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08003330 userId = handleIncomingUser(userId);
3331 UserAccounts accounts = getUserAccounts(userId);
3332 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003333 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003334 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3335 new String[] {account.name, account.type});
3336 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003337 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3338 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07003339 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003340 }
3341 return r > 0;
3342 }
3343
3344 @Override
3345 public Account[] getSharedAccountsAsUser(int userId) {
3346 userId = handleIncomingUser(userId);
3347 UserAccounts accounts = getUserAccounts(userId);
3348 ArrayList<Account> accountList = new ArrayList<Account>();
3349 Cursor cursor = null;
3350 try {
3351 cursor = accounts.openHelper.getReadableDatabase()
3352 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3353 null, null, null, null, null);
3354 if (cursor != null && cursor.moveToFirst()) {
3355 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3356 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3357 do {
3358 accountList.add(new Account(cursor.getString(nameIndex),
3359 cursor.getString(typeIndex)));
3360 } while (cursor.moveToNext());
3361 }
3362 } finally {
3363 if (cursor != null) {
3364 cursor.close();
3365 }
3366 }
3367 Account[] accountArray = new Account[accountList.size()];
3368 accountList.toArray(accountArray);
3369 return accountArray;
3370 }
3371
3372 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003373 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003374 public Account[] getAccounts(String type, String opPackageName) {
3375 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003376 }
3377
Amith Yamasani27db4682013-03-30 17:07:47 -07003378 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003379 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003380 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003381 int callingUid = Binder.getCallingUid();
3382 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3383 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3384 + callingUid + " with uid=" + uid);
3385 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07003386 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3387 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07003388 }
3389
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003390 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003391 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003392 public Account[] getAccountsByTypeForPackage(String type, String packageName,
3393 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003394 int packageUid = -1;
3395 try {
3396 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07003397 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3398 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003399 } catch (RemoteException re) {
3400 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3401 return new Account[0];
3402 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07003403 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3404 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003405 }
3406
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003407 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003408 public void getAccountsByFeatures(
3409 IAccountManagerResponse response,
3410 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07003411 String[] features,
3412 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003413 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003414 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3415 Log.v(TAG, "getAccounts: accountType " + type
3416 + ", response " + response
3417 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003418 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003419 + ", pid " + Binder.getCallingPid());
3420 }
Fred Quintana382601f2010-03-25 12:25:10 -07003421 if (response == null) throw new IllegalArgumentException("response is null");
3422 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003423 int userId = UserHandle.getCallingUserId();
3424
Svetoslavf3f02ac2015-09-08 14:36:35 -07003425 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3426 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003427 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003428 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003429 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003430 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
3431 try {
3432 response.onResult(result);
3433 } catch (RemoteException e) {
3434 Log.e(TAG, "Cannot respond to caller do to exception." , e);
3435 }
3436 return;
3437 }
Fred Quintana33269202009-04-20 16:05:10 -07003438 long identityToken = clearCallingIdentity();
3439 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003440 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003441 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003442 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003443 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003444 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003445 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08003446 Bundle result = new Bundle();
3447 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3448 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003449 return;
3450 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003451 new GetAccountsByTypeAndFeatureSession(
3452 userAccounts,
3453 response,
3454 type,
3455 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003456 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07003457 } finally {
3458 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07003459 }
3460 }
3461
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003462 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
3463 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
3464 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3465 try {
3466 if (cursor.moveToNext()) {
3467 return cursor.getLong(0);
3468 }
3469 return -1;
3470 } finally {
3471 cursor.close();
3472 }
3473 }
3474
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003475 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07003476 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003477 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07003478 try {
3479 if (cursor.moveToNext()) {
3480 return cursor.getLong(0);
3481 }
3482 return -1;
3483 } finally {
3484 cursor.close();
3485 }
3486 }
3487
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003488 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07003489 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
3490 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
3491 new String[]{key}, null, null, null);
3492 try {
3493 if (cursor.moveToNext()) {
3494 return cursor.getLong(0);
3495 }
3496 return -1;
3497 } finally {
3498 cursor.close();
3499 }
3500 }
3501
Fred Quintanaa698f422009-04-08 19:14:54 -07003502 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07003503 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07003504 IAccountManagerResponse mResponse;
3505 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07003506 final boolean mExpectActivityLaunch;
3507 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003508 final String mAccountName;
3509 // Indicates if we need to add auth details(like last credential time)
3510 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003511 // If set, we need to update the last authenticated time. This is
3512 // currently
3513 // used on
3514 // successful confirming credentials.
3515 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07003516
Fred Quintana33269202009-04-20 16:05:10 -07003517 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07003518 private int mNumRequestContinued = 0;
3519 private int mNumErrors = 0;
3520
Fred Quintana60307342009-03-24 22:48:12 -07003521 IAccountAuthenticator mAuthenticator = null;
3522
Fred Quintana8570f742010-02-18 10:32:54 -08003523 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003524 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08003525
Amith Yamasani04e0d262012-02-14 11:50:53 -08003526 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003527 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3528 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003529 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
3530 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
3531 }
3532
3533 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3534 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3535 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07003536 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08003537 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07003538 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08003539 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08003540 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07003541 mResponse = response;
3542 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07003543 mExpectActivityLaunch = expectActivityLaunch;
3544 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003545 mAccountName = accountName;
3546 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003547 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003548
Fred Quintanaa698f422009-04-08 19:14:54 -07003549 synchronized (mSessions) {
3550 mSessions.put(toString(), this);
3551 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08003552 if (response != null) {
3553 try {
3554 response.asBinder().linkToDeath(this, 0 /* flags */);
3555 } catch (RemoteException e) {
3556 mResponse = null;
3557 binderDied();
3558 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003559 }
Fred Quintana60307342009-03-24 22:48:12 -07003560 }
3561
Fred Quintanaa698f422009-04-08 19:14:54 -07003562 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07003563 if (mResponse == null) {
3564 // this session has already been closed
3565 return null;
3566 }
Fred Quintana60307342009-03-24 22:48:12 -07003567 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07003568 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07003569 return response;
3570 }
3571
Fred Quintanaa698f422009-04-08 19:14:54 -07003572 private void close() {
3573 synchronized (mSessions) {
3574 if (mSessions.remove(toString()) == null) {
3575 // the session was already closed, so bail out now
3576 return;
3577 }
3578 }
3579 if (mResponse != null) {
3580 // stop listening for response deaths
3581 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
3582
3583 // clear this so that we don't accidentally send any further results
3584 mResponse = null;
3585 }
3586 cancelTimeout();
3587 unbind();
3588 }
3589
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003590 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003591 public void binderDied() {
3592 mResponse = null;
3593 close();
3594 }
3595
3596 protected String toDebugString() {
3597 return toDebugString(SystemClock.elapsedRealtime());
3598 }
3599
3600 protected String toDebugString(long now) {
3601 return "Session: expectLaunch " + mExpectActivityLaunch
3602 + ", connected " + (mAuthenticator != null)
3603 + ", stats (" + mNumResults + "/" + mNumRequestContinued
3604 + "/" + mNumErrors + ")"
3605 + ", lifetime " + ((now - mCreationTime) / 1000.0);
3606 }
3607
Fred Quintana60307342009-03-24 22:48:12 -07003608 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07003609 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3610 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
3611 }
Fred Quintanab839afc2009-10-14 15:57:28 -07003612 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003613 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003614 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07003615 }
3616 }
3617
3618 private void unbind() {
3619 if (mAuthenticator != null) {
3620 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07003621 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07003622 }
3623 }
3624
Fred Quintana60307342009-03-24 22:48:12 -07003625 public void cancelTimeout() {
3626 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
3627 }
3628
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003629 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07003630 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07003631 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07003632 try {
3633 run();
3634 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003635 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07003636 "remote exception");
3637 }
Fred Quintana60307342009-03-24 22:48:12 -07003638 }
3639
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003640 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07003641 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003642 mAuthenticator = null;
3643 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003644 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07003645 try {
3646 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3647 "disconnected");
3648 } catch (RemoteException e) {
3649 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3650 Log.v(TAG, "Session.onServiceDisconnected: "
3651 + "caught RemoteException while responding", e);
3652 }
3653 }
Fred Quintana60307342009-03-24 22:48:12 -07003654 }
3655 }
3656
Fred Quintanab839afc2009-10-14 15:57:28 -07003657 public abstract void run() throws RemoteException;
3658
Fred Quintana60307342009-03-24 22:48:12 -07003659 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07003660 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003661 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07003662 try {
3663 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3664 "timeout");
3665 } catch (RemoteException e) {
3666 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3667 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
3668 e);
3669 }
3670 }
Fred Quintana60307342009-03-24 22:48:12 -07003671 }
3672 }
3673
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003674 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003675 public void onResult(Bundle result) {
3676 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003677 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003678 if (result != null) {
3679 boolean isSuccessfulConfirmCreds = result.getBoolean(
3680 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003681 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003682 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
3683 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07003684 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003685 // mUpdateLastAuthenticatedTime is true and the confirmRequest
3686 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07003687 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003688 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003689 if (needUpdate || mAuthDetailsRequired) {
3690 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
3691 if (needUpdate && accountPresent) {
3692 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
3693 }
3694 if (mAuthDetailsRequired) {
3695 long lastAuthenticatedTime = -1;
3696 if (accountPresent) {
3697 lastAuthenticatedTime = DatabaseUtils.longForQuery(
3698 mAccounts.openHelper.getReadableDatabase(),
3699 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
3700 + " from " +
3701 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
3702 + ACCOUNTS_TYPE + "=?",
3703 new String[] {
3704 mAccountName, mAccountType
3705 });
3706 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07003707 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003708 lastAuthenticatedTime);
3709 }
3710 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003711 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003712 if (result != null
3713 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
3714 /*
3715 * The Authenticator API allows third party authenticators to
3716 * supply arbitrary intents to other apps that they can run,
3717 * this can be very bad when those apps are in the system like
3718 * the System Settings.
3719 */
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003720 int authenticatorUid = Binder.getCallingUid();
Carlos Valdivia416747a2013-10-14 17:22:42 -07003721 long bid = Binder.clearCallingIdentity();
3722 try {
3723 PackageManager pm = mContext.getPackageManager();
3724 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
3725 int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
3726 if (PackageManager.SIGNATURE_MATCH !=
3727 pm.checkSignatures(authenticatorUid, targetUid)) {
3728 throw new SecurityException(
3729 "Activity to be started with KEY_INTENT must " +
3730 "share Authenticator's signatures");
3731 }
3732 } finally {
3733 Binder.restoreCallingIdentity(bid);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003734 }
3735 }
3736 if (result != null
3737 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003738 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
3739 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003740 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
3741 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003742 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
3743 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003744 }
Fred Quintana60307342009-03-24 22:48:12 -07003745 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003746 IAccountManagerResponse response;
3747 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003748 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003749 response = mResponse;
3750 } else {
3751 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003752 }
Fred Quintana60307342009-03-24 22:48:12 -07003753 if (response != null) {
3754 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07003755 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08003756 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3757 Log.v(TAG, getClass().getSimpleName()
3758 + " calling onError() on response " + response);
3759 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003760 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07003761 "null bundle returned");
3762 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08003763 if (mStripAuthTokenFromResult) {
3764 result.remove(AccountManager.KEY_AUTHTOKEN);
3765 }
Fred Quintana56285a62010-12-02 14:20:51 -08003766 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3767 Log.v(TAG, getClass().getSimpleName()
3768 + " calling onResult() on response " + response);
3769 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003770 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
3771 (intent == null)) {
3772 // All AccountManager error codes are greater than 0
3773 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
3774 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3775 } else {
3776 response.onResult(result);
3777 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003778 }
Fred Quintana60307342009-03-24 22:48:12 -07003779 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003780 // if the caller is dead then there is no one to care about remote exceptions
3781 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3782 Log.v(TAG, "failure while notifying response", e);
3783 }
Fred Quintana60307342009-03-24 22:48:12 -07003784 }
3785 }
3786 }
Fred Quintana60307342009-03-24 22:48:12 -07003787
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003788 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003789 public void onRequestContinued() {
3790 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07003791 }
3792
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003793 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003794 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003795 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07003796 IAccountManagerResponse response = getResponseAndClose();
3797 if (response != null) {
3798 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08003799 Log.v(TAG, getClass().getSimpleName()
3800 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07003801 }
3802 try {
3803 response.onError(errorCode, errorMessage);
3804 } catch (RemoteException e) {
3805 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3806 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
3807 }
3808 }
3809 } else {
3810 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3811 Log.v(TAG, "Session.onError: already closed");
3812 }
Fred Quintana60307342009-03-24 22:48:12 -07003813 }
3814 }
Fred Quintanab839afc2009-10-14 15:57:28 -07003815
3816 /**
3817 * find the component name for the authenticator and initiate a bind
3818 * if no authenticator or the bind fails then return false, otherwise return true
3819 */
3820 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003821 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
3822 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
3823 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07003824 if (authenticatorInfo == null) {
3825 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3826 Log.v(TAG, "there is no authenticator for " + authenticatorType
3827 + ", bailing out");
3828 }
3829 return false;
3830 }
3831
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07003832 final ActivityManager am = mContext.getSystemService(ActivityManager.class);
3833 if (am.isUserRunningAndLocked(mAccounts.userId)
3834 && !authenticatorInfo.componentInfo.encryptionAware) {
3835 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
3836 + " which isn't encryption aware");
3837 return false;
3838 }
3839
Fred Quintanab839afc2009-10-14 15:57:28 -07003840 Intent intent = new Intent();
3841 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
3842 intent.setComponent(authenticatorInfo.componentName);
3843 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3844 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
3845 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08003846 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
3847 new UserHandle(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07003848 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3849 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
3850 }
3851 return false;
3852 }
3853
Fred Quintanab839afc2009-10-14 15:57:28 -07003854 return true;
3855 }
Fred Quintana60307342009-03-24 22:48:12 -07003856 }
3857
3858 private class MessageHandler extends Handler {
3859 MessageHandler(Looper looper) {
3860 super(looper);
3861 }
Costin Manolache3348f142009-09-29 18:58:36 -07003862
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003863 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003864 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07003865 switch (msg.what) {
3866 case MESSAGE_TIMED_OUT:
3867 Session session = (Session)msg.obj;
3868 session.onTimedOut();
3869 break;
3870
Amith Yamasani5be347b2013-03-31 17:44:31 -07003871 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00003872 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07003873 break;
3874
Fred Quintana60307342009-03-24 22:48:12 -07003875 default:
3876 throw new IllegalStateException("unhandled message: " + msg.what);
3877 }
3878 }
3879 }
3880
Amith Yamasani04e0d262012-02-14 11:50:53 -08003881 private static String getDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07003882 File systemDir = Environment.getDataSystemDirectory();
Amith Yamasani61f57372012-08-31 12:12:28 -07003883 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003884 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07003885 // Migrate old file, if it exists, to the new location.
3886 // Make sure the new file doesn't already exist. A dummy file could have been
3887 // accidentally created in the old location, causing the new one to become corrupted
3888 // as well.
Amith Yamasani04e0d262012-02-14 11:50:53 -08003889 File oldFile = new File(systemDir, DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07003890 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07003891 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07003892 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07003893 if (!userDir.exists()) {
3894 if (!userDir.mkdirs()) {
3895 throw new IllegalStateException("User dir cannot be created: " + userDir);
3896 }
3897 }
3898 if (!oldFile.renameTo(databaseFile)) {
3899 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
3900 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003901 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08003902 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003903 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08003904 }
3905
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003906 private static class DebugDbHelper{
3907 private DebugDbHelper() {
3908 }
3909
3910 private static String TABLE_DEBUG = "debug_table";
3911
3912 // Columns for the table
3913 private static String ACTION_TYPE = "action_type";
3914 private static String TIMESTAMP = "time";
3915 private static String CALLER_UID = "caller_uid";
3916 private static String TABLE_NAME = "table_name";
3917 private static String KEY = "primary_key";
3918
3919 // These actions correspond to the occurrence of real actions. Since
3920 // these are called by the authenticators, the uid associated will be
3921 // of the authenticator.
3922 private static String ACTION_SET_PASSWORD = "action_set_password";
3923 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
3924 private static String ACTION_ACCOUNT_ADD = "action_account_add";
3925 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
3926 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
3927 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
3928
3929 // These actions don't necessarily correspond to any action on
3930 // accountDb taking place. As an example, there might be a request for
3931 // addingAccount, which might not lead to addition of account on grounds
3932 // of bad authentication. We will still be logging it to keep track of
3933 // who called.
3934 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
3935 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003936
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003937 //This action doesn't add account to accountdb. Account is only
3938 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08003939 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003940 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
3941 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08003942
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003943 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3944
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003945 private static void createDebugTable(SQLiteDatabase db) {
3946 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
3947 + ACCOUNTS_ID + " INTEGER,"
3948 + ACTION_TYPE + " TEXT NOT NULL, "
3949 + TIMESTAMP + " DATETIME,"
3950 + CALLER_UID + " INTEGER NOT NULL,"
3951 + TABLE_NAME + " TEXT NOT NULL,"
3952 + KEY + " INTEGER PRIMARY KEY)");
3953 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
3954 }
3955 }
3956
3957 private void logRecord(UserAccounts accounts, String action, String tableName) {
3958 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3959 logRecord(db, action, tableName, -1, accounts);
3960 }
3961
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003962 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
3963 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3964 logRecord(db, action, tableName, -1, accounts, uid);
3965 }
3966
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003967 /*
3968 * This function receives an opened writable database.
3969 */
3970 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3971 UserAccounts userAccount) {
3972 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
3973 }
3974
3975 /*
3976 * This function receives an opened writable database.
3977 */
3978 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3979 UserAccounts userAccount, int callingUid) {
3980 SQLiteStatement logStatement = userAccount.statementForLogging;
3981 logStatement.bindLong(1, accountId);
3982 logStatement.bindString(2, action);
3983 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
3984 logStatement.bindLong(4, callingUid);
3985 logStatement.bindString(5, tableName);
3986 logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
3987 logStatement.execute();
3988 logStatement.clearBindings();
3989 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
3990 % MAX_DEBUG_DB_SIZE;
3991 }
3992
3993 /*
3994 * This should only be called once to compile the sql statement for logging
3995 * and to find the insertion point.
3996 */
3997 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
3998 UserAccounts userAccount) {
3999 // Initialize the count if not done earlier.
4000 int size = (int) getDebugTableRowCount(db);
4001 if (size >= MAX_DEBUG_DB_SIZE) {
4002 // Table is full, and we need to find the point where to insert.
4003 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
4004 } else {
4005 userAccount.debugDbInsertionPoint = size;
4006 }
4007 compileSqlStatementForLogging(db, userAccount);
4008 }
4009
4010 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
4011 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
4012 + " VALUES (?,?,?,?,?,?)";
4013 userAccount.statementForLogging = db.compileStatement(sql);
4014 }
4015
4016 private long getDebugTableRowCount(SQLiteDatabase db) {
4017 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
4018 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4019 }
4020
4021 /*
4022 * Finds the row key where the next insertion should take place. This should
4023 * be invoked only if the table has reached its full capacity.
4024 */
4025 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
4026 // This query finds the smallest timestamp value (and if 2 records have
4027 // same timestamp, the choose the lower id).
4028 String queryCountDebugDbRows = new StringBuilder()
4029 .append("SELECT ").append(DebugDbHelper.KEY)
4030 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
4031 .append(" ORDER BY ")
4032 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
4033 .append(" LIMIT 1")
4034 .toString();
4035 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4036 }
4037
Amith Yamasani04e0d262012-02-14 11:50:53 -08004038 static class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08004039
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004040 private final Context mContext;
4041 private final int mUserId;
4042
Amith Yamasani04e0d262012-02-14 11:50:53 -08004043 public DatabaseHelper(Context context, int userId) {
4044 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004045 mContext = context;
4046 mUserId = userId;
Fred Quintana60307342009-03-24 22:48:12 -07004047 }
4048
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004049 /**
4050 * This call needs to be made while the mCacheLock is held. The way to
4051 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
4052 * @param db The database.
4053 */
Fred Quintana60307342009-03-24 22:48:12 -07004054 @Override
4055 public void onCreate(SQLiteDatabase db) {
4056 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4057 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4058 + ACCOUNTS_NAME + " TEXT NOT NULL, "
4059 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4060 + ACCOUNTS_PASSWORD + " TEXT, "
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004061 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004062 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
Fred Quintana60307342009-03-24 22:48:12 -07004063 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4064
4065 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
4066 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4067 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4068 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
4069 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
4070 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
4071
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004072 createGrantsTable(db);
4073
Fred Quintana60307342009-03-24 22:48:12 -07004074 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
4075 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4076 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
4077 + EXTRAS_KEY + " TEXT NOT NULL, "
4078 + EXTRAS_VALUE + " TEXT, "
4079 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
4080
4081 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4082 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4083 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07004084
Amith Yamasani67df64b2012-12-14 12:09:36 -08004085 createSharedAccountsTable(db);
4086
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004087 createAccountsDeletionTrigger(db);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004088
4089 DebugDbHelper.createDebugTable(db);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004090 }
4091
Amith Yamasani67df64b2012-12-14 12:09:36 -08004092 private void createSharedAccountsTable(SQLiteDatabase db) {
4093 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4094 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4095 + ACCOUNTS_NAME + " TEXT NOT NULL, "
4096 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4097 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4098 }
4099
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004100 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4101 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4102 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4103 }
4104
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004105 private void addOldAccountNameColumn(SQLiteDatabase db) {
4106 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4107 }
4108
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004109 private void addDebugTable(SQLiteDatabase db) {
4110 DebugDbHelper.createDebugTable(db);
4111 }
4112
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004113 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004114 db.execSQL(""
4115 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4116 + " BEGIN"
4117 + " DELETE FROM " + TABLE_AUTHTOKENS
4118 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4119 + " DELETE FROM " + TABLE_EXTRAS
4120 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004121 + " DELETE FROM " + TABLE_GRANTS
4122 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07004123 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07004124 }
4125
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004126 private void createGrantsTable(SQLiteDatabase db) {
4127 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
4128 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4129 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
4130 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
4131 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4132 + "," + GRANTS_GRANTEE_UID + "))");
4133 }
4134
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004135 private void populateMetaTableWithAuthTypeAndUID(
4136 SQLiteDatabase db,
4137 Map<String, Integer> authTypeAndUIDMap) {
4138 Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator();
4139 while (iterator.hasNext()) {
4140 Entry<String, Integer> entry = iterator.next();
4141 ContentValues values = new ContentValues();
4142 values.put(META_KEY,
4143 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
4144 values.put(META_VALUE, entry.getValue());
4145 db.insert(TABLE_META, null, values);
4146 }
4147 }
4148
Fred Quintana60307342009-03-24 22:48:12 -07004149 @Override
4150 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004151 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07004152
Fred Quintanaa698f422009-04-08 19:14:54 -07004153 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004154 // no longer need to do anything since the work is done
4155 // when upgrading from version 2
4156 oldVersion++;
4157 }
4158
4159 if (oldVersion == 2) {
4160 createGrantsTable(db);
4161 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4162 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07004163 oldVersion++;
4164 }
Costin Manolache3348f142009-09-29 18:58:36 -07004165
4166 if (oldVersion == 3) {
4167 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4168 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4169 oldVersion++;
4170 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004171
4172 if (oldVersion == 4) {
4173 createSharedAccountsTable(db);
4174 oldVersion++;
4175 }
4176
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004177 if (oldVersion == 5) {
4178 addOldAccountNameColumn(db);
4179 oldVersion++;
4180 }
4181
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004182 if (oldVersion == 6) {
4183 addLastSuccessfullAuthenticatedTimeColumn(db);
4184 oldVersion++;
4185 }
4186
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004187 if (oldVersion == 7) {
4188 addDebugTable(db);
4189 oldVersion++;
4190 }
4191
Sandra Kwan1c9026d2016-02-23 10:22:15 -08004192 if (oldVersion == 8) {
4193 populateMetaTableWithAuthTypeAndUID(
4194 db,
4195 AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
4196 oldVersion++;
4197 }
4198
Amith Yamasani67df64b2012-12-14 12:09:36 -08004199 if (oldVersion != newVersion) {
4200 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4201 }
Fred Quintana60307342009-03-24 22:48:12 -07004202 }
4203
4204 @Override
4205 public void onOpen(SQLiteDatabase db) {
4206 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4207 }
4208 }
4209
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004210 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07004211 return asBinder();
4212 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004213
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004214 /**
4215 * Searches array of arguments for the specified string
4216 * @param args array of argument strings
4217 * @param value value to search for
4218 * @return true if the value is contained in the array
4219 */
4220 private static boolean scanArgs(String[] args, String value) {
4221 if (args != null) {
4222 for (String arg : args) {
4223 if (value.equals(arg)) {
4224 return true;
4225 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004226 }
4227 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004228 return false;
4229 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004230
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004231 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004232 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07004233 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4234 != PackageManager.PERMISSION_GRANTED) {
4235 fout.println("Permission Denial: can't dump AccountsManager from from pid="
4236 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4237 + " without permission " + android.Manifest.permission.DUMP);
4238 return;
4239 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004240 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004241 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07004242
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004243 final List<UserInfo> users = getUserManager().getUsers();
4244 for (UserInfo user : users) {
4245 ipw.println("User " + user + ":");
4246 ipw.increaseIndent();
4247 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4248 ipw.println();
4249 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08004250 }
4251 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004252
Amith Yamasani04e0d262012-02-14 11:50:53 -08004253 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4254 String[] args, boolean isCheckinRequest) {
4255 synchronized (userAccounts.cacheLock) {
4256 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004257
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004258 if (isCheckinRequest) {
4259 // This is a checkin request. *Only* upload the account types and the count of each.
4260 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
4261 null, null, ACCOUNTS_TYPE, null, null);
4262 try {
4263 while (cursor.moveToNext()) {
4264 // print type,count
4265 fout.println(cursor.getString(0) + "," + cursor.getString(1));
4266 }
4267 } finally {
4268 if (cursor != null) {
4269 cursor.close();
4270 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004271 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004272 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004273 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07004274 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004275 fout.println("Accounts: " + accounts.length);
4276 for (Account account : accounts) {
4277 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004278 }
Fred Quintana307da1a2010-01-21 14:24:20 -08004279
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004280 // Add debug information.
4281 fout.println();
4282 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
4283 null, null, null, null, DebugDbHelper.TIMESTAMP);
4284 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
4285 fout.println("Accounts History");
4286 try {
4287 while (cursor.moveToNext()) {
4288 // print type,count
4289 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
4290 cursor.getString(2) + "," + cursor.getString(3) + ","
4291 + cursor.getString(4) + "," + cursor.getString(5));
4292 }
4293 } finally {
4294 cursor.close();
4295 }
4296
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004297 fout.println();
4298 synchronized (mSessions) {
4299 final long now = SystemClock.elapsedRealtime();
4300 fout.println("Active Sessions: " + mSessions.size());
4301 for (Session session : mSessions.values()) {
4302 fout.println(" " + session.toDebugString(now));
4303 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004304 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004305
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004306 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004307 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004308 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004309 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004310 }
4311
Amith Yamasani04e0d262012-02-14 11:50:53 -08004312 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Dianne Hackborn41203752012-08-31 14:05:51 -07004313 Intent intent, int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004314 long identityToken = clearCallingIdentity();
4315 try {
4316 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4317 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4318 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004319
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004320 if (intent.getComponent() != null &&
4321 GrantCredentialsPermissionActivity.class.getName().equals(
4322 intent.getComponent().getClassName())) {
Dianne Hackborn41203752012-08-31 14:05:51 -07004323 createNoCredentialsPermissionNotification(account, intent, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004324 } else {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004325 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07004326 intent.addCategory(String.valueOf(notificationId));
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004327 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004328 Context contextForUser = getContextForUser(user);
Fred Quintana33f889a2009-09-14 17:31:26 -07004329 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004330 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004331 Notification n = new Notification.Builder(contextForUser)
4332 .setWhen(0)
4333 .setSmallIcon(android.R.drawable.stat_sys_warning)
4334 .setColor(contextForUser.getColor(
4335 com.android.internal.R.color.system_notification_accent_color))
4336 .setContentTitle(String.format(notificationTitleFormat, account.name))
4337 .setContentText(message)
4338 .setContentIntent(PendingIntent.getActivityAsUser(
4339 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
4340 null, user))
4341 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004342 installNotification(notificationId, n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004343 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004344 } finally {
4345 restoreCallingIdentity(identityToken);
4346 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004347 }
4348
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004349 protected void installNotification(final int notificationId, final Notification n,
4350 UserHandle user) {
Fred Quintana56285a62010-12-02 14:20:51 -08004351 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004352 .notifyAsUser(null, notificationId, n, user);
Fred Quintana56285a62010-12-02 14:20:51 -08004353 }
4354
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004355 protected void cancelNotification(int id, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004356 long identityToken = clearCallingIdentity();
4357 try {
4358 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004359 .cancelAsUser(null, id, user);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004360 } finally {
4361 restoreCallingIdentity(identityToken);
4362 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004363 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004364
Carlos Valdiviabf33bd42016-02-02 20:20:42 +00004365 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4366 for (String perm : permissions) {
4367 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4368 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4369 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
4370 }
4371 final int opCode = AppOpsManager.permissionToOpCode(perm);
4372 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4373 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4374 return true;
4375 }
4376 }
4377 }
4378 return false;
4379 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004380
Amith Yamasani67df64b2012-12-14 12:09:36 -08004381 private int handleIncomingUser(int userId) {
4382 try {
4383 return ActivityManagerNative.getDefault().handleIncomingUser(
4384 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4385 } catch (RemoteException re) {
4386 // Shouldn't happen, local.
4387 }
4388 return userId;
4389 }
4390
Christopher Tateccbf84f2013-05-08 15:25:41 -07004391 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004392 final int callingUserId = UserHandle.getUserId(callingUid);
4393
4394 final PackageManager userPackageManager;
4395 try {
4396 userPackageManager = mContext.createPackageContextAsUser(
4397 "android", 0, new UserHandle(callingUserId)).getPackageManager();
4398 } catch (NameNotFoundException e) {
4399 return false;
4400 }
4401
4402 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07004403 for (String name : packages) {
4404 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004405 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08004406 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08004407 && (packageInfo.applicationInfo.privateFlags
4408 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07004409 return true;
4410 }
4411 } catch (PackageManager.NameNotFoundException e) {
4412 return false;
4413 }
4414 }
4415 return false;
4416 }
4417
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004418 private boolean permissionIsGranted(
4419 Account account, String authTokenType, int callerUid, int userId) {
Christopher Tateccbf84f2013-05-08 15:25:41 -07004420 final boolean isPrivileged = isPrivileged(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07004421 final boolean fromAuthenticator = account != null
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004422 && isAccountManagedByCaller(account.type, callerUid, userId);
Fred Quintana31957f12009-10-21 13:43:10 -07004423 final boolean hasExplicitGrants = account != null
Amith Yamasani04e0d262012-02-14 11:50:53 -08004424 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004425 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4426 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08004427 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004428 + ": is authenticator? " + fromAuthenticator
4429 + ", has explicit permission? " + hasExplicitGrants);
4430 }
Christopher Tateccbf84f2013-05-08 15:25:41 -07004431 return fromAuthenticator || hasExplicitGrants || isPrivileged;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004432 }
4433
Svetoslavf3f02ac2015-09-08 14:36:35 -07004434 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
4435 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004436 if (accountType == null) {
4437 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004438 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07004439 return getTypesVisibleToCaller(callingUid, userId,
4440 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004441 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004442 }
4443
4444 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
4445 if (accountType == null) {
4446 return false;
4447 } else {
4448 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
4449 }
4450 }
4451
Svetoslavf3f02ac2015-09-08 14:36:35 -07004452 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
4453 String opPackageName) {
Carlos Valdiviabf33bd42016-02-02 20:20:42 +00004454 boolean isPermitted =
4455 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
4456 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004457 return getTypesForCaller(callingUid, userId, isPermitted);
4458 }
4459
4460 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
4461 return getTypesForCaller(callingUid, userId, false);
4462 }
4463
4464 private List<String> getTypesForCaller(
4465 int callingUid, int userId, boolean isOtherwisePermitted) {
4466 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004467 long identityToken = Binder.clearCallingIdentity();
4468 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
4469 try {
4470 serviceInfos = mAuthenticatorCache.getAllServices(userId);
4471 } finally {
4472 Binder.restoreCallingIdentity(identityToken);
4473 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004474 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004475 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004476 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
4477 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
4478 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004479 }
4480 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004481 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004482 }
4483
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004484 private boolean isAccountPresentForCaller(String accountName, String accountType) {
4485 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
4486 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
4487 if (account.name.equals(accountName)) {
4488 return true;
4489 }
4490 }
4491 }
4492 return false;
4493 }
4494
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004495 private static void checkManageUsersPermission(String message) {
4496 if (ActivityManager.checkComponentPermission(
4497 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
4498 != PackageManager.PERMISSION_GRANTED) {
4499 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
4500 }
4501 }
4502
Amith Yamasani04e0d262012-02-14 11:50:53 -08004503 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
4504 int callerUid) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004505 if (callerUid == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004506 return true;
4507 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004508 UserAccounts accounts = getUserAccountsForCaller();
4509 synchronized (accounts.cacheLock) {
4510 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4511 String[] args = { String.valueOf(callerUid), authTokenType,
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004512 account.name, account.type};
4513 final boolean permissionGranted =
4514 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
4515 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
4516 // TODO: Skip this check when running automated tests. Replace this
4517 // with a more general solution.
4518 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08004519 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004520 + " but ignoring since device is in test harness.");
4521 return true;
4522 }
4523 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004524 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004525 }
4526
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004527 private boolean isSystemUid(int callingUid) {
4528 String[] packages = null;
4529 long ident = Binder.clearCallingIdentity();
4530 try {
4531 packages = mPackageManager.getPackagesForUid(callingUid);
4532 } finally {
4533 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07004534 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004535 if (packages != null) {
4536 for (String name : packages) {
4537 try {
4538 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
4539 if (packageInfo != null
4540 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
4541 != 0) {
4542 return true;
4543 }
4544 } catch (PackageManager.NameNotFoundException e) {
4545 Log.w(TAG, String.format("Could not find package [%s]", name), e);
4546 }
4547 }
4548 } else {
4549 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07004550 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004551 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00004552 }
4553
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004554 /** Succeeds if any of the specified permissions are granted. */
4555 private void checkReadAccountsPermitted(
4556 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004557 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004558 int userId,
4559 String opPackageName) {
4560 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004561 String msg = String.format(
4562 "caller uid %s cannot access %s accounts",
4563 callingUid,
4564 accountType);
4565 Log.w(TAG, " " + msg);
4566 throw new SecurityException(msg);
4567 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004568 }
4569
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004570 private boolean canUserModifyAccounts(int userId, int callingUid) {
4571 // the managing app can always modify accounts
4572 if (isProfileOwner(callingUid)) {
4573 return true;
4574 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004575 if (getUserManager().getUserRestrictions(new UserHandle(userId))
4576 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
4577 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08004578 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004579 return true;
4580 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004581
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004582 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
4583 // the managing app can always modify accounts
4584 if (isProfileOwner(callingUid)) {
4585 return true;
4586 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004587 DevicePolicyManager dpm = (DevicePolicyManager) mContext
4588 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004589 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02004590 if (typesArray == null) {
4591 return true;
4592 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004593 for (String forbiddenType : typesArray) {
4594 if (forbiddenType.equals(accountType)) {
4595 return false;
4596 }
4597 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08004598 return true;
4599 }
4600
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004601 private boolean isProfileOwner(int uid) {
4602 final DevicePolicyManagerInternal dpmi =
4603 LocalServices.getService(DevicePolicyManagerInternal.class);
4604 return (dpmi != null)
4605 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
4606 }
4607
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004608 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07004609 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
4610 throws RemoteException {
4611 final int callingUid = getCallingUid();
4612
Amith Yamasani27db4682013-03-30 17:07:47 -07004613 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07004614 throw new SecurityException();
4615 }
4616
4617 if (value) {
4618 grantAppPermission(account, authTokenType, uid);
4619 } else {
4620 revokeAppPermission(account, authTokenType, uid);
4621 }
4622 }
4623
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004624 /**
4625 * Allow callers with the given uid permission to get credentials for account/authTokenType.
4626 * <p>
4627 * Although this is public it can only be accessed via the AccountManagerService object
4628 * which is in the system. This means we don't need to protect it with permissions.
4629 * @hide
4630 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07004631 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07004632 if (account == null || authTokenType == null) {
4633 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07004634 return;
4635 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07004636 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004637 synchronized (accounts.cacheLock) {
4638 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004639 db.beginTransaction();
4640 try {
4641 long accountId = getAccountIdLocked(db, account);
4642 if (accountId >= 0) {
4643 ContentValues values = new ContentValues();
4644 values.put(GRANTS_ACCOUNTS_ID, accountId);
4645 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
4646 values.put(GRANTS_GRANTEE_UID, uid);
4647 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
4648 db.setTransactionSuccessful();
4649 }
4650 } finally {
4651 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004652 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004653 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4654 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004655 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004656 }
4657
4658 /**
4659 * Don't allow callers with the given uid permission to get credentials for
4660 * account/authTokenType.
4661 * <p>
4662 * Although this is public it can only be accessed via the AccountManagerService object
4663 * which is in the system. This means we don't need to protect it with permissions.
4664 * @hide
4665 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07004666 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07004667 if (account == null || authTokenType == null) {
4668 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07004669 return;
4670 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07004671 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004672 synchronized (accounts.cacheLock) {
4673 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004674 db.beginTransaction();
4675 try {
4676 long accountId = getAccountIdLocked(db, account);
4677 if (accountId >= 0) {
4678 db.delete(TABLE_GRANTS,
4679 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
4680 + GRANTS_GRANTEE_UID + "=?",
4681 new String[]{String.valueOf(accountId), authTokenType,
4682 String.valueOf(uid)});
4683 db.setTransactionSuccessful();
4684 }
4685 } finally {
4686 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004687 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004688 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4689 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004690 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004691 }
Fred Quintana56285a62010-12-02 14:20:51 -08004692
4693 static final private String stringArrayToString(String[] value) {
4694 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
4695 }
4696
Amith Yamasani04e0d262012-02-14 11:50:53 -08004697 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
4698 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004699 if (oldAccountsForType != null) {
4700 ArrayList<Account> newAccountsList = new ArrayList<Account>();
4701 for (Account curAccount : oldAccountsForType) {
4702 if (!curAccount.equals(account)) {
4703 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004704 }
4705 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004706 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004707 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004708 } else {
4709 Account[] newAccountsForType = new Account[newAccountsList.size()];
4710 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004711 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004712 }
Fred Quintana56285a62010-12-02 14:20:51 -08004713 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004714 accounts.userDataCache.remove(account);
4715 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004716 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004717 }
4718
4719 /**
4720 * This assumes that the caller has already checked that the account is not already present.
4721 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08004722 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
4723 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004724 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
4725 Account[] newAccountsForType = new Account[oldLength + 1];
4726 if (accountsForType != null) {
4727 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08004728 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004729 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004730 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08004731 }
4732
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004733 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07004734 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004735 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07004736 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004737 return unfiltered;
4738 }
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004739 UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
4740 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004741 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004742 // If any of the packages is a white listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004743 // otherwise return non-shared accounts only.
4744 // This might be a temporary way to specify a whitelist
4745 String whiteList = mContext.getResources().getString(
4746 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
4747 for (String packageName : packages) {
4748 if (whiteList.contains(";" + packageName + ";")) {
4749 return unfiltered;
4750 }
4751 }
4752 ArrayList<Account> allowed = new ArrayList<Account>();
4753 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
4754 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004755 String requiredAccountType = "";
4756 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07004757 // If there's an explicit callingPackage specified, check if that package
4758 // opted in to see restricted accounts.
4759 if (callingPackage != null) {
4760 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004761 if (pi != null && pi.restrictedAccountType != null) {
4762 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07004763 }
4764 } else {
4765 // Otherwise check if the callingUid has a package that has opted in
4766 for (String packageName : packages) {
4767 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
4768 if (pi != null && pi.restrictedAccountType != null) {
4769 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07004770 break;
4771 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004772 }
4773 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004774 } catch (NameNotFoundException nnfe) {
4775 }
4776 for (Account account : unfiltered) {
4777 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004778 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004779 } else {
4780 boolean found = false;
4781 for (Account shared : sharedAccounts) {
4782 if (shared.equals(account)) {
4783 found = true;
4784 break;
4785 }
4786 }
4787 if (!found) {
4788 allowed.add(account);
4789 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004790 }
4791 }
4792 Account[] filtered = new Account[allowed.size()];
4793 allowed.toArray(filtered);
4794 return filtered;
4795 } else {
4796 return unfiltered;
4797 }
4798 }
4799
Amith Yamasani27db4682013-03-30 17:07:47 -07004800 /*
4801 * packageName can be null. If not null, it should be used to filter out restricted accounts
4802 * that the package is not allowed to access.
4803 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004804 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07004805 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004806 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004807 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004808 if (accounts == null) {
4809 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08004810 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004811 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07004812 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08004813 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004814 } else {
4815 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004816 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004817 totalLength += accounts.length;
4818 }
4819 if (totalLength == 0) {
4820 return EMPTY_ACCOUNT_ARRAY;
4821 }
4822 Account[] accounts = new Account[totalLength];
4823 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004824 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004825 System.arraycopy(accountsOfType, 0, accounts, totalLength,
4826 accountsOfType.length);
4827 totalLength += accountsOfType.length;
4828 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004829 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08004830 }
4831 }
4832
Amith Yamasani04e0d262012-02-14 11:50:53 -08004833 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4834 Account account, String key, String value) {
4835 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004836 if (userDataForAccount == null) {
4837 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004838 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004839 }
4840 if (value == null) {
4841 userDataForAccount.remove(key);
4842 } else {
4843 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004844 }
4845 }
4846
Carlos Valdivia91979be2015-05-22 14:11:35 -07004847 protected String readCachedTokenInternal(
4848 UserAccounts accounts,
4849 Account account,
4850 String tokenType,
4851 String callingPackage,
4852 byte[] pkgSigDigest) {
4853 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004854 return accounts.accountTokenCaches.get(
4855 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004856 }
4857 }
4858
Amith Yamasani04e0d262012-02-14 11:50:53 -08004859 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4860 Account account, String key, String value) {
4861 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004862 if (authTokensForAccount == null) {
4863 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004864 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004865 }
4866 if (value == null) {
4867 authTokensForAccount.remove(key);
4868 } else {
4869 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004870 }
4871 }
4872
Amith Yamasani04e0d262012-02-14 11:50:53 -08004873 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
4874 String authTokenType) {
4875 synchronized (accounts.cacheLock) {
4876 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004877 if (authTokensForAccount == null) {
4878 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08004879 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004880 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004881 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004882 }
4883 return authTokensForAccount.get(authTokenType);
4884 }
4885 }
4886
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00004887 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
4888 synchronized (accounts.cacheLock) {
4889 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
4890 if (userDataForAccount == null) {
4891 // need to populate the cache for this account
4892 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4893 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
4894 accounts.userDataCache.put(account, userDataForAccount);
4895 }
4896 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08004897 }
4898 }
4899
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004900 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
4901 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004902 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004903 Cursor cursor = db.query(TABLE_EXTRAS,
4904 COLUMNS_EXTRAS_KEY_AND_VALUE,
4905 SELECTION_USERDATA_BY_ACCOUNT,
4906 new String[]{account.name, account.type},
4907 null, null, null);
4908 try {
4909 while (cursor.moveToNext()) {
4910 final String tmpkey = cursor.getString(0);
4911 final String value = cursor.getString(1);
4912 userDataForAccount.put(tmpkey, value);
4913 }
4914 } finally {
4915 cursor.close();
4916 }
4917 return userDataForAccount;
4918 }
4919
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004920 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
4921 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004922 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004923 Cursor cursor = db.query(TABLE_AUTHTOKENS,
4924 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
4925 SELECTION_AUTHTOKENS_BY_ACCOUNT,
4926 new String[]{account.name, account.type},
4927 null, null, null);
4928 try {
4929 while (cursor.moveToNext()) {
4930 final String type = cursor.getString(0);
4931 final String authToken = cursor.getString(1);
4932 authTokensForAccount.put(type, authToken);
4933 }
4934 } finally {
4935 cursor.close();
4936 }
4937 return authTokensForAccount;
4938 }
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004939
4940 private Context getContextForUser(UserHandle user) {
4941 try {
4942 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4943 } catch (NameNotFoundException e) {
4944 // Default to mContext, not finding the package system is running as is unlikely.
4945 return mContext;
4946 }
4947 }
Sandra Kwan78812282015-11-04 11:19:47 -08004948
4949 private void sendResponse(IAccountManagerResponse response, Bundle result) {
4950 try {
4951 response.onResult(result);
4952 } catch (RemoteException e) {
4953 // if the caller is dead then there is no one to care about remote
4954 // exceptions
4955 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4956 Log.v(TAG, "failure while notifying response", e);
4957 }
4958 }
4959 }
4960
4961 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
4962 String errorMessage) {
4963 try {
4964 response.onError(errorCode, errorMessage);
4965 } catch (RemoteException e) {
4966 // if the caller is dead then there is no one to care about remote
4967 // exceptions
4968 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4969 Log.v(TAG, "failure while notifying response", e);
4970 }
4971 }
4972 }
Fred Quintana60307342009-03-24 22:48:12 -07004973}