blob: b152372d092d51c6db8c03495d7b630dda34eb0d [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;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070025import android.accounts.AccountManagerInternal;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070027import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.accounts.GrantCredentialsPermissionActivity;
29import android.accounts.IAccountAuthenticator;
30import android.accounts.IAccountAuthenticatorResponse;
31import android.accounts.IAccountManager;
32import android.accounts.IAccountManagerResponse;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070033import android.annotation.IntRange;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070034import android.annotation.NonNull;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080035import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070036import android.app.ActivityManagerNative;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070037import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070038import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070039import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070040import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070041import android.app.Notification;
42import android.app.NotificationManager;
43import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000044import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010045import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000046import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070047import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070048import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070049import android.content.ContentValues;
50import android.content.Context;
51import android.content.Intent;
52import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070053import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070054import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080055import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070056import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070057import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070059import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070060import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070061import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070062import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070063import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070064import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070065import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070066import android.database.Cursor;
67import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070068import android.database.sqlite.SQLiteDatabase;
69import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070070import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070071import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070072import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080073import android.os.Environment;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070074import android.os.FileUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070075import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070076import android.os.IBinder;
77import android.os.Looper;
78import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070079import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070080import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070081import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070082import android.os.RemoteException;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070083import android.os.ServiceManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070084import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070085import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070086import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070087import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070088import android.text.TextUtils;
89import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070090import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070091import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080092import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070093import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070094
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070095import com.android.internal.R;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -070096import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070097import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -080098import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080099import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -0700100import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000101import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700102import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600103import com.android.server.SystemService;
104
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700105import com.google.android.collect.Lists;
106import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700107
Oscar Montemayora8529f62009-11-18 10:14:20 -0800108import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700109import java.io.FileDescriptor;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700110import java.io.IOException;
Fred Quintanaa698f422009-04-08 19:14:54 -0700111import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800112import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700113import java.security.MessageDigest;
114import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700115import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700116import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800117import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700118import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700119import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700120import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700121import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800122import java.util.LinkedHashMap;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700123import java.util.LinkedList;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700124import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800125import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800126import java.util.Map.Entry;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700127import java.util.Set;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700128import java.util.concurrent.atomic.AtomicInteger;
129import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700130
Fred Quintana60307342009-03-24 22:48:12 -0700131/**
132 * A system service that provides account, password, and authtoken management for all
133 * accounts on the device. Some of these calls are implemented with the help of the corresponding
134 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
135 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700136 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700137 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700138 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700139public class AccountManagerService
140 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800141 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700142 private static final String TAG = "AccountManagerService";
143
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600144 public static class Lifecycle extends SystemService {
145 private AccountManagerService mService;
146
147 public Lifecycle(Context context) {
148 super(context);
149 }
150
151 @Override
152 public void onStart() {
153 mService = new AccountManagerService(getContext());
154 publishBinderService(Context.ACCOUNT_SERVICE, mService);
155 }
156
157 @Override
158 public void onBootPhase(int phase) {
159 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
160 mService.systemReady();
161 }
162 }
163
164 @Override
165 public void onUnlockUser(int userHandle) {
166 mService.onUnlockUser(userHandle);
167 }
168 }
169
Fred Quintana60307342009-03-24 22:48:12 -0700170 private static final String DATABASE_NAME = "accounts.db";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700171 private static final int PRE_N_DATABASE_VERSION = 9;
172 private static final int CE_DATABASE_VERSION = 10;
173 private static final int DE_DATABASE_VERSION = 1;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700174
175 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700176
177 private final Context mContext;
178
Fred Quintana56285a62010-12-02 14:20:51 -0800179 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700180 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700181 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800182
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700183 private final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700184
Fred Quintana60307342009-03-24 22:48:12 -0700185 // Messages that can be sent on mHandler
186 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700187 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700188
Fred Quintana56285a62010-12-02 14:20:51 -0800189 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700190
191 private static final String TABLE_ACCOUNTS = "accounts";
192 private static final String ACCOUNTS_ID = "_id";
193 private static final String ACCOUNTS_NAME = "name";
194 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700195 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700196 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700197 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800198 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
199 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700200
201 private static final String TABLE_AUTHTOKENS = "authtokens";
202 private static final String AUTHTOKENS_ID = "_id";
203 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
204 private static final String AUTHTOKENS_TYPE = "type";
205 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
206
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700207 private static final String TABLE_GRANTS = "grants";
208 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
209 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
210 private static final String GRANTS_GRANTEE_UID = "uid";
211
Fred Quintana60307342009-03-24 22:48:12 -0700212 private static final String TABLE_EXTRAS = "extras";
213 private static final String EXTRAS_ID = "_id";
214 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
215 private static final String EXTRAS_KEY = "key";
216 private static final String EXTRAS_VALUE = "value";
217
218 private static final String TABLE_META = "meta";
219 private static final String META_KEY = "key";
220 private static final String META_VALUE = "value";
221
Amith Yamasani67df64b2012-12-14 12:09:36 -0800222 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700223 private static final String SHARED_ACCOUNTS_ID = "_id";
224
225 private static final String PRE_N_DATABASE_NAME = "accounts.db";
226 private static final String CE_DATABASE_NAME = "accounts_ce.db";
227 private static final String DE_DATABASE_NAME = "accounts_de.db";
228 private static final String CE_DB_PREFIX = "ceDb.";
229 private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
230 private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
231 private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800232
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700233 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
234 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700235 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800236
Carlos Valdivia91979be2015-05-22 14:11:35 -0700237 static {
238 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
239 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
240 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700241
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700242 private static final String COUNT_OF_MATCHING_GRANTS = ""
243 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
244 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
245 + " AND " + GRANTS_GRANTEE_UID + "=?"
246 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
247 + " AND " + ACCOUNTS_NAME + "=?"
248 + " AND " + ACCOUNTS_TYPE + "=?";
249
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700250 private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = ""
251 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
252 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
253 + " AND " + GRANTS_GRANTEE_UID + "=?"
254 + " AND " + ACCOUNTS_NAME + "=?"
255 + " AND " + ACCOUNTS_TYPE + "=?";
256
Fred Quintana56285a62010-12-02 14:20:51 -0800257 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
258 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700259
Fred Quintana56285a62010-12-02 14:20:51 -0800260 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
261 AUTHTOKENS_AUTHTOKEN};
262
263 private static final String SELECTION_USERDATA_BY_ACCOUNT =
264 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
265 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
266
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800267 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
268 "auth_uid_for_type:";
269 private static final String META_KEY_DELIMITER = ":";
270 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
271
Fred Quintanaa698f422009-04-08 19:14:54 -0700272 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700273 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
274
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700275 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
276
Amith Yamasani04e0d262012-02-14 11:50:53 -0800277 static class UserAccounts {
278 private final int userId;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700279 private final DeDatabaseHelper openHelper;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800280 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
281 credentialsPermissionNotificationIds =
282 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
283 private final HashMap<Account, Integer> signinRequiredNotificationIds =
284 new HashMap<Account, Integer>();
285 private final Object cacheLock = new Object();
286 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700287 private final HashMap<String, Account[]> accountCache =
288 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800289 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700290 private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800291 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700292 private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700293
294 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700295 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700296
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700297 /** protected by the {@link #cacheLock} */
298 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
299 new HashMap<>();
300
301 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
302 number. Another maps Account number to Account.*/
303
304 /** protected by the {@link #cacheLock} */
305 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
306 new SparseArray<>();
307
308 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
309 /** protected by the {@link #cacheLock} */
310 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
311
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700312 /**
313 * protected by the {@link #cacheLock}
314 *
315 * Caches the previous names associated with an account. Previous names
316 * should be cached because we expect that when an Account is renamed,
317 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
318 * want to know if the accounts they care about have been renamed.
319 *
320 * The previous names are wrapped in an {@link AtomicReference} so that
321 * we can distinguish between those accounts with no previous names and
322 * those whose previous names haven't been cached (yet).
323 */
324 private final HashMap<Account, AtomicReference<String>> previousNameCache =
325 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800326
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700327 private int debugDbInsertionPoint = -1;
328 private SQLiteStatement statementForLogging;
329
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700330 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800331 this.userId = userId;
332 synchronized (cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700333 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800334 }
335 }
336 }
337
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700338 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600339 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800340
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700341 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700342 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700343
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700344 /**
345 * This should only be called by system code. One should only call this after the service
346 * has started.
347 * @return a reference to the AccountManagerService instance
348 * @hide
349 */
350 public static AccountManagerService getSingleton() {
351 return sThis.get();
352 }
Fred Quintana60307342009-03-24 22:48:12 -0700353
Fred Quintana56285a62010-12-02 14:20:51 -0800354 public AccountManagerService(Context context) {
355 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700356 }
357
Fred Quintana56285a62010-12-02 14:20:51 -0800358 public AccountManagerService(Context context, PackageManager packageManager,
359 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700360 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800361 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700362 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700363
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700364 ServiceThread serviceThread = new ServiceThread(TAG,
365 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
366 serviceThread.start();
367 mHandler = new MessageHandler(serviceThread.getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700368
Fred Quintana56285a62010-12-02 14:20:51 -0800369 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800370 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700371
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700372 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800373
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700374 addRequestsForPreInstalledApplications();
375
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800376 IntentFilter intentFilter = new IntentFilter();
377 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
378 intentFilter.addDataScheme("package");
379 mContext.registerReceiver(new BroadcastReceiver() {
380 @Override
381 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700382 // Don't delete accounts when updating a authenticator's
383 // package.
384 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700385 /* Purging data requires file io, don't block the main thread. This is probably
386 * less than ideal because we are introducing a race condition where old grants
387 * could be exercised until they are purged. But that race condition existed
388 * anyway with the broadcast receiver.
389 *
390 * Ideally, we would completely clear the cache, purge data from the database,
391 * and then rebuild the cache. All under the cache lock. But that change is too
392 * large at this point.
393 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700394 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700395 @Override
396 public void run() {
397 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700398
399 /* clears application request's for account types supported */
400 int uidOfUninstalledApplication =
401 intent.getIntExtra(Intent.EXTRA_UID, -1);
402 if(uidOfUninstalledApplication != -1) {
403 clearRequestedAccountVisibility(uidOfUninstalledApplication,
404 getUserAccounts(UserHandle.getUserId(
405 uidOfUninstalledApplication)));
406 }
407
408 /* removes visibility of previous UID of this uninstalled application*/
409 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
410 getUserAccounts(UserHandle.getUserId(
411 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700412 }
413 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700414 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700415 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700416
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800417 }
418 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800419
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700420 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
421 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
422 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
423 packageAddedOrChangedFilter.addDataScheme("package");
424 mContext.registerReceiverAsUser(new BroadcastReceiver() {
425 @Override
426 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700427 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700428 @Override
429 public void run() {
430 int uidOfInstalledApplication =
431 intent.getIntExtra(Intent.EXTRA_UID, -1);
432 if(uidOfInstalledApplication != -1) {
433 registerAccountTypesSupported(
434 uidOfInstalledApplication,
435 getUserAccounts(
436 UserHandle.getUserId(uidOfInstalledApplication)));
437 }
438 }
439 });
440 }
441 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
442
Amith Yamasani13593602012-03-22 16:16:17 -0700443 IntentFilter userFilter = new IntentFilter();
444 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800445 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700446 @Override
447 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800448 String action = intent.getAction();
449 if (Intent.ACTION_USER_REMOVED.equals(action)) {
450 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800451 }
Amith Yamasani13593602012-03-22 16:16:17 -0700452 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800453 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700454
455 LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl());
456
457 // Need to cancel account request notifications if the update/install can access the account
458 new PackageMonitor() {
459 @Override
460 public void onPackageAdded(String packageName, int uid) {
461 // Called on a handler, and running as the system
462 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
463 }
464
465 @Override
466 public void onPackageUpdateFinished(String packageName, int uid) {
467 // Called on a handler, and running as the system
468 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
469 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700470 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700471
472 // Cancel account request notification if an app op was preventing the account access
473 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
474 new AppOpsManager.OnOpChangedInternalListener() {
475 @Override
476 public void onOpChanged(int op, String packageName) {
477 try {
478 final int userId = ActivityManager.getCurrentUser();
479 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
480 final int mode = mAppOpsManager.checkOpNoThrow(
481 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
482 if (mode == AppOpsManager.MODE_ALLOWED) {
483 final long identity = Binder.clearCallingIdentity();
484 try {
485 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
486 } finally {
487 Binder.restoreCallingIdentity(identity);
488 }
489 }
490 } catch (NameNotFoundException e) {
491 /* ignore */
492 }
493 }
494 });
495
496 // Cancel account request notification if a permission was preventing the account access
497 mPackageManager.addOnPermissionsChangeListener(
498 (int uid) -> {
499 Account[] accounts = null;
500 String[] packageNames = mPackageManager.getPackagesForUid(uid);
501 if (packageNames != null) {
502 final int userId = UserHandle.getUserId(uid);
503 final long identity = Binder.clearCallingIdentity();
504 try {
505 for (String packageName : packageNames) {
506 if (mContext.getPackageManager().checkPermission(
507 Manifest.permission.GET_ACCOUNTS, packageName)
508 != PackageManager.PERMISSION_GRANTED) {
509 continue;
510 }
511
512 if (accounts == null) {
513 accounts = getAccountsAsUser(null, userId, "android");
514 if (ArrayUtils.isEmpty(accounts)) {
515 return;
516 }
517 }
518
519 for (Account account : accounts) {
520 cancelAccountAccessRequestNotificationIfNeeded(
521 account, uid, packageName, true);
522 }
523 }
524 } finally {
525 Binder.restoreCallingIdentity(identity);
526 }
527 }
528 });
529 }
530
531 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
532 boolean checkAccess) {
533 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
534 for (Account account : accounts) {
535 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
536 }
537 }
538
539 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
540 boolean checkAccess) {
541 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
542 for (Account account : accounts) {
543 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
544 }
545 }
546
547 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
548 boolean checkAccess) {
549 String[] packageNames = mPackageManager.getPackagesForUid(uid);
550 if (packageNames != null) {
551 for (String packageName : packageNames) {
552 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
553 packageName, checkAccess);
554 }
555 }
556 }
557
558 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
559 int uid, String packageName, boolean checkAccess) {
560 if (!checkAccess || hasAccountAccess(account, packageName,
561 UserHandle.getUserHandleForUid(uid))) {
562 cancelNotification(getCredentialPermissionNotificationId(account,
563 AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
564 UserHandle.getUserHandleForUid(uid));
565 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800566 }
567
Dianne Hackborn164371f2013-10-01 19:10:13 -0700568 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700569 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
570 int[] selectedUids) {
571 if(addAccountExplicitly(account,password,extras)) {
572 for(int thisUid : selectedUids) {
573 makeAccountVisible(account, thisUid);
574 }
575 return true;
576 }
577 return false;
578 }
579
580 @Override
581 public int[] getRequestingUidsForType(String accountType) {
582 int callingUid = Binder.getCallingUid();
583 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
584 String msg = String.format(
585 "uid %s cannot get secrets for accounts of type: %s",
586 callingUid,
587 accountType);
588 throw new SecurityException(msg);
589 }
590 return getRequestingUidsForType(accountType, getUserAccounts(
591 UserHandle.getUserId(callingUid)));
592 }
593
594 /**
595 * Returns all UIDs for applications that requested the account type. This method
596 * is called indirectly by the Authenticator and AccountManager
597 *
598 * @param accountType authenticator would like to know the requesting apps of
599 * @param ua UserAccount that currently hosts the account and application
600 *
601 * @return ArrayList of all UIDs that support accounts of this
602 * account type that seek approval (to be used to know which accounts for
603 * the authenticator to include in addAccountExplicitly). Null if none.
604 */
605 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
606 synchronized(ua.cacheLock) {
607 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
608 ua.mApplicationAccountRequestMappings;
609 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
610 accountType);
611 if(allUidsForAccountType == null) {
612 return null;
613 }
614 int[] toReturn = new int[allUidsForAccountType.size()];
615 for(int i = 0 ; i < toReturn.length ; i++) {
616 toReturn[i] = allUidsForAccountType.get(i);
617 }
618 return toReturn;
619 }
620 }
621
622 @Override
623 public boolean isAccountVisible(Account a, int uid) {
624 int callingUid = Binder.getCallingUid();
625 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
626 String msg = String.format(
627 "uid %s cannot get secrets for accounts of type: %s",
628 callingUid,
629 a.type);
630 throw new SecurityException(msg);
631 }
632 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
633 }
634
635 /**
636 * Checks visibility of certain account of a process identified
637 * by a given UID. This is called by the Authenticator indirectly.
638 *
639 * @param a The account to check visibility of
640 * @param uid UID to check visibility of
641 * @param ua UserAccount that currently hosts the account and application
642 *
643 * @return True if application has access to the account
644 *
645 */
646 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700647 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
648 return true;
649 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700650 int accountMapping = getMockAccountNumber(a, ua);
651 if(accountMapping < 0) {
652 return true;
653 }
654 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700655 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700656 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
657 ua.mVisibleListUidToMockAccountNumbers;
658 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700659 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
660 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
661 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700662 }
663 }
664
665 @Override
666 public boolean makeAccountVisible(Account a, int uid) {
667 int callingUid = Binder.getCallingUid();
668 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
669 String msg = String.format(
670 "uid %s cannot get secrets for accounts of type: %s",
671 callingUid,
672 a.type);
673 throw new SecurityException(msg);
674 }
675 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
676 }
677
678 /**
679 * Gives a certain UID, represented a application, access to an account. This method
680 * is called indirectly by the Authenticator.
681 *
682 * @param a Account to make visible
683 * @param uid to add visibility of the Account from
684 * @param ua UserAccount that currently hosts the account and application
685 *
686 * @return True if account made visible to application and was not previously visible.
687 */
688 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
689 int accountMapping = getMockAccountNumber(a, ua);
690 if(accountMapping < 0) {
691 accountMapping = makeAccountNumber(a, ua);
692 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700693 synchronized(ua.cacheLock) {
694 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
695 ua.mVisibleListUidToMockAccountNumbers;
696 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
697 if(linkedAccountsToUid == null) {
698 linkedAccountsToUid = new ArrayList<>();
699 linkedAccountsToUid.add(accountMapping);
700 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
701 } else if(!linkedAccountsToUid.contains(accountMapping)) {
702 linkedAccountsToUid.add(accountMapping);
703 } else {
704 return false;
705 }
706 }
707
708 String[] subPackages = mPackageManager.getPackagesForUid(uid);
709 if(subPackages != null) {
710 for(String subPackage : subPackages) {
711 sendNotification(subPackage, a);
712 }
713 }
714 return true;
715 }
716
717 @Override
718 public boolean removeAccountVisibility(Account a, int uid) {
719 int callingUid = Binder.getCallingUid();
720 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
721 String msg = String.format(
722 "uid %s cannot get secrets for accounts of type: %s",
723 callingUid,
724 a.type);
725 throw new SecurityException(msg);
726 }
727 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
728 }
729
730 /**
731 * Removes visibility of certain account of a process identified
732 * by a given UID to an application. This is called directly by the
733 * AccountManager and indirectly by the Authenticator.
734 *
735 * @param a Account to remove visibility from
736 * @param uid UID to remove visibility of the Account from
737 * @param ua UserAccount that hosts the account and application
738 *
739 * @return True if application access to account removed and was previously visible.
740 */
741 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
742 int accountMapping = getMockAccountNumber(a, ua);
743 if(accountMapping < 0) {
744 return false;
745 }
746 synchronized(ua.cacheLock) {
747 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
748 ua.mVisibleListUidToMockAccountNumbers;
749 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
750 if(linkedAccountsToUid != null) {
751 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
752 if(linkedAccountsToUid.size() == 0) {
753 userWlUidToMockAccountNums.remove(uid);
754 }
755 return toReturn;
756 }
757 }
758 return false;
759 }
760
761 /**
762 * Registers an application's preferences for supported account types for login. This is
763 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
764 *
765 * @param accountTypes account types third party app is willing to support
766 * @param uid of application requesting account visibility
767 * @param ua UserAccount that hosts the account and application
768 */
769 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
770 synchronized(ua.cacheLock) {
771 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
772 ua.mApplicationAccountRequestMappings;
773 for(String accountType : accountTypes) {
774 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
775 .get(accountType);
776 if(accountUidAppList == null) {
777 accountUidAppList = new ArrayList<>();
778 accountUidAppList.add(uid);
779 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
780 } else if (!accountUidAppList.contains(uid)) {
781 accountUidAppList.add(uid);
782 }
783 }
784 }
785 }
786
787 /**
788 * Registers the requested login account types requested by all the applications already
789 * installed on the device.
790 */
791 private void addRequestsForPreInstalledApplications() {
792 List<PackageInfo> allInstalledPackages = mContext.getPackageManager().
793 getInstalledPackages(0);
794 for(PackageInfo pi : allInstalledPackages) {
795 int currentUid = pi.applicationInfo.uid;
796 if(currentUid != -1) {
797 registerAccountTypesSupported(currentUid,
798 getUserAccounts(UserHandle.getUserId(currentUid)));
799 }
800 }
801 }
802
803 /**
804 * Clears all preferences an application had for login account types it offered
805 * support for. This method is used by AccountManager after application is
806 * uninstalled.
807 *
808 * @param uid Uid of the application to clear account type preferences
809 * @param ua UserAccount that hosted the account and application
810 *
811 * @return true if any previous settings were overridden.
812 */
813 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
814 boolean accountsDeleted = false;
815 ArrayList<String> accountTypesToRemove = new ArrayList<>();
816 synchronized(ua.cacheLock) {
817 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
818 ua.mApplicationAccountRequestMappings;
819 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
820 userApplicationAccountRequestMappings.entrySet();
821
822 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
823 ArrayList<Integer> supportedApps = entry.getValue();
824 if(supportedApps.remove((Integer) uid)) {
825 accountsDeleted = true;
826 }
827
828 if(supportedApps.isEmpty()) {
829 accountTypesToRemove.add(entry.getKey());
830 }
831 }
832
833 for(String s : accountTypesToRemove) {
834 userApplicationAccountRequestMappings.remove(s);
835 }
836 }
837
838 return accountsDeleted;
839 }
840
841 /**
842 * Retrieves the mock account number associated with an Account in order to later retrieve
843 * the account from the Integer-Account Mapping. An account number is not the same as
844 * accountId in the database. This method can be indirectly called by AccountManager and
845 * indirectly by the Authenticator.
846 *
847 * @param a account to retrieve account number mapping
848 * @param ua UserAccount that currently hosts the account and application
849 *
850 * @return account number affiliated with the Account in question. Negative number if none.
851 */
852 private int getMockAccountNumber(Account a, UserAccounts ua) {
853 //TODO: Each account is linked to AccountId rather than generated mock account numbers
854 SparseArray<Account> userAcctIdToAcctMap =
855 ua.mMockAccountIdToAccount;
856 synchronized(ua.cacheLock) {
857 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
858 if(indexOfAccount < 0) {
859 return -1;
860 }
861 return userAcctIdToAcctMap.keyAt(indexOfAccount);
862 }
863 }
864
865 /**
866 * Returns a full list of accounts that a certain UID is allowed access
867 * based on the visible list entries.
868 *
869 * @param uid of application to retrieve visible listed accounts for
870 * @param ua UserAccount that currently hosts the account and application
871 *
872 * @return array of Account values that are accessible by the given uids
873 */
874 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
875 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
876 synchronized(ua.cacheLock) {
877 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
878 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
879 ua.mVisibleListUidToMockAccountNumbers;
880 ArrayList<Integer> visibleListedUidAccountNumbers =
881 userWlUidToMockAccountNums.get(uid);
882 if(visibleListedUidAccountNumbers != null) {
883 for(Integer accountNumber : visibleListedUidAccountNumbers) {
884 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
885 visibleListedAccounts.add(currentAccount);
886 }
887 }
888 }
889 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
890 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
891 }
892
893 /**
894 * Makes an account number for a given Account to be mapped to.
895 * This method is called by makeVisible if an Account does not have
896 * a mapping for the visible list. This method is thus indirectly
897 * called by the Authenticator.
898 *
899 * @param a account to make an account number mapping of
900 * @param ua UserAccount that currently hosts the account and application
901 *
902 * @return account number created to map to the given account
903 */
904 // TODO: Remove this method and use accountId from DB.
905 private int makeAccountNumber(Account a, UserAccounts ua) {
906 synchronized(ua.cacheLock) {
907 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
908 int newAccountMapping = 0;
909 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
910 newAccountMapping++;
911 }
912 userAcctIdToAcctMap.put(newAccountMapping, a);
913 return newAccountMapping;
914 }
915 }
916
917
918
919 /**
920 * Registers an application, represented by a UID, to support account types detailed in
921 * the applications manifest as well as allowing it to opt for notifications.
922 *
923 * @param uid UID of application
924 * @param ua UserAccount that currently hosts the account and application
925 */
926 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
927 /* Account types supported are drawn from the Android Manifest of the Application */
928 String interestedPackages = null;
929 try {
930 String[] allPackages = mPackageManager.getPackagesForUid(uid);
Nicolas Prevotf7d8df12016-09-16 17:45:34 +0100931 if (allPackages != null) {
932 for(String aPackage : allPackages) {
933 ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
934 PackageManager.GET_META_DATA);
935 Bundle b = ai.metaData;
936 if(b == null) {
937 return;
938 }
939 interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700940 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700941 }
942 } catch (PackageManager.NameNotFoundException e) {
943 Log.d("NameNotFoundException", e.getMessage());
944 }
945 if(interestedPackages != null) {
946 /* request remote account types directly from here. Reads from Android Manifest */
947 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
948 }
949 }
950
951 /**
952 * Allows AccountManager to register account types that an application has login
953 * support for. This method over-writes all of the application's previous settings
954 * for accounts it supported.
955 *
956 * @param accountTypes array of account types application wishes to support
957 * @param uid of application registering requested account types
958 * @param ua UserAccount that hosts the account and application
959 */
960 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
961 if(accountTypes.length > 0) {
962 clearRequestedAccountVisibility(uid, ua);
963 addRequestedAccountsVisibility(accountTypes, uid, ua);
964 }
965 }
966
967 /**
968 * Removes visibility of all Accounts to this particular UID. This is called when an
969 * application is uninstalled so another application that is installed with the same
970 * UID cannot access Accounts. This is called by AccountManager.
971 *
972 * @param uid of application to remove all Account visibility to
973 * @param ua UserAccount that hosts the current Account
974 */
975 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
976 synchronized(ua.cacheLock) {
977 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
978 ua.mVisibleListUidToMockAccountNumbers;
979 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
980 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
981 if(allAccountNumbersList != null) {
982 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
983 new Integer[allAccountNumbersList.size()]);
984 for(int accountNum : allAccountNumbers) {
985 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
986 }
987 }
988 }
989 }
990
991 /**
992 * Removes visible list functionality of a certain Account.
993 * This method is currently called by (1) addAccountExplicitly (as opposed to
994 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
995 *
996 * @param a the account to clear the visible list functionality for
997 * @param ua currently UserAccounts profile containing Account
998 *
999 * @return true if account previously had visible list functionality
1000 */
1001 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
1002 int mockAccountNum = getMockAccountNumber(a, ua);
1003 if(mockAccountNum < 0) {
1004 return false;
1005 }
1006 synchronized(ua.cacheLock) {
1007 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
1008 ua.mVisibleListUidToMockAccountNumbers;
1009 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
1010
1011 /* Removing mapping from account number to account removes visible list functionality*/
1012 userAcctIdToAcctMap.remove(mockAccountNum);
1013
1014 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
1015 int uidKey = userWlUidToMockAccountNums.keyAt(i);
1016 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
1017 if(allAccountNumbers != null) {
1018 allAccountNumbers.remove(mockAccountNum);
1019 if(allAccountNumbers.isEmpty()) {
1020 userWlUidToMockAccountNums.remove(uidKey);
1021 }
1022 }
1023 }
1024 }
1025 return true;
1026 }
1027
1028 /**
1029 * Sends a direct intent to a package, notifying it of a visible account. This
1030 * method is a helper method of makeAccountVisible.
1031 *
1032 * @param desiredPackage to send Account to
1033 * @param visibleAccount to send to package
1034 */
1035 private void sendNotification(String desiredPackage, Account visibleAccount) {
1036 Intent intent = new Intent();
1037 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
1038 intent.setAction(NEW_ACCOUNT_VISIBLE);
1039 intent.setPackage(desiredPackage);
1040 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
1041 mContext.sendBroadcast(intent);
1042 }
1043
1044 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -07001045 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1046 throws RemoteException {
1047 try {
1048 return super.onTransact(code, data, reply, flags);
1049 } catch (RuntimeException e) {
1050 // The account manager only throws security exceptions, so let's
1051 // log all others.
1052 if (!(e instanceof SecurityException)) {
1053 Slog.wtf(TAG, "Account Manager Crash", e);
1054 }
1055 throw e;
1056 }
1057 }
1058
Kenny Root26ff6622012-07-30 12:58:03 -07001059 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -07001060 }
1061
Amith Yamasani258848d2012-08-10 17:06:33 -07001062 private UserManager getUserManager() {
1063 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001064 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -07001065 }
1066 return mUserManager;
1067 }
1068
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001069 /**
1070 * Validate internal set of accounts against installed authenticators for
1071 * given user. Clears cached authenticators before validating.
1072 */
1073 public void validateAccounts(int userId) {
1074 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001075 // Invalidate user-specific cache to make sure we catch any
1076 // removed authenticators.
1077 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
1078 }
1079
1080 /**
1081 * Validate internal set of accounts against installed authenticators for
1082 * given user. Clear cached authenticators before validating when requested.
1083 */
1084 private void validateAccountsInternal(
1085 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001086 if (Log.isLoggable(TAG, Log.DEBUG)) {
1087 Log.d(TAG, "validateAccountsInternal " + accounts.userId
1088 + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001089 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001090 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001091
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001092 if (invalidateAuthenticatorCache) {
1093 mAuthenticatorCache.invalidateCache(accounts.userId);
1094 }
1095
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001096 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1097 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001098 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001099
Amith Yamasani04e0d262012-02-14 11:50:53 -08001100 synchronized (accounts.cacheLock) {
1101 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001102 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001103
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001104 // Get a map of stored authenticator types to UID
1105 Map<String, Integer> metaAuthUid = AccountsDbUtils.findMetaAuthUid(db);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001106 // Create a list of authenticator type whose previous uid no longer exists
1107 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001108 SparseBooleanArray knownUids = null;
1109 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1110 String type = authToUidEntry.getKey();
1111 int uid = authToUidEntry.getValue();
1112 Integer knownUid = knownAuth.get(type);
1113 if (knownUid != null && uid == knownUid) {
1114 // Remove it from the knownAuth list if it's unchanged.
1115 knownAuth.remove(type);
1116 } else {
1117 /*
1118 * The authenticator is presently not cached and should only be triggered
1119 * when we think an authenticator has been removed (or is being updated).
1120 * But we still want to check if any data with the associated uid is
1121 * around. This is an (imperfect) signal that the package may be updating.
1122 *
1123 * A side effect of this is that an authenticator sharing a uid with
1124 * multiple apps won't get its credentials wiped as long as some app with
1125 * that uid is still on the device. But I suspect that this is a rare case.
1126 * And it isn't clear to me how an attacker could really exploit that
1127 * feature.
1128 *
1129 * The upshot is that we don't have to worry about accounts getting
1130 * uninstalled while the authenticator's package is being updated.
1131 *
1132 */
1133 if (knownUids == null) {
1134 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001135 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001136 if (!knownUids.get(uid)) {
1137 // The authenticator is not presently available to the cache. And the
1138 // package no longer has a data directory (so we surmise it isn't updating).
1139 // So purge its data from the account databases.
1140 obsoleteAuthType.add(type);
1141 // And delete it from the TABLE_META
1142 AccountsDbUtils.deleteMetaByAuthTypeAndUid(db, type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001143 }
1144 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001145 }
1146
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001147 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001148 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001149 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001150 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
1151 AccountsDbUtils.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
1152 entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001153 }
1154
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001155 final Map<Long, Account> accountsMap = AccountsDbUtils.findAllAccounts(db);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001156 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001157 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001158 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001159 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1160 final long accountId = accountEntry.getKey();
1161 final Account account = accountEntry.getValue();
1162 if (obsoleteAuthType.contains(account.type)) {
1163 Slog.w(TAG, "deleting account " + account.name + " because type "
1164 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001165 db.beginTransaction();
1166 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001167 AccountsDbUtils.deleteAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001168 // Also delete from CE table if user is unlocked; if user is currently
1169 // locked the account will be removed later by syncDeCeAccountsLocked
1170 if (userUnlocked) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001171 AccountsDbUtils.deleteCeAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001172 }
1173 db.setTransactionSuccessful();
1174 } finally {
1175 db.endTransaction();
1176 }
Fred Quintana56285a62010-12-02 14:20:51 -08001177 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001178
1179 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
1180 accountId, accounts);
1181
Amith Yamasani04e0d262012-02-14 11:50:53 -08001182 accounts.userDataCache.remove(account);
1183 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001184 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001185 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001186 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001187 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001188 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001189 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001190 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001191 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001192 }
1193 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001194 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001195 final String accountType = cur.getKey();
1196 final ArrayList<String> accountNames = cur.getValue();
1197 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001198 for (int i = 0; i < accountsForType.length; i++) {
1199 accountsForType[i] = new Account(accountNames.get(i), accountType);
Fred Quintana56285a62010-12-02 14:20:51 -08001200 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001201 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001202 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001203 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001204 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001205 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001206 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001207 }
1208 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001209 }
1210
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001211 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1212 // Get the UIDs of all apps that might have data on the device. We want
1213 // to preserve user data if the app might otherwise be storing data.
1214 List<PackageInfo> pkgsWithData =
1215 mPackageManager.getInstalledPackagesAsUser(
1216 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1217 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1218 for (PackageInfo pkgInfo : pkgsWithData) {
1219 if (pkgInfo.applicationInfo != null
1220 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1221 knownUids.put(pkgInfo.applicationInfo.uid, true);
1222 }
1223 }
1224 return knownUids;
1225 }
1226
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001227 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1228 Context context,
1229 int userId) {
1230 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001231 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1232 }
1233
1234 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1235 IAccountAuthenticatorCache authCache,
1236 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001237 HashMap<String, Integer> knownAuth = new HashMap<>();
1238 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1239 .getAllServices(userId)) {
1240 knownAuth.put(service.type.type, service.uid);
1241 }
1242 return knownAuth;
1243 }
1244
Amith Yamasani04e0d262012-02-14 11:50:53 -08001245 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001246 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001247 }
1248
1249 protected UserAccounts getUserAccounts(int userId) {
1250 synchronized (mUsers) {
1251 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001252 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001253 if (accounts == null) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001254 File preNDbFile = new File(getPreNDatabaseName(userId));
1255 File deDbFile = new File(getDeDatabaseName(userId));
1256 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001257 initializeDebugDbSizeAndCompileSqlStatementForLogging(
1258 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001259 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001260 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001261 validateAccounts = true;
1262 }
1263 // open CE database if necessary
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001264 if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001265 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1266 synchronized (accounts.cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001267 File preNDatabaseFile = new File(getPreNDatabaseName(userId));
1268 File ceDatabaseFile = new File(getCeDatabaseName(userId));
1269 CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
1270 accounts.openHelper.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001271 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001272 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001273 }
1274 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001275 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001276 }
1277 return accounts;
1278 }
1279 }
1280
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001281 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1282 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
1283 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001284 List<Account> accountsToRemove = AccountsDbUtils.findCeAccountsNotInDe(db);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001285 if (!accountsToRemove.isEmpty()) {
1286 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1287 + accounts.userId + " was locked. Removing accounts from CE tables");
1288 logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
1289
1290 for (Account account : accountsToRemove) {
1291 removeAccountInternal(accounts, account, Process.myUid());
1292 }
1293 }
1294 }
1295
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001296 private void purgeOldGrantsAll() {
1297 synchronized (mUsers) {
1298 for (int i = 0; i < mUsers.size(); i++) {
1299 purgeOldGrants(mUsers.valueAt(i));
1300 }
1301 }
1302 }
1303
1304 private void purgeOldGrants(UserAccounts accounts) {
1305 synchronized (accounts.cacheLock) {
1306 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001307 List<Integer> uids = AccountsDbUtils.findAllUidGrants(db);
1308 for (int uid : uids) {
1309 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1310 if (packageExists) {
1311 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001312 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001313 Log.d(TAG, "deleting grants for UID " + uid
1314 + " because its package is no longer installed");
1315 AccountsDbUtils.deleteGrantsByUid(db, uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001316 }
1317 }
1318 }
1319
Amith Yamasani13593602012-03-22 16:16:17 -07001320 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001321 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001322 if (userId < 1) return;
1323
1324 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001325 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001326 synchronized (mUsers) {
1327 accounts = mUsers.get(userId);
1328 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001329 userUnlocked = mLocalUnlockedUsers.get(userId);
1330 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001331 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001332 if (accounts != null) {
1333 synchronized (accounts.cacheLock) {
1334 accounts.openHelper.close();
1335 }
Amith Yamasani13593602012-03-22 16:16:17 -07001336 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001337 Log.i(TAG, "Removing database files for user " + userId);
1338 File dbFile = new File(getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001339
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001340 deleteDbFileWarnIfFailed(dbFile);
1341 // Remove CE file if user is unlocked, or FBE is not enabled
1342 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1343 if (!fbeEnabled || userUnlocked) {
1344 File ceDb = new File(getCeDatabaseName(userId));
1345 if (ceDb.exists()) {
1346 deleteDbFileWarnIfFailed(ceDb);
1347 }
1348 }
1349 }
1350
1351 private static void deleteDbFileWarnIfFailed(File dbFile) {
1352 if (!SQLiteDatabase.deleteDatabase(dbFile)) {
1353 Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
Amith Yamasani13593602012-03-22 16:16:17 -07001354 }
1355 }
1356
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001357 @VisibleForTesting
1358 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001359 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1360 }
1361
1362 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001363 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1364 Log.v(TAG, "onUserUnlocked " + userId);
1365 }
1366 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001367 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001368 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001369 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001370 syncSharedAccounts(userId);
1371 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001372
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001373 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001374 // Check if there's a shared account that needs to be created as an account
1375 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1376 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001377 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001378 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001379 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001380 : UserHandle.USER_SYSTEM;
1381 if (parentUserId < 0) {
1382 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1383 return;
1384 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001385 for (Account sa : sharedAccounts) {
1386 if (ArrayUtils.contains(accounts, sa)) continue;
1387 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001388 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001389 }
1390 }
1391
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001392 @Override
1393 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001394 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001395 }
1396
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001397 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001398 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001399 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001400 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1401 Log.v(TAG, "getPassword: " + account
1402 + ", caller's uid " + Binder.getCallingUid()
1403 + ", pid " + Binder.getCallingPid());
1404 }
Fred Quintana382601f2010-03-25 12:25:10 -07001405 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001406 int userId = UserHandle.getCallingUserId();
1407 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001408 String msg = String.format(
1409 "uid %s cannot get secrets for accounts of type: %s",
1410 callingUid,
1411 account.type);
1412 throw new SecurityException(msg);
1413 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001414 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001415 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001416 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001417 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001418 } finally {
1419 restoreCallingIdentity(identityToken);
1420 }
1421 }
1422
Amith Yamasani04e0d262012-02-14 11:50:53 -08001423 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001424 if (account == null) {
1425 return null;
1426 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001427 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001428 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1429 return null;
1430 }
Fred Quintana31957f12009-10-21 13:43:10 -07001431
Amith Yamasani04e0d262012-02-14 11:50:53 -08001432 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001433 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001434 return AccountsDbUtils.findAccountPasswordByNameAndType(db, account.name,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001435 account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001436 }
1437 }
1438
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001439 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001440 public String getPreviousName(Account account) {
1441 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1442 Log.v(TAG, "getPreviousName: " + account
1443 + ", caller's uid " + Binder.getCallingUid()
1444 + ", pid " + Binder.getCallingPid());
1445 }
1446 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001447 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001448 long identityToken = clearCallingIdentity();
1449 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001450 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001451 return readPreviousNameInternal(accounts, account);
1452 } finally {
1453 restoreCallingIdentity(identityToken);
1454 }
1455 }
1456
1457 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1458 if (account == null) {
1459 return null;
1460 }
1461 synchronized (accounts.cacheLock) {
1462 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1463 if (previousNameRef == null) {
1464 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001465 String previousName = AccountsDbUtils.findAccountPreviousName(db, account);
1466 previousNameRef = new AtomicReference<>(previousName);
1467 accounts.previousNameCache.put(account, previousNameRef);
1468 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001469 } else {
1470 return previousNameRef.get();
1471 }
1472 }
1473 }
1474
1475 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001476 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001477 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001478 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001479 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1480 account, key, callingUid, Binder.getCallingPid());
1481 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001482 }
Fred Quintana382601f2010-03-25 12:25:10 -07001483 if (account == null) throw new IllegalArgumentException("account is null");
1484 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001485 int userId = UserHandle.getCallingUserId();
1486 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001487 String msg = String.format(
1488 "uid %s cannot get user data for accounts of type: %s",
1489 callingUid,
1490 account.type);
1491 throw new SecurityException(msg);
1492 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001493 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001494 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1495 return null;
1496 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001497 long identityToken = clearCallingIdentity();
1498 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001499 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001500 synchronized (accounts.cacheLock) {
1501 if (!accountExistsCacheLocked(accounts, account)) {
1502 return null;
1503 }
1504 return readUserDataInternalLocked(accounts, account, key);
1505 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001506 } finally {
1507 restoreCallingIdentity(identityToken);
1508 }
1509 }
1510
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001511 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001512 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001513 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001514 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1515 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001516 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001517 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001518 + ", pid " + Binder.getCallingPid());
1519 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001520 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001521 if (isCrossUser(callingUid, userId)) {
1522 throw new SecurityException(
1523 String.format(
1524 "User %s tying to get authenticator types for %s" ,
1525 UserHandle.getCallingUserId(),
1526 userId));
1527 }
1528
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001529 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001530 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001531 return getAuthenticatorTypesInternal(userId);
1532
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001533 } finally {
1534 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001535 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001536 }
1537
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001538 /**
1539 * Should only be called inside of a clearCallingIdentity block.
1540 */
1541 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
Fyodor Kupolov81446482016-08-24 11:27:49 -07001542 mAuthenticatorCache.updateServices(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001543 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1544 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1545 AuthenticatorDescription[] types =
1546 new AuthenticatorDescription[authenticatorCollection.size()];
1547 int i = 0;
1548 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1549 : authenticatorCollection) {
1550 types[i] = authenticator.type;
1551 i++;
1552 }
1553 return types;
1554 }
1555
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001556 private boolean isCrossUser(int callingUid, int userId) {
1557 return (userId != UserHandle.getCallingUserId()
1558 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001559 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001560 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1561 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001562 }
1563
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001564 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001565 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001566 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001567 // clears the visible list functionality for this account because this method allows
1568 // default account access to all applications for account.
1569
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001570 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001571 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001572 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001573 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001574 + ", pid " + Binder.getCallingPid());
1575 }
Fred Quintana382601f2010-03-25 12:25:10 -07001576 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001577 int userId = UserHandle.getCallingUserId();
1578 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001579 String msg = String.format(
1580 "uid %s cannot explicitly add accounts of type: %s",
1581 callingUid,
1582 account.type);
1583 throw new SecurityException(msg);
1584 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001585 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001586 /*
1587 * Child users are not allowed to add accounts. Only the accounts that are
1588 * shared by the parent profile can be added to child profile.
1589 *
1590 * TODO: Only allow accounts that were shared to be added by
1591 * a limited user.
1592 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001593
Fred Quintana60307342009-03-24 22:48:12 -07001594 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001595 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001596 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001597 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001598 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001599 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001600 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001601 }
1602 }
1603
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001604 @Override
1605 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001606 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001607 int callingUid = Binder.getCallingUid();
1608 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1609 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001610 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001611 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001612 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1613 final UserAccounts toAccounts = getUserAccounts(userTo);
1614 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001615 if (response != null) {
1616 Bundle result = new Bundle();
1617 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1618 try {
1619 response.onResult(result);
1620 } catch (RemoteException e) {
1621 Slog.w(TAG, "Failed to report error back to the client." + e);
1622 }
1623 }
1624 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001625 }
1626
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001627 Slog.d(TAG, "Copying account " + account.name
1628 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001629 long identityToken = clearCallingIdentity();
1630 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001631 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001632 false /* stripAuthTokenFromResult */, account.name,
1633 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001634 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001635 protected String toDebugString(long now) {
1636 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1637 + ", " + account.type;
1638 }
1639
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001640 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001641 public void run() throws RemoteException {
1642 mAuthenticator.getAccountCredentialsForCloning(this, account);
1643 }
1644
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001645 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001646 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001647 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001648 if (result != null
1649 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1650 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001651 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001652 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001653 super.onResult(result);
1654 }
1655 }
1656 }.bind();
1657 } finally {
1658 restoreCallingIdentity(identityToken);
1659 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001660 }
1661
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001662 @Override
1663 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001664 final int callingUid = Binder.getCallingUid();
1665 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1666 String msg = String.format(
1667 "accountAuthenticated( account: %s, callerUid: %s)",
1668 account,
1669 callingUid);
1670 Log.v(TAG, msg);
1671 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001672 if (account == null) {
1673 throw new IllegalArgumentException("account is null");
1674 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001675 int userId = UserHandle.getCallingUserId();
1676 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001677 String msg = String.format(
1678 "uid %s cannot notify authentication for accounts of type: %s",
1679 callingUid,
1680 account.type);
1681 throw new SecurityException(msg);
1682 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001683
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001684 if (!canUserModifyAccounts(userId, callingUid) ||
1685 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001686 return false;
1687 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001688
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001689 long identityToken = clearCallingIdentity();
1690 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001691 UserAccounts accounts = getUserAccounts(userId);
1692 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001693 } finally {
1694 restoreCallingIdentity(identityToken);
1695 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001696 }
1697
1698 private boolean updateLastAuthenticatedTime(Account account) {
1699 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001700 synchronized (accounts.cacheLock) {
1701 final ContentValues values = new ContentValues();
1702 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1703 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1704 int i = db.update(
1705 TABLE_ACCOUNTS,
1706 values,
1707 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1708 new String[] {
1709 account.name, account.type
1710 });
1711 if (i > 0) {
1712 return true;
1713 }
1714 }
1715 return false;
1716 }
1717
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001718 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001719 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1720 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001721 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001722 long id = clearCallingIdentity();
1723 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001724 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001725 false /* stripAuthTokenFromResult */, account.name,
1726 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001727 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001728 protected String toDebugString(long now) {
1729 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1730 + ", " + account.type;
1731 }
1732
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001733 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001734 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001735 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001736 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001737 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001738 for (Account acc : getAccounts(parentUserId,
1739 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001740 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001741 mAuthenticator.addAccountFromCredentials(
1742 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001743 break;
1744 }
1745 }
1746 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001747 }
1748
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001749 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001750 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001751 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001752 // TODO: Anything to do if if succedded?
1753 // TODO: If it failed: Show error notification? Should we remove the shadow
1754 // account to avoid retries?
1755 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001756 }
1757
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001758 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001759 public void onError(int errorCode, String errorMessage) {
1760 super.onError(errorCode, errorMessage);
1761 // TODO: Show error notification to user
1762 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1763 }
1764
1765 }.bind();
1766 } finally {
1767 restoreCallingIdentity(id);
1768 }
1769 }
1770
Amith Yamasani04e0d262012-02-14 11:50:53 -08001771 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001772 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001773 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001774 if (account == null) {
1775 return false;
1776 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001777 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001778 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1779 + " is locked. callingUid=" + callingUid);
1780 return false;
1781 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001782 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001783 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001784 db.beginTransaction();
1785 try {
1786 long numMatches = DatabaseUtils.longForQuery(db,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001787 "select count(*) from " + CE_TABLE_ACCOUNTS
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001788 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1789 new String[]{account.name, account.type});
1790 if (numMatches > 0) {
1791 Log.w(TAG, "insertAccountIntoDatabase: " + account
1792 + ", skipping since the account already exists");
1793 return false;
1794 }
1795 ContentValues values = new ContentValues();
1796 values.put(ACCOUNTS_NAME, account.name);
1797 values.put(ACCOUNTS_TYPE, account.type);
1798 values.put(ACCOUNTS_PASSWORD, password);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001799 long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001800 if (accountId < 0) {
1801 Log.w(TAG, "insertAccountIntoDatabase: " + account
1802 + ", skipping the DB insert failed");
1803 return false;
1804 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001805 // Insert into DE table
1806 values = new ContentValues();
1807 values.put(ACCOUNTS_ID, accountId);
1808 values.put(ACCOUNTS_NAME, account.name);
1809 values.put(ACCOUNTS_TYPE, account.type);
1810 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
1811 System.currentTimeMillis());
1812 if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
1813 Log.w(TAG, "insertAccountIntoDatabase: " + account
1814 + ", skipping the DB insert failed");
1815 return false;
1816 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001817 if (extras != null) {
1818 for (String key : extras.keySet()) {
1819 final String value = extras.getString(key);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001820 if (AccountsDbUtils.insertExtra(db, accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001821 Log.w(TAG, "insertAccountIntoDatabase: " + account
1822 + ", skipping since insertExtra failed for key " + key);
1823 return false;
1824 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001825 }
1826 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001827 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001828
1829 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1830 accounts, callingUid);
1831
Amith Yamasani04e0d262012-02-14 11:50:53 -08001832 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001833 } finally {
1834 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001835 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001836 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001837 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1838 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001839 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001840
1841 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1842 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001843 return true;
1844 }
1845
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001846 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001847 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001848 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001849 }
1850 }
1851
Amith Yamasani5be347b2013-03-31 17:44:31 -07001852 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001853 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001854 * running, then clone the account too.
1855 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001856 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001857 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001858 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001859 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001860 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001861 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001862 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001863 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001864 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001865 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001866 }
1867 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001868 }
1869 }
1870
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001871 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001872 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001873 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001874 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001875 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1876 Log.v(TAG, "hasFeatures: " + account
1877 + ", response " + response
1878 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001879 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001880 + ", pid " + Binder.getCallingPid());
1881 }
Fred Quintana382601f2010-03-25 12:25:10 -07001882 if (response == null) throw new IllegalArgumentException("response is null");
1883 if (account == null) throw new IllegalArgumentException("account is null");
1884 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001885 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001886 checkReadAccountsPermitted(callingUid, account.type, userId,
1887 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001888
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001889 long identityToken = clearCallingIdentity();
1890 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001891 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001892 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001893 } finally {
1894 restoreCallingIdentity(identityToken);
1895 }
1896 }
1897
1898 private class TestFeaturesSession extends Session {
1899 private final String[] mFeatures;
1900 private final Account mAccount;
1901
Amith Yamasani04e0d262012-02-14 11:50:53 -08001902 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001903 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001904 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001905 true /* stripAuthTokenFromResult */, account.name,
1906 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001907 mFeatures = features;
1908 mAccount = account;
1909 }
1910
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001911 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001912 public void run() throws RemoteException {
1913 try {
1914 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1915 } catch (RemoteException e) {
1916 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1917 }
1918 }
1919
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001920 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001921 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001922 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001923 IAccountManagerResponse response = getResponseAndClose();
1924 if (response != null) {
1925 try {
1926 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001927 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001928 return;
1929 }
Fred Quintana56285a62010-12-02 14:20:51 -08001930 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1931 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1932 + response);
1933 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001934 final Bundle newResult = new Bundle();
1935 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1936 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1937 response.onResult(newResult);
1938 } catch (RemoteException e) {
1939 // if the caller is dead then there is no one to care about remote exceptions
1940 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1941 Log.v(TAG, "failure while notifying response", e);
1942 }
1943 }
1944 }
1945 }
1946
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001947 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001948 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001949 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001950 + ", " + mAccount
1951 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1952 }
1953 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001954
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001955 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001956 public void renameAccount(
1957 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001958 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001959 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1960 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001961 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001962 + ", pid " + Binder.getCallingPid());
1963 }
1964 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001965 int userId = UserHandle.getCallingUserId();
1966 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001967 String msg = String.format(
1968 "uid %s cannot rename accounts of type: %s",
1969 callingUid,
1970 accountToRename.type);
1971 throw new SecurityException(msg);
1972 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001973 long identityToken = clearCallingIdentity();
1974 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001975 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001976 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001977 Bundle result = new Bundle();
1978 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1979 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1980 try {
1981 response.onResult(result);
1982 } catch (RemoteException e) {
1983 Log.w(TAG, e.getMessage());
1984 }
1985 } finally {
1986 restoreCallingIdentity(identityToken);
1987 }
1988 }
1989
1990 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001991 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001992 Account resultAccount = null;
1993 /*
1994 * Cancel existing notifications. Let authenticators
1995 * re-post notifications as required. But we don't know if
1996 * the authenticators have bound their notifications to
1997 * now stale account name data.
1998 *
1999 * With a rename api, we might not need to do this anymore but it
2000 * shouldn't hurt.
2001 */
2002 cancelNotification(
2003 getSigninRequiredNotificationId(accounts, accountToRename),
2004 new UserHandle(accounts.userId));
2005 synchronized(accounts.credentialsPermissionNotificationIds) {
2006 for (Pair<Pair<Account, String>, Integer> pair:
2007 accounts.credentialsPermissionNotificationIds.keySet()) {
2008 if (accountToRename.equals(pair.first.first)) {
2009 int id = accounts.credentialsPermissionNotificationIds.get(pair);
2010 cancelNotification(id, new UserHandle(accounts.userId));
2011 }
2012 }
2013 }
2014 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002015 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002016 db.beginTransaction();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002017 Account renamedAccount = new Account(newName, accountToRename.type);
2018 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002019 final long accountId = AccountsDbUtils.findAccountId(db, accountToRename);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002020 if (accountId >= 0) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002021 final ContentValues values = new ContentValues();
2022 values.put(ACCOUNTS_NAME, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002023 final String[] argsAccountId = { String.valueOf(accountId) };
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002024 db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2025 // Update NAME/PREVIOUS_NAME in DE accounts table
2026 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002027 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2028 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002029 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
2030 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002031 }
2032 } finally {
2033 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002034 }
2035 /*
2036 * Database transaction was successful. Clean up cached
2037 * data associated with the account in the user profile.
2038 */
2039 insertAccountIntoCacheLocked(accounts, renamedAccount);
2040 /*
2041 * Extract the data and token caches before removing the
2042 * old account to preserve the user data associated with
2043 * the account.
2044 */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002045 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
2046 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002047 removeAccountFromCacheLocked(accounts, accountToRename);
2048 /*
2049 * Update the cached data associated with the renamed
2050 * account.
2051 */
2052 accounts.userDataCache.put(renamedAccount, tmpData);
2053 accounts.authTokenCache.put(renamedAccount, tmpTokens);
2054 accounts.previousNameCache.put(
2055 renamedAccount,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002056 new AtomicReference<>(accountToRename.name));
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002057 resultAccount = renamedAccount;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002058
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002059 int parentUserId = accounts.userId;
2060 if (canHaveProfile(parentUserId)) {
2061 /*
2062 * Owner or system user account was renamed, rename the account for
2063 * those users with which the account was shared.
2064 */
2065 List<UserInfo> users = getUserManager().getUsers(true);
2066 for (UserInfo user : users) {
2067 if (user.isRestricted()
2068 && (user.restrictedProfileParentId == parentUserId)) {
2069 renameSharedAccountAsUser(accountToRename, newName, user.id);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002070 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002071 }
2072 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002073 sendAccountsChangedBroadcast(accounts.userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002074 }
2075 return resultAccount;
2076 }
2077
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002078 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002079 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002080 return userInfo != null && userInfo.canHaveProfile();
2081 }
2082
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002083 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002084 public void removeAccount(IAccountManagerResponse response, Account account,
2085 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002086 removeAccountAsUser(
2087 response,
2088 account,
2089 expectActivityLaunch,
2090 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002091 }
2092
2093 @Override
2094 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002095 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002096 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002097 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2098 Log.v(TAG, "removeAccount: " + account
2099 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002100 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002101 + ", pid " + Binder.getCallingPid()
2102 + ", for user id " + userId);
2103 }
2104 if (response == null) throw new IllegalArgumentException("response is null");
2105 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002106 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002107 if (isCrossUser(callingUid, userId)) {
2108 throw new SecurityException(
2109 String.format(
2110 "User %s tying remove account for %s" ,
2111 UserHandle.getCallingUserId(),
2112 userId));
2113 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002114 /*
2115 * Only the system or authenticator should be allowed to remove accounts for that
2116 * authenticator. This will let users remove accounts (via Settings in the system) but not
2117 * arbitrary applications (like competing authenticators).
2118 */
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002119 UserHandle user = UserHandle.of(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00002120 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
2121 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002122 String msg = String.format(
2123 "uid %s cannot remove accounts of type: %s",
2124 callingUid,
2125 account.type);
2126 throw new SecurityException(msg);
2127 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002128 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002129 try {
2130 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2131 "User cannot modify accounts");
2132 } catch (RemoteException re) {
2133 }
2134 return;
2135 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002136 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002137 try {
2138 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2139 "User cannot modify accounts of this type (policy).");
2140 } catch (RemoteException re) {
2141 }
2142 return;
2143 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002144 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002145 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002146 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002147 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002148 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08002149 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002150 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002151 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002152 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002153 }
2154 }
2155 }
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002156 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002157 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002158 logRecord(
2159 db,
2160 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2161 TABLE_ACCOUNTS,
2162 accountId,
2163 accounts,
2164 callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002165 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002166 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2167 } finally {
2168 restoreCallingIdentity(identityToken);
2169 }
2170 }
2171
2172 @Override
2173 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002174 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002175 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2176 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002177 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002178 + ", pid " + Binder.getCallingPid());
2179 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002180 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002181 if (account == null) {
2182 /*
2183 * Null accounts should result in returning false, as per
2184 * AccountManage.addAccountExplicitly(...) java doc.
2185 */
2186 Log.e(TAG, "account is null");
2187 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002188 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002189 String msg = String.format(
2190 "uid %s cannot explicitly add accounts of type: %s",
2191 callingUid,
2192 account.type);
2193 throw new SecurityException(msg);
2194 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07002195 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002196 UserAccounts accounts = getUserAccountsForCaller();
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002197 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002198 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002199 logRecord(
2200 db,
2201 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2202 TABLE_ACCOUNTS,
2203 accountId,
2204 accounts,
2205 callingUid);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002206 long identityToken = clearCallingIdentity();
2207 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002208 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002209 } finally {
2210 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07002211 }
Fred Quintana60307342009-03-24 22:48:12 -07002212 }
2213
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002214 private class RemoveAccountSession extends Session {
2215 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002216 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002217 Account account, boolean expectActivityLaunch) {
2218 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002219 true /* stripAuthTokenFromResult */, account.name,
2220 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002221 mAccount = account;
2222 }
2223
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002224 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002225 protected String toDebugString(long now) {
2226 return super.toDebugString(now) + ", removeAccount"
2227 + ", account " + mAccount;
2228 }
2229
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002230 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002231 public void run() throws RemoteException {
2232 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2233 }
2234
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002235 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002236 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002237 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002238 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2239 && !result.containsKey(AccountManager.KEY_INTENT)) {
2240 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002241 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002242 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002243 }
2244 IAccountManagerResponse response = getResponseAndClose();
2245 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002246 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2247 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2248 + response);
2249 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002250 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002251 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002252 try {
2253 response.onResult(result2);
2254 } catch (RemoteException e) {
2255 // ignore
2256 }
2257 }
2258 }
2259 super.onResult(result);
2260 }
2261 }
2262
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07002263 @VisibleForTesting
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002264 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002265 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08002266 }
2267
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002268 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002269 boolean isChanged = false;
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002270 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002271 if (!userUnlocked) {
2272 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
2273 + " is still locked. CE data will be removed later");
2274 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002275 synchronized (accounts.cacheLock) {
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002276 final SQLiteDatabase db = userUnlocked
2277 ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
2278 : accounts.openHelper.getWritableDatabase();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002279 db.beginTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002280 // Set to a dummy value, this will only be used if the database
2281 // transaction succeeds.
2282 long accountId = -1;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002283 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002284 accountId = AccountsDbUtils.findAccountId(db, account);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002285 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002286 AccountsDbUtils.deleteAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002287 if (userUnlocked) {
2288 // Delete from CE table
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002289 AccountsDbUtils.deleteCeAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002290 }
2291 db.setTransactionSuccessful();
2292 isChanged = true;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002293 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002294 } finally {
2295 db.endTransaction();
2296 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002297 if (isChanged) {
2298 removeAccountFromCacheLocked(accounts, account);
2299 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
2300 sendAccountsChangedBroadcast(accounts.userId);
2301 String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
2302 : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
2303 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
2304 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002305 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002306 long id = Binder.clearCallingIdentity();
2307 try {
2308 int parentUserId = accounts.userId;
2309 if (canHaveProfile(parentUserId)) {
2310 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002311 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002312 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002313 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002314 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002315 }
2316 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002317 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002318 } finally {
2319 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002320 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002321
2322 if (isChanged) {
2323 synchronized (accounts.credentialsPermissionNotificationIds) {
2324 for (Pair<Pair<Account, String>, Integer> key
2325 : accounts.credentialsPermissionNotificationIds.keySet()) {
2326 if (account.equals(key.first.first)
2327 && AccountManager.ACCOUNT_ACCESS_TOKEN.equals(key.first.second)) {
2328 final int uid = (Integer) key.second;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07002329 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002330 account, uid, false));
2331 }
2332 }
2333 }
2334 }
2335
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002336 return isChanged;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002337 }
2338
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002339 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002340 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002341 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002342 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2343 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07002344 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08002345 + ", pid " + Binder.getCallingPid());
2346 }
Fred Quintana382601f2010-03-25 12:25:10 -07002347 if (accountType == null) throw new IllegalArgumentException("accountType is null");
2348 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002349 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002350 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002351 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002352 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002353 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002354 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002355 db.beginTransaction();
2356 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002357 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002358 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002359 db.setTransactionSuccessful();
2360 } finally {
2361 db.endTransaction();
2362 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002363 }
Fred Quintana60307342009-03-24 22:48:12 -07002364 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002365 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002366 }
2367 }
2368
Carlos Valdivia91979be2015-05-22 14:11:35 -07002369 private void invalidateCustomTokenLocked(
2370 UserAccounts accounts,
2371 String accountType,
2372 String authToken) {
2373 if (authToken == null || accountType == null) {
2374 return;
2375 }
2376 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002377 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002378 }
2379
Amith Yamasani04e0d262012-02-14 11:50:53 -08002380 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
2381 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002382 if (authToken == null || accountType == null) {
2383 return;
2384 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002385 Cursor cursor = AccountsDbUtils.findAuthtokenForAllAccounts(db, accountType, authToken);
Fred Quintana33269202009-04-20 16:05:10 -07002386 try {
2387 while (cursor.moveToNext()) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002388 String authTokenId = cursor.getString(0);
Fred Quintana33269202009-04-20 16:05:10 -07002389 String accountName = cursor.getString(1);
2390 String authTokenType = cursor.getString(2);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002391 AccountsDbUtils.deleteAuthToken(db, authTokenId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002392 writeAuthTokenIntoCacheLocked(
2393 accounts,
2394 db,
2395 new Account(accountName, accountType),
2396 authTokenType,
2397 null);
Fred Quintana60307342009-03-24 22:48:12 -07002398 }
Fred Quintana33269202009-04-20 16:05:10 -07002399 } finally {
2400 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07002401 }
2402 }
2403
Carlos Valdivia91979be2015-05-22 14:11:35 -07002404 private void saveCachedToken(
2405 UserAccounts accounts,
2406 Account account,
2407 String callerPkg,
2408 byte[] callerSigDigest,
2409 String tokenType,
2410 String token,
2411 long expiryMillis) {
2412
2413 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2414 return;
2415 }
2416 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002417 UserHandle.of(accounts.userId));
Carlos Valdivia91979be2015-05-22 14:11:35 -07002418 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002419 accounts.accountTokenCaches.put(
2420 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002421 }
2422 }
2423
Amith Yamasani04e0d262012-02-14 11:50:53 -08002424 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2425 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07002426 if (account == null || type == null) {
2427 return false;
2428 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002429 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002430 UserHandle.of(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002431 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002432 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002433 db.beginTransaction();
2434 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002435 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002436 if (accountId < 0) {
2437 return false;
2438 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002439 AccountsDbUtils.deleteAuthtokensByAccountIdAndType(db, accountId, type);
2440 if (AccountsDbUtils.insertAuthToken(db, accountId, type, authToken) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002441 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002442 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002443 return true;
2444 }
Fred Quintana33269202009-04-20 16:05:10 -07002445 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002446 } finally {
2447 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07002448 }
Fred Quintana60307342009-03-24 22:48:12 -07002449 }
2450 }
2451
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002452 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002453 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002454 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002455 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2456 Log.v(TAG, "peekAuthToken: " + account
2457 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002458 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002459 + ", pid " + Binder.getCallingPid());
2460 }
Fred Quintana382601f2010-03-25 12:25:10 -07002461 if (account == null) throw new IllegalArgumentException("account is null");
2462 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002463 int userId = UserHandle.getCallingUserId();
2464 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002465 String msg = String.format(
2466 "uid %s cannot peek the authtokens associated with accounts of type: %s",
2467 callingUid,
2468 account.type);
2469 throw new SecurityException(msg);
2470 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002471 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07002472 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2473 + callingUid);
2474 return null;
2475 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002476 long identityToken = clearCallingIdentity();
2477 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002478 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002479 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002480 } finally {
2481 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002482 }
Fred Quintana60307342009-03-24 22:48:12 -07002483 }
2484
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002485 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002486 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002487 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002488 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2489 Log.v(TAG, "setAuthToken: " + account
2490 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002491 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002492 + ", pid " + Binder.getCallingPid());
2493 }
Fred Quintana382601f2010-03-25 12:25:10 -07002494 if (account == null) throw new IllegalArgumentException("account is null");
2495 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002496 int userId = UserHandle.getCallingUserId();
2497 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002498 String msg = String.format(
2499 "uid %s cannot set auth tokens associated with accounts of type: %s",
2500 callingUid,
2501 account.type);
2502 throw new SecurityException(msg);
2503 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002504 long identityToken = clearCallingIdentity();
2505 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002506 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002507 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002508 } finally {
2509 restoreCallingIdentity(identityToken);
2510 }
Fred Quintana60307342009-03-24 22:48:12 -07002511 }
2512
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002513 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002514 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002515 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002516 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2517 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002518 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002519 + ", pid " + Binder.getCallingPid());
2520 }
Fred Quintana382601f2010-03-25 12:25:10 -07002521 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002522 int userId = UserHandle.getCallingUserId();
2523 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002524 String msg = String.format(
2525 "uid %s cannot set secrets for accounts of type: %s",
2526 callingUid,
2527 account.type);
2528 throw new SecurityException(msg);
2529 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002530 long identityToken = clearCallingIdentity();
2531 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002532 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002533 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002534 } finally {
2535 restoreCallingIdentity(identityToken);
2536 }
Fred Quintana60307342009-03-24 22:48:12 -07002537 }
2538
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002539 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2540 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07002541 if (account == null) {
2542 return;
2543 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002544 boolean isChanged = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002545 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002546 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002547 db.beginTransaction();
2548 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002549 final long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002550 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002551 AccountsDbUtils.updateAccountPassword(db, accountId, password);
2552 AccountsDbUtils.deleteAuthTokensByAccountId(db, accountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002553 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002554 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002555 db.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002556 // If there is an account whose password will be updated and the database
2557 // transactions succeed, then we say that a change has occured. Even if the
2558 // new password is the same as the old and there were no authtokens to delete.
2559 isChanged = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002560 String action = (password == null || password.length() == 0) ?
2561 DebugDbHelper.ACTION_CLEAR_PASSWORD
2562 : DebugDbHelper.ACTION_SET_PASSWORD;
2563 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08002564 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002565 } finally {
2566 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002567 if (isChanged) {
2568 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2569 sendAccountsChangedBroadcast(accounts.userId);
2570 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002571 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002572 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07002573 }
2574
Amith Yamasani04e0d262012-02-14 11:50:53 -08002575 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08002576 Log.i(TAG, "the accounts changed, sending broadcast of "
2577 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07002578 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07002579 }
2580
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002581 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002582 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002583 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002584 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2585 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002586 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002587 + ", pid " + Binder.getCallingPid());
2588 }
Fred Quintana382601f2010-03-25 12:25:10 -07002589 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002590 int userId = UserHandle.getCallingUserId();
2591 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002592 String msg = String.format(
2593 "uid %s cannot clear passwords for accounts of type: %s",
2594 callingUid,
2595 account.type);
2596 throw new SecurityException(msg);
2597 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002598 long identityToken = clearCallingIdentity();
2599 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002600 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002601 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002602 } finally {
2603 restoreCallingIdentity(identityToken);
2604 }
Fred Quintana60307342009-03-24 22:48:12 -07002605 }
2606
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002607 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002608 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002609 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002610 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2611 Log.v(TAG, "setUserData: " + account
2612 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002613 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002614 + ", pid " + Binder.getCallingPid());
2615 }
Fred Quintana382601f2010-03-25 12:25:10 -07002616 if (key == null) throw new IllegalArgumentException("key is null");
2617 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002618 int userId = UserHandle.getCallingUserId();
2619 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002620 String msg = String.format(
2621 "uid %s cannot set user data for accounts of type: %s",
2622 callingUid,
2623 account.type);
2624 throw new SecurityException(msg);
2625 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002626 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002627 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002628 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002629 synchronized (accounts.cacheLock) {
2630 if (!accountExistsCacheLocked(accounts, account)) {
2631 return;
2632 }
2633 setUserdataInternalLocked(accounts, account, key, value);
2634 }
Fred Quintana60307342009-03-24 22:48:12 -07002635 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002636 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002637 }
2638 }
2639
Simranjit Kohli858511c2016-03-10 18:36:11 +00002640 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2641 if (accounts.accountCache.containsKey(account.type)) {
2642 for (Account acc : accounts.accountCache.get(account.type)) {
2643 if (acc.name.equals(account.name)) {
2644 return true;
2645 }
2646 }
2647 }
2648 return false;
2649 }
2650
2651 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002652 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07002653 if (account == null || key == null) {
2654 return;
2655 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002656 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2657 db.beginTransaction();
2658 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002659 long accountId = AccountsDbUtils.findAccountId(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002660 if (accountId < 0) {
2661 return;
2662 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002663 long extrasId = AccountsDbUtils.findExtrasIdByAccountId(db, accountId, key);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002664 if (extrasId < 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002665 extrasId = AccountsDbUtils.insertExtra(db, accountId, key, value);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002666 if (extrasId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002667 return;
2668 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002669 } else if (!AccountsDbUtils.updateExtra(db, extrasId, value)) {
2670 return;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002671 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002672 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
2673 db.setTransactionSuccessful();
2674 } finally {
2675 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002676 }
2677 }
2678
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002679 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08002680 if (result == null) {
2681 Log.e(TAG, "the result is unexpectedly null", new Exception());
2682 }
2683 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2684 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2685 + response);
2686 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002687 try {
2688 response.onResult(result);
2689 } catch (RemoteException e) {
2690 // if the caller is dead then there is no one to care about remote
2691 // exceptions
2692 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2693 Log.v(TAG, "failure while notifying response", e);
2694 }
2695 }
2696 }
2697
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002698 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07002699 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2700 final String authTokenType)
2701 throws RemoteException {
2702 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08002703 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2704
Fred Quintanad9640ec2012-05-23 12:37:00 -07002705 final int callingUid = getCallingUid();
2706 clearCallingIdentity();
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07002707 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07002708 throw new SecurityException("can only call from system");
2709 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002710 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002711 long identityToken = clearCallingIdentity();
2712 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002713 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002714 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2715 false /* stripAuthTokenFromResult */, null /* accountName */,
2716 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002717 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002718 protected String toDebugString(long now) {
2719 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07002720 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08002721 + ", authTokenType " + authTokenType;
2722 }
2723
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002724 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002725 public void run() throws RemoteException {
2726 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2727 }
2728
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002729 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002730 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002731 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002732 if (result != null) {
2733 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2734 Bundle bundle = new Bundle();
2735 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2736 super.onResult(bundle);
2737 return;
2738 } else {
2739 super.onResult(result);
2740 }
2741 }
2742 }.bind();
2743 } finally {
2744 restoreCallingIdentity(identityToken);
2745 }
2746 }
2747
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002748 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07002749 public void getAuthToken(
2750 IAccountManagerResponse response,
2751 final Account account,
2752 final String authTokenType,
2753 final boolean notifyOnAuthFailure,
2754 final boolean expectActivityLaunch,
2755 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002756 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002757 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2758 Log.v(TAG, "getAuthToken: " + account
2759 + ", response " + response
2760 + ", authTokenType " + authTokenType
2761 + ", notifyOnAuthFailure " + notifyOnAuthFailure
2762 + ", expectActivityLaunch " + expectActivityLaunch
2763 + ", caller's uid " + Binder.getCallingUid()
2764 + ", pid " + Binder.getCallingPid());
2765 }
Fred Quintana382601f2010-03-25 12:25:10 -07002766 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002767 try {
2768 if (account == null) {
2769 Slog.w(TAG, "getAuthToken called with null account");
2770 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2771 return;
2772 }
2773 if (authTokenType == null) {
2774 Slog.w(TAG, "getAuthToken called with null authTokenType");
2775 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2776 return;
2777 }
2778 } catch (RemoteException e) {
2779 Slog.w(TAG, "Failed to report error back to the client." + e);
2780 return;
2781 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002782 int userId = UserHandle.getCallingUserId();
2783 long ident = Binder.clearCallingIdentity();
2784 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002785 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002786 try {
2787 accounts = getUserAccounts(userId);
2788 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2789 AuthenticatorDescription.newKey(account.type), accounts.userId);
2790 } finally {
2791 Binder.restoreCallingIdentity(ident);
2792 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002793
Costin Manolachea40c6302010-12-13 14:50:45 -08002794 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07002795 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08002796
2797 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002798 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002799 final boolean permissionGranted =
2800 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08002801
Carlos Valdivia91979be2015-05-22 14:11:35 -07002802 // Get the calling package. We will use it for the purpose of caching.
2803 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07002804 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002805 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07002806 try {
2807 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2808 } finally {
2809 Binder.restoreCallingIdentity(ident);
2810 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002811 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2812 String msg = String.format(
2813 "Uid %s is attempting to illegally masquerade as package %s!",
2814 callerUid,
2815 callerPkg);
2816 throw new SecurityException(msg);
2817 }
2818
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002819 // let authenticator know the identity of the caller
2820 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2821 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07002822
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002823 if (notifyOnAuthFailure) {
2824 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08002825 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002826
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002827 long identityToken = clearCallingIdentity();
2828 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07002829 // Distill the caller's package signatures into a single digest.
2830 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2831
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002832 // if the caller has permission, do the peek. otherwise go the more expensive
2833 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08002834 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002835 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002836 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002837 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002838 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2839 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2840 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002841 onResult(response, result);
2842 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002843 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002844 }
2845
Carlos Valdivia91979be2015-05-22 14:11:35 -07002846 if (customTokens) {
2847 /*
2848 * Look up tokens in the new cache only if the loginOptions don't have parameters
2849 * outside of those expected to be injected by the AccountManager, e.g.
2850 * ANDORID_PACKAGE_NAME.
2851 */
2852 String token = readCachedTokenInternal(
2853 accounts,
2854 account,
2855 authTokenType,
2856 callerPkg,
2857 callerPkgSigDigest);
2858 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002859 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2860 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2861 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002862 Bundle result = new Bundle();
2863 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2864 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2865 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2866 onResult(response, result);
2867 return;
2868 }
2869 }
2870
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002871 new Session(
2872 accounts,
2873 response,
2874 account.type,
2875 expectActivityLaunch,
2876 false /* stripAuthTokenFromResult */,
2877 account.name,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002878 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002879 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002880 protected String toDebugString(long now) {
2881 if (loginOptions != null) loginOptions.keySet();
2882 return super.toDebugString(now) + ", getAuthToken"
2883 + ", " + account
2884 + ", authTokenType " + authTokenType
2885 + ", loginOptions " + loginOptions
2886 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2887 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002888
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002889 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002890 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002891 // If the caller doesn't have permission then create and return the
2892 // "grant permission" intent instead of the "getAuthToken" intent.
2893 if (!permissionGranted) {
2894 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2895 } else {
2896 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2897 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002898 }
2899
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002900 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002901 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002902 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002903 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002904 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002905 Intent intent = newGrantCredentialsPermissionIntent(
2906 account,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002907 null,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002908 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002909 new AccountAuthenticatorResponse(this),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002910 authTokenType,
2911 true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002912 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002913 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002914 onResult(bundle);
2915 return;
2916 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002917 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002918 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002919 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2920 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002921 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002922 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002923 "the type and name should not be empty");
2924 return;
2925 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002926 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002927 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002928 saveAuthTokenToDatabase(
2929 mAccounts,
2930 resultAccount,
2931 authTokenType,
2932 authToken);
2933 }
2934 long expiryMillis = result.getLong(
2935 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2936 if (customTokens
2937 && expiryMillis > System.currentTimeMillis()) {
2938 saveCachedToken(
2939 mAccounts,
2940 account,
2941 callerPkg,
2942 callerPkgSigDigest,
2943 authTokenType,
2944 authToken,
2945 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002946 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002947 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002948
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002949 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002950 if (intent != null && notifyOnAuthFailure && !customTokens) {
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002951 /*
2952 * Make sure that the supplied intent is owned by the authenticator
2953 * giving it to the system. Otherwise a malicious authenticator could
2954 * have users launching arbitrary activities by tricking users to
2955 * interact with malicious notifications.
2956 */
2957 checkKeyIntent(
2958 Binder.getCallingUid(),
2959 intent);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002960 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002961 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002962 intent, "android", accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002963 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002964 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002965 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002966 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002967 }.bind();
2968 } finally {
2969 restoreCallingIdentity(identityToken);
2970 }
Fred Quintana60307342009-03-24 22:48:12 -07002971 }
2972
Carlos Valdivia91979be2015-05-22 14:11:35 -07002973 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2974 MessageDigest digester;
2975 try {
2976 digester = MessageDigest.getInstance("SHA-256");
2977 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2978 callerPkg, PackageManager.GET_SIGNATURES);
2979 for (Signature sig : pkgInfo.signatures) {
2980 digester.update(sig.toByteArray());
2981 }
2982 } catch (NoSuchAlgorithmException x) {
2983 Log.wtf(TAG, "SHA-256 should be available", x);
2984 digester = null;
2985 } catch (NameNotFoundException e) {
2986 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2987 digester = null;
2988 }
2989 return (digester == null) ? null : digester.digest();
2990 }
2991
Dianne Hackborn41203752012-08-31 14:05:51 -07002992 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002993 String packageName, int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002994 int uid = intent.getIntExtra(
2995 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2996 String authTokenType = intent.getStringExtra(
2997 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002998 final String titleAndSubtitle =
2999 mContext.getString(R.string.permission_request_notification_with_subtitle,
3000 account.name);
3001 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07003002 String title = titleAndSubtitle;
3003 String subtitle = "";
3004 if (index > 0) {
3005 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04003006 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07003007 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07003008 UserHandle user = UserHandle.of(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003009 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04003010 Notification n = new Notification.Builder(contextForUser)
3011 .setSmallIcon(android.R.drawable.stat_sys_warning)
3012 .setWhen(0)
3013 .setColor(contextForUser.getColor(
3014 com.android.internal.R.color.system_notification_accent_color))
3015 .setContentTitle(title)
3016 .setContentText(subtitle)
3017 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
3018 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
3019 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003020 installNotification(getCredentialPermissionNotificationId(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003021 account, authTokenType, uid), n, packageName, user.getIdentifier());
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003022 }
3023
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003024 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
3025 int uid, AccountAuthenticatorResponse response, String authTokenType,
3026 boolean startInNewTask) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003027
3028 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003029
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003030 if (startInNewTask) {
3031 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
3032 // Since it was set in Eclair+ we can't change it without breaking apps using
3033 // the intent from a non-Activity context. This is the default behavior.
3034 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3035 }
3036 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
3037 authTokenType, uid) + (packageName != null ? packageName : "")));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003038 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003039 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
3040 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003041 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003042
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003043 return intent;
3044 }
3045
3046 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
3047 int uid) {
3048 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003049 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003050 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003051 final Pair<Pair<Account, String>, Integer> key =
3052 new Pair<Pair<Account, String>, Integer>(
3053 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003054 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003055 if (id == null) {
3056 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003057 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003058 }
3059 }
3060 return id;
3061 }
3062
Amith Yamasani04e0d262012-02-14 11:50:53 -08003063 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003064 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003065 synchronized (accounts.signinRequiredNotificationIds) {
3066 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003067 if (id == null) {
3068 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003069 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003070 }
3071 }
3072 return id;
3073 }
3074
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003075 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07003076 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07003077 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003078 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003079 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003080 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3081 Log.v(TAG, "addAccount: accountType " + accountType
3082 + ", response " + response
3083 + ", authTokenType " + authTokenType
3084 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3085 + ", expectActivityLaunch " + expectActivityLaunch
3086 + ", caller's uid " + Binder.getCallingUid()
3087 + ", pid " + Binder.getCallingPid());
3088 }
Fred Quintana382601f2010-03-25 12:25:10 -07003089 if (response == null) throw new IllegalArgumentException("response is null");
3090 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003091
Amith Yamasani71e6c692013-03-24 17:39:28 -07003092 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003093 final int uid = Binder.getCallingUid();
3094 final int userId = UserHandle.getUserId(uid);
3095 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003096 try {
3097 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3098 "User is not allowed to add an account!");
3099 } catch (RemoteException re) {
3100 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003101 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003102 return;
3103 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003104 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003105 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003106 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3107 "User cannot modify accounts of this type (policy).");
3108 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003109 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003110 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3111 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003112 return;
3113 }
3114
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003115 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003116 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3117 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3118 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3119
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003120 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003121 long identityToken = clearCallingIdentity();
3122 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003123 UserAccounts accounts = getUserAccounts(usrId);
3124 logRecordWithUid(
3125 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003126 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003127 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003128 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003129 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003130 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07003131 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07003132 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003133 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003134
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003135 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003136 protected String toDebugString(long now) {
3137 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07003138 + ", accountType " + accountType
3139 + ", requiredFeatures "
3140 + (requiredFeatures != null
3141 ? TextUtils.join(",", requiredFeatures)
3142 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003143 }
3144 }.bind();
3145 } finally {
3146 restoreCallingIdentity(identityToken);
3147 }
Fred Quintana60307342009-03-24 22:48:12 -07003148 }
3149
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003150 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003151 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
3152 final String authTokenType, final String[] requiredFeatures,
3153 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003154 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003155 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003156 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3157 Log.v(TAG, "addAccount: accountType " + accountType
3158 + ", response " + response
3159 + ", authTokenType " + authTokenType
3160 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3161 + ", expectActivityLaunch " + expectActivityLaunch
3162 + ", caller's uid " + Binder.getCallingUid()
3163 + ", pid " + Binder.getCallingPid()
3164 + ", for user id " + userId);
3165 }
3166 if (response == null) throw new IllegalArgumentException("response is null");
3167 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003168 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003169 if (isCrossUser(callingUid, userId)) {
3170 throw new SecurityException(
3171 String.format(
3172 "User %s trying to add account for %s" ,
3173 UserHandle.getCallingUserId(),
3174 userId));
3175 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003176
3177 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003178 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003179 try {
3180 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3181 "User is not allowed to add an account!");
3182 } catch (RemoteException re) {
3183 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003184 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003185 return;
3186 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003187 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003188 try {
3189 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3190 "User cannot modify accounts of this type (policy).");
3191 } catch (RemoteException re) {
3192 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003193 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3194 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003195 return;
3196 }
3197
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003198 final int pid = Binder.getCallingPid();
3199 final int uid = Binder.getCallingUid();
3200 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3201 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3202 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3203
3204 long identityToken = clearCallingIdentity();
3205 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003206 UserAccounts accounts = getUserAccounts(userId);
3207 logRecordWithUid(
3208 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003209 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003210 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003211 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003212 @Override
3213 public void run() throws RemoteException {
3214 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3215 options);
3216 }
3217
3218 @Override
3219 protected String toDebugString(long now) {
3220 return super.toDebugString(now) + ", addAccount"
3221 + ", accountType " + accountType
3222 + ", requiredFeatures "
3223 + (requiredFeatures != null
3224 ? TextUtils.join(",", requiredFeatures)
3225 : null);
3226 }
3227 }.bind();
3228 } finally {
3229 restoreCallingIdentity(identityToken);
3230 }
3231 }
3232
Sandra Kwan78812282015-11-04 11:19:47 -08003233 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003234 public void startAddAccountSession(
3235 final IAccountManagerResponse response,
3236 final String accountType,
3237 final String authTokenType,
3238 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08003239 final boolean expectActivityLaunch,
3240 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003241 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003242 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3243 Log.v(TAG,
3244 "startAddAccountSession: accountType " + accountType
3245 + ", response " + response
3246 + ", authTokenType " + authTokenType
3247 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3248 + ", expectActivityLaunch " + expectActivityLaunch
3249 + ", caller's uid " + Binder.getCallingUid()
3250 + ", pid " + Binder.getCallingPid());
3251 }
3252 if (response == null) {
3253 throw new IllegalArgumentException("response is null");
3254 }
3255 if (accountType == null) {
3256 throw new IllegalArgumentException("accountType is null");
3257 }
3258
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003259 final int uid = Binder.getCallingUid();
3260 final int userId = UserHandle.getUserId(uid);
3261 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003262 try {
3263 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3264 "User is not allowed to add an account!");
3265 } catch (RemoteException re) {
3266 }
3267 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3268 return;
3269 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003270 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003271 try {
3272 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3273 "User cannot modify accounts of this type (policy).");
3274 } catch (RemoteException re) {
3275 }
3276 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3277 userId);
3278 return;
3279 }
Sandra Kwan78812282015-11-04 11:19:47 -08003280 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08003281 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3282 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3283 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3284
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003285 // Check to see if the Password should be included to the caller.
3286 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3287 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003288 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003289
Sandra Kwan78812282015-11-04 11:19:47 -08003290 long identityToken = clearCallingIdentity();
3291 try {
Hongming Jin368aa192016-07-29 14:29:54 -07003292 UserAccounts accounts = getUserAccounts(userId);
Sandra Kwan78812282015-11-04 11:19:47 -08003293 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
3294 TABLE_ACCOUNTS, uid);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003295 new StartAccountSession(
3296 accounts,
3297 response,
3298 accountType,
3299 expectActivityLaunch,
3300 null /* accountName */,
3301 false /* authDetailsRequired */,
3302 true /* updateLastAuthenticationTime */,
3303 isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003304 @Override
3305 public void run() throws RemoteException {
3306 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3307 requiredFeatures, options);
3308 }
3309
3310 @Override
3311 protected String toDebugString(long now) {
3312 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3313 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3314 + accountType + ", requiredFeatures "
3315 + (requiredFeatures != null ? requiredFeaturesStr : null);
3316 }
3317 }.bind();
3318 } finally {
3319 restoreCallingIdentity(identityToken);
3320 }
3321 }
3322
3323 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3324 private abstract class StartAccountSession extends Session {
3325
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003326 private final boolean mIsPasswordForwardingAllowed;
3327
3328 public StartAccountSession(
3329 UserAccounts accounts,
3330 IAccountManagerResponse response,
3331 String accountType,
3332 boolean expectActivityLaunch,
3333 String accountName,
3334 boolean authDetailsRequired,
3335 boolean updateLastAuthenticationTime,
3336 boolean isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003337 super(accounts, response, accountType, expectActivityLaunch,
3338 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3339 updateLastAuthenticationTime);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003340 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
Sandra Kwan78812282015-11-04 11:19:47 -08003341 }
3342
3343 @Override
3344 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003345 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003346 mNumResults++;
3347 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08003348 if (result != null
3349 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003350 checkKeyIntent(
3351 Binder.getCallingUid(),
3352 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08003353 }
Sandra Kwan78812282015-11-04 11:19:47 -08003354 IAccountManagerResponse response;
3355 if (mExpectActivityLaunch && result != null
3356 && result.containsKey(AccountManager.KEY_INTENT)) {
3357 response = mResponse;
3358 } else {
3359 response = getResponseAndClose();
3360 }
3361 if (response == null) {
3362 return;
3363 }
3364 if (result == null) {
3365 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3366 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3367 + response);
3368 }
3369 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3370 "null bundle returned");
3371 return;
3372 }
3373
3374 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3375 // All AccountManager error codes are greater
3376 // than 0
3377 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3378 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3379 return;
3380 }
3381
Hongming Jin368aa192016-07-29 14:29:54 -07003382 // Omit passwords if the caller isn't permitted to see them.
3383 if (!mIsPasswordForwardingAllowed) {
3384 result.remove(AccountManager.KEY_PASSWORD);
3385 }
3386
Sandra Kwan78812282015-11-04 11:19:47 -08003387 // Strip auth token from result.
3388 result.remove(AccountManager.KEY_AUTHTOKEN);
3389
3390 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3391 Log.v(TAG,
3392 getClass().getSimpleName() + " calling onResult() on response " + response);
3393 }
3394
3395 // Get the session bundle created by authenticator. The
3396 // bundle contains data necessary for finishing the session
3397 // later. The session bundle will be encrypted here and
3398 // decrypted later when trying to finish the session.
3399 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3400 if (sessionBundle != null) {
3401 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3402 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08003403 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003404 Log.w(TAG, "Account type in session bundle doesn't match request.");
3405 }
3406 // Add accountType info to session bundle. This will
3407 // override any value set by authenticator.
3408 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3409
3410 // Encrypt session bundle before returning to caller.
3411 try {
3412 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3413 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3414 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3415 } catch (GeneralSecurityException e) {
3416 if (Log.isLoggable(TAG, Log.DEBUG)) {
3417 Log.v(TAG, "Failed to encrypt session bundle!", e);
3418 }
3419 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3420 "failed to encrypt session bundle");
3421 return;
3422 }
3423 }
3424
3425 sendResponse(response, result);
3426 }
3427 }
3428
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003429 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08003430 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003431 @NonNull Bundle sessionBundle,
3432 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003433 Bundle appInfo,
3434 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003435 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08003436 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003437 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3438 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003439 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003440 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08003441 + ", caller's uid " + callingUid
3442 + ", caller's user id " + UserHandle.getCallingUserId()
3443 + ", pid " + Binder.getCallingPid()
3444 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003445 }
3446 if (response == null) {
3447 throw new IllegalArgumentException("response is null");
3448 }
3449
3450 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3451 // Account type is added to it before encryption.
3452 if (sessionBundle == null || sessionBundle.size() == 0) {
3453 throw new IllegalArgumentException("sessionBundle is empty");
3454 }
3455
Sandra Kwan0b84b452016-01-20 15:25:42 -08003456 // Only allow the system process to finish session for other users
3457 if (isCrossUser(callingUid, userId)) {
3458 throw new SecurityException(
3459 String.format(
3460 "User %s trying to finish session for %s without cross user permission",
3461 UserHandle.getCallingUserId(),
3462 userId));
3463 }
3464
Sandra Kwan0b84b452016-01-20 15:25:42 -08003465 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003466 sendErrorResponse(response,
3467 AccountManager.ERROR_CODE_USER_RESTRICTED,
3468 "User is not allowed to add an account!");
3469 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3470 return;
3471 }
3472
3473 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003474 final Bundle decryptedBundle;
3475 final String accountType;
3476 // First decrypt session bundle to get account type for checking permission.
3477 try {
3478 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3479 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3480 if (decryptedBundle == null) {
3481 sendErrorResponse(
3482 response,
3483 AccountManager.ERROR_CODE_BAD_REQUEST,
3484 "failed to decrypt session bundle");
3485 return;
3486 }
3487 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3488 // Account type cannot be null. This should not happen if session bundle was created
3489 // properly by #StartAccountSession.
3490 if (TextUtils.isEmpty(accountType)) {
3491 sendErrorResponse(
3492 response,
3493 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3494 "accountType is empty");
3495 return;
3496 }
3497
3498 // If by any chances, decryptedBundle contains colliding keys with
3499 // system info
3500 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3501 // update credentials flow, we should replace with the new values of the current call.
3502 if (appInfo != null) {
3503 decryptedBundle.putAll(appInfo);
3504 }
3505
3506 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08003507 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003508 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3509 } catch (GeneralSecurityException e) {
3510 if (Log.isLoggable(TAG, Log.DEBUG)) {
3511 Log.v(TAG, "Failed to decrypt session bundle!", e);
3512 }
3513 sendErrorResponse(
3514 response,
3515 AccountManager.ERROR_CODE_BAD_REQUEST,
3516 "failed to decrypt session bundle");
3517 return;
3518 }
3519
Sandra Kwan0b84b452016-01-20 15:25:42 -08003520 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003521 sendErrorResponse(
3522 response,
3523 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3524 "User cannot modify accounts of this type (policy).");
3525 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3526 userId);
3527 return;
3528 }
3529
3530 long identityToken = clearCallingIdentity();
3531 try {
3532 UserAccounts accounts = getUserAccounts(userId);
3533 logRecordWithUid(
3534 accounts,
3535 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3536 TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003537 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003538 new Session(
3539 accounts,
3540 response,
3541 accountType,
3542 expectActivityLaunch,
3543 true /* stripAuthTokenFromResult */,
3544 null /* accountName */,
3545 false /* authDetailsRequired */,
3546 true /* updateLastAuthenticationTime */) {
3547 @Override
3548 public void run() throws RemoteException {
3549 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3550 }
3551
3552 @Override
3553 protected String toDebugString(long now) {
3554 return super.toDebugString(now)
3555 + ", finishSession"
3556 + ", accountType " + accountType;
3557 }
3558 }.bind();
3559 } finally {
3560 restoreCallingIdentity(identityToken);
3561 }
3562 }
3563
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003564 private void showCantAddAccount(int errorCode, int userId) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003565 final DevicePolicyManagerInternal dpmi =
3566 LocalServices.getService(DevicePolicyManagerInternal.class);
3567 Intent intent = null;
Nicolas Prevot14fc1972016-08-24 14:21:38 +01003568 if (dpmi == null) {
3569 intent = getDefaultCantAddAccountIntent(errorCode);
3570 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003571 intent = dpmi.createUserRestrictionSupportIntent(userId,
3572 UserManager.DISALLOW_MODIFY_ACCOUNTS);
3573 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3574 intent = dpmi.createShowAdminSupportIntent(userId, false);
3575 }
3576 if (intent == null) {
3577 intent = getDefaultCantAddAccountIntent(errorCode);
3578 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003579 long identityToken = clearCallingIdentity();
3580 try {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003581 mContext.startActivityAsUser(intent, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003582 } finally {
3583 restoreCallingIdentity(identityToken);
3584 }
3585 }
3586
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003587 /**
3588 * Called when we don't know precisely who is preventing us from adding an account.
3589 */
3590 private Intent getDefaultCantAddAccountIntent(int errorCode) {
3591 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3592 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3593 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3594 return cantAddAccount;
3595 }
3596
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003597 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003598 public void confirmCredentialsAsUser(
3599 IAccountManagerResponse response,
3600 final Account account,
3601 final Bundle options,
3602 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003603 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003604 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003605 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003606 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3607 Log.v(TAG, "confirmCredentials: " + account
3608 + ", response " + response
3609 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003610 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003611 + ", pid " + Binder.getCallingPid());
3612 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003613 // Only allow the system process to read accounts of other users
3614 if (isCrossUser(callingUid, userId)) {
3615 throw new SecurityException(
3616 String.format(
3617 "User %s trying to confirm account credentials for %s" ,
3618 UserHandle.getCallingUserId(),
3619 userId));
3620 }
Fred Quintana382601f2010-03-25 12:25:10 -07003621 if (response == null) throw new IllegalArgumentException("response is null");
3622 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003623 long identityToken = clearCallingIdentity();
3624 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003625 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003626 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003627 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003628 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003629 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003630 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003631 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003632 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003633 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003634 protected String toDebugString(long now) {
3635 return super.toDebugString(now) + ", confirmCredentials"
3636 + ", " + account;
3637 }
3638 }.bind();
3639 } finally {
3640 restoreCallingIdentity(identityToken);
3641 }
Fred Quintana60307342009-03-24 22:48:12 -07003642 }
3643
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003644 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003645 public void updateCredentials(IAccountManagerResponse response, final Account account,
3646 final String authTokenType, final boolean expectActivityLaunch,
3647 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003648 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003649 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3650 Log.v(TAG, "updateCredentials: " + account
3651 + ", response " + response
3652 + ", authTokenType " + authTokenType
3653 + ", expectActivityLaunch " + expectActivityLaunch
3654 + ", caller's uid " + Binder.getCallingUid()
3655 + ", pid " + Binder.getCallingPid());
3656 }
Fred Quintana382601f2010-03-25 12:25:10 -07003657 if (response == null) throw new IllegalArgumentException("response is null");
3658 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003659 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003660 long identityToken = clearCallingIdentity();
3661 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003662 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003663 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003664 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003665 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003666 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003667 public void run() throws RemoteException {
3668 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3669 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003670 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003671 protected String toDebugString(long now) {
3672 if (loginOptions != null) loginOptions.keySet();
3673 return super.toDebugString(now) + ", updateCredentials"
3674 + ", " + account
3675 + ", authTokenType " + authTokenType
3676 + ", loginOptions " + loginOptions;
3677 }
3678 }.bind();
3679 } finally {
3680 restoreCallingIdentity(identityToken);
3681 }
Fred Quintana60307342009-03-24 22:48:12 -07003682 }
3683
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003684 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003685 public void startUpdateCredentialsSession(
3686 IAccountManagerResponse response,
3687 final Account account,
3688 final String authTokenType,
3689 final boolean expectActivityLaunch,
3690 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003691 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08003692 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3693 Log.v(TAG,
3694 "startUpdateCredentialsSession: " + account + ", response " + response
3695 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3696 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3697 + ", pid " + Binder.getCallingPid());
3698 }
3699 if (response == null) {
3700 throw new IllegalArgumentException("response is null");
3701 }
3702 if (account == null) {
3703 throw new IllegalArgumentException("account is null");
3704 }
Sandra Kwana578d112015-12-16 16:01:43 -08003705
3706 final int uid = Binder.getCallingUid();
Sandra Kwane68c37e2015-11-12 17:11:49 -08003707 int userId = UserHandle.getCallingUserId();
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003708
3709 // Check to see if the Password should be included to the caller.
3710 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3711 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003712 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003713
Sandra Kwane68c37e2015-11-12 17:11:49 -08003714 long identityToken = clearCallingIdentity();
3715 try {
3716 UserAccounts accounts = getUserAccounts(userId);
3717 new StartAccountSession(
3718 accounts,
3719 response,
3720 account.type,
3721 expectActivityLaunch,
3722 account.name,
3723 false /* authDetailsRequired */,
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003724 true /* updateLastCredentialTime */,
3725 isPasswordForwardingAllowed) {
Sandra Kwane68c37e2015-11-12 17:11:49 -08003726 @Override
3727 public void run() throws RemoteException {
3728 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3729 loginOptions);
3730 }
3731
3732 @Override
3733 protected String toDebugString(long now) {
3734 if (loginOptions != null)
3735 loginOptions.keySet();
3736 return super.toDebugString(now)
3737 + ", startUpdateCredentialsSession"
3738 + ", " + account
3739 + ", authTokenType " + authTokenType
3740 + ", loginOptions " + loginOptions;
3741 }
3742 }.bind();
3743 } finally {
3744 restoreCallingIdentity(identityToken);
3745 }
3746 }
3747
3748 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08003749 public void isCredentialsUpdateSuggested(
3750 IAccountManagerResponse response,
3751 final Account account,
3752 final String statusToken) {
3753 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3754 Log.v(TAG,
3755 "isCredentialsUpdateSuggested: " + account + ", response " + response
3756 + ", caller's uid " + Binder.getCallingUid()
3757 + ", pid " + Binder.getCallingPid());
3758 }
3759 if (response == null) {
3760 throw new IllegalArgumentException("response is null");
3761 }
3762 if (account == null) {
3763 throw new IllegalArgumentException("account is null");
3764 }
3765 if (TextUtils.isEmpty(statusToken)) {
3766 throw new IllegalArgumentException("status token is empty");
3767 }
3768
Sandra Kwan390c9d22016-01-12 14:13:37 -08003769 int usrId = UserHandle.getCallingUserId();
3770 long identityToken = clearCallingIdentity();
3771 try {
3772 UserAccounts accounts = getUserAccounts(usrId);
3773 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3774 false /* stripAuthTokenFromResult */, account.name,
3775 false /* authDetailsRequired */) {
3776 @Override
3777 protected String toDebugString(long now) {
3778 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3779 + ", " + account;
3780 }
3781
3782 @Override
3783 public void run() throws RemoteException {
3784 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3785 }
3786
3787 @Override
3788 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003789 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08003790 IAccountManagerResponse response = getResponseAndClose();
3791 if (response == null) {
3792 return;
3793 }
3794
3795 if (result == null) {
3796 sendErrorResponse(
3797 response,
3798 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3799 "null bundle");
3800 return;
3801 }
3802
3803 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3804 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3805 + response);
3806 }
3807 // Check to see if an error occurred. We know if an error occurred because all
3808 // error codes are greater than 0.
3809 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3810 sendErrorResponse(response,
3811 result.getInt(AccountManager.KEY_ERROR_CODE),
3812 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3813 return;
3814 }
3815 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3816 sendErrorResponse(
3817 response,
3818 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3819 "no result in response");
3820 return;
3821 }
3822 final Bundle newResult = new Bundle();
3823 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3824 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3825 sendResponse(response, newResult);
3826 }
3827 }.bind();
3828 } finally {
3829 restoreCallingIdentity(identityToken);
3830 }
3831 }
3832
3833 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003834 public void editProperties(IAccountManagerResponse response, final String accountType,
3835 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003836 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003837 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3838 Log.v(TAG, "editProperties: accountType " + accountType
3839 + ", response " + response
3840 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003841 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003842 + ", pid " + Binder.getCallingPid());
3843 }
Fred Quintana382601f2010-03-25 12:25:10 -07003844 if (response == null) throw new IllegalArgumentException("response is null");
3845 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003846 int userId = UserHandle.getCallingUserId();
3847 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003848 String msg = String.format(
3849 "uid %s cannot edit authenticator properites for account type: %s",
3850 callingUid,
3851 accountType);
3852 throw new SecurityException(msg);
3853 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003854 long identityToken = clearCallingIdentity();
3855 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003856 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003857 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003858 true /* stripAuthTokenFromResult */, null /* accountName */,
3859 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003860 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003861 public void run() throws RemoteException {
3862 mAuthenticator.editProperties(this, mAccountType);
3863 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003864 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003865 protected String toDebugString(long now) {
3866 return super.toDebugString(now) + ", editProperties"
3867 + ", accountType " + accountType;
3868 }
3869 }.bind();
3870 } finally {
3871 restoreCallingIdentity(identityToken);
3872 }
Fred Quintana60307342009-03-24 22:48:12 -07003873 }
3874
Amith Yamasani12747872015-12-07 14:19:49 -08003875 @Override
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003876 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
3877 @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003878 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003879 throw new SecurityException("Can be called only by system UID");
3880 }
3881 Preconditions.checkNotNull(account, "account cannot be null");
3882 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3883 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3884
3885 final int userId = userHandle.getIdentifier();
3886
3887 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3888
3889 try {
3890
3891 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3892 // Use null token which means any token. Having a token means the package
3893 // is trusted by the authenticator, hence it is fine to access the account.
3894 if (permissionIsGranted(account, null, uid, userId)) {
3895 return true;
3896 }
3897 // In addition to the permissions required to get an auth token we also allow
3898 // the account to be accessed by holders of the get accounts permissions.
3899 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3900 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3901 } catch (NameNotFoundException e) {
3902 return false;
3903 }
3904 }
3905
3906 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3907 final long identity = Binder.clearCallingIdentity();
3908 try {
3909 IPackageManager pm = ActivityThread.getPackageManager();
3910 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3911 return false;
3912 }
3913 final int opCode = AppOpsManager.permissionToOpCode(permission);
3914 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3915 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3916 } catch (RemoteException e) {
3917 /* ignore - local call */
3918 } finally {
3919 Binder.restoreCallingIdentity(identity);
3920 }
3921 return false;
3922 }
3923
3924 @Override
3925 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3926 @NonNull String packageName, @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003927 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003928 throw new SecurityException("Can be called only by system UID");
3929 }
3930
3931 Preconditions.checkNotNull(account, "account cannot be null");
3932 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3933 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3934
3935 final int userId = userHandle.getIdentifier();
3936
3937 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3938
3939 final int uid;
3940 try {
3941 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3942 } catch (NameNotFoundException e) {
3943 Slog.e(TAG, "Unknown package " + packageName);
3944 return null;
3945 }
3946
3947 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3948
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003949 final long identity = Binder.clearCallingIdentity();
3950 try {
3951 return PendingIntent.getActivityAsUser(
3952 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3953 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3954 null, new UserHandle(userId)).getIntentSender();
3955 } finally {
3956 Binder.restoreCallingIdentity(identity);
3957 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003958 }
3959
3960 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3961 int uid, RemoteCallback callback) {
3962 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3963 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3964 @Override
3965 public void onResult(Bundle value) throws RemoteException {
3966 handleAuthenticatorResponse(true);
3967 }
3968
3969 @Override
3970 public void onRequestContinued() {
3971 /* ignore */
3972 }
3973
3974 @Override
3975 public void onError(int errorCode, String errorMessage) throws RemoteException {
3976 handleAuthenticatorResponse(false);
3977 }
3978
3979 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3980 cancelNotification(getCredentialPermissionNotificationId(account,
3981 AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
3982 UserHandle.getUserHandleForUid(uid));
3983 if (callback != null) {
3984 Bundle result = new Bundle();
3985 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3986 callback.sendResult(result);
3987 }
3988 }
3989 }), AccountManager.ACCOUNT_ACCESS_TOKEN, false);
3990 }
3991
3992 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08003993 public boolean someUserHasAccount(@NonNull final Account account) {
3994 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3995 throw new SecurityException("Only system can check for accounts across users");
3996 }
3997 final long token = Binder.clearCallingIdentity();
3998 try {
3999 AccountAndUser[] allAccounts = getAllAccounts();
4000 for (int i = allAccounts.length - 1; i >= 0; i--) {
4001 if (allAccounts[i].account.equals(account)) {
4002 return true;
4003 }
4004 }
4005 return false;
4006 } finally {
4007 Binder.restoreCallingIdentity(token);
4008 }
4009 }
4010
Fred Quintana33269202009-04-20 16:05:10 -07004011 private class GetAccountsByTypeAndFeatureSession extends Session {
4012 private final String[] mFeatures;
4013 private volatile Account[] mAccountsOfType = null;
4014 private volatile ArrayList<Account> mAccountsWithFeatures = null;
4015 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004016 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004017
Amith Yamasani04e0d262012-02-14 11:50:53 -08004018 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004019 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004020 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004021 true /* stripAuthTokenFromResult */, null /* accountName */,
4022 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004023 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004024 mFeatures = features;
4025 }
4026
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004027 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004028 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004029 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004030 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
4031 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004032 }
Fred Quintana33269202009-04-20 16:05:10 -07004033 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07004034 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07004035 mCurrentAccount = 0;
4036
4037 checkAccount();
4038 }
4039
4040 public void checkAccount() {
4041 if (mCurrentAccount >= mAccountsOfType.length) {
4042 sendResult();
4043 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07004044 }
Fred Quintana33269202009-04-20 16:05:10 -07004045
Fred Quintana29e94b82010-03-10 12:11:51 -08004046 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
4047 if (accountAuthenticator == null) {
4048 // It is possible that the authenticator has died, which is indicated by
4049 // mAuthenticator being set to null. If this happens then just abort.
4050 // There is no need to send back a result or error in this case since
4051 // that already happened when mAuthenticator was cleared.
4052 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4053 Log.v(TAG, "checkAccount: aborting session since we are no longer"
4054 + " connected to the authenticator, " + toDebugString());
4055 }
4056 return;
4057 }
Fred Quintana33269202009-04-20 16:05:10 -07004058 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08004059 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07004060 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004061 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07004062 }
4063 }
4064
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004065 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004066 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004067 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07004068 mNumResults++;
4069 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004070 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07004071 return;
4072 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004073 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07004074 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
4075 }
4076 mCurrentAccount++;
4077 checkAccount();
4078 }
4079
4080 public void sendResult() {
4081 IAccountManagerResponse response = getResponseAndClose();
4082 if (response != null) {
4083 try {
4084 Account[] accounts = new Account[mAccountsWithFeatures.size()];
4085 for (int i = 0; i < accounts.length; i++) {
4086 accounts[i] = mAccountsWithFeatures.get(i);
4087 }
Fred Quintana56285a62010-12-02 14:20:51 -08004088 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4089 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
4090 + response);
4091 }
Fred Quintana33269202009-04-20 16:05:10 -07004092 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004093 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07004094 response.onResult(result);
4095 } catch (RemoteException e) {
4096 // if the caller is dead then there is no one to care about remote exceptions
4097 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4098 Log.v(TAG, "failure while notifying response", e);
4099 }
4100 }
4101 }
4102 }
4103
4104
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004105 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004106 protected String toDebugString(long now) {
4107 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
4108 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
4109 }
4110 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004111
Amith Yamasani04e0d262012-02-14 11:50:53 -08004112 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004113 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08004114 * @hide
4115 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004116 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004117 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004118 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07004119 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4120 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004121 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004122 return new Account[0];
4123 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004124 long identityToken = clearCallingIdentity();
4125 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004126 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004127 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004128 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004129 callingUid,
4130 null, // packageName
4131 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004132 } finally {
4133 restoreCallingIdentity(identityToken);
4134 }
4135 }
4136
Amith Yamasanif29f2362012-04-05 18:29:52 -07004137 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004138 * Returns accounts for all running users.
4139 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07004140 * @hide
4141 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004142 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004143 public AccountAndUser[] getRunningAccounts() {
4144 final int[] runningUserIds;
4145 try {
4146 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
4147 } catch (RemoteException e) {
4148 // Running in system_server; should never happen
4149 throw new RuntimeException(e);
4150 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004151 return getAccounts(runningUserIds);
4152 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004153
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004154 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004155 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004156 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004157 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004158 final int[] userIds = new int[users.size()];
4159 for (int i = 0; i < userIds.length; i++) {
4160 userIds[i] = users.get(i).id;
4161 }
4162 return getAccounts(userIds);
4163 }
4164
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004165 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004166 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004167 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004168 for (int userId : userIds) {
4169 UserAccounts userAccounts = getUserAccounts(userId);
4170 if (userAccounts == null) continue;
4171 synchronized (userAccounts.cacheLock) {
4172 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4173 Binder.getCallingUid(), null);
4174 for (int a = 0; a < accounts.length; a++) {
4175 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004176 }
4177 }
4178 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004179
4180 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4181 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004182 }
4183
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004184 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004185 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004186 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4187 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004188 }
4189
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004190 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004191 private Account[] getAccountsAsUser(
4192 String type,
4193 int userId,
4194 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004195 int packageUid,
4196 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004197 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004198 // Only allow the system process to read accounts of other users
4199 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004200 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004201 && mContext.checkCallingOrSelfPermission(
4202 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4203 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004204 throw new SecurityException("User " + UserHandle.getCallingUserId()
4205 + " trying to get account for " + userId);
4206 }
4207
Fred Quintana56285a62010-12-02 14:20:51 -08004208 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4209 Log.v(TAG, "getAccounts: accountType " + type
4210 + ", caller's uid " + Binder.getCallingUid()
4211 + ", pid " + Binder.getCallingPid());
4212 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004213 // If the original calling app was using the framework account chooser activity, we'll
4214 // be passed in the original caller's uid here, which is what should be used for filtering.
4215 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4216 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004217 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004218 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004219 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4220 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004221 if (visibleAccountTypes.isEmpty()
4222 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004223 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004224 } else if (visibleAccountTypes.contains(type)) {
4225 // Prune the list down to just the requested type.
4226 visibleAccountTypes = new ArrayList<>();
4227 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004228 } // else aggregate all the visible accounts (it won't matter if the
4229 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004230
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004231 long identityToken = clearCallingIdentity();
4232 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004233 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004234 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004235 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004236 callingUid,
4237 callingPackage,
4238 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004239 ArrayList<Account> accountsToReturnList = new
4240 ArrayList<Account>(Arrays.asList(accountsToReturn));
4241 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4242 // if account not visible to caller or managed by caller, remove from
4243 // accounts to return. Note that all accounts visible by default unless
4244 // visible list functionality implemented
4245 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4246 getUserAccounts(userId)))) {
4247 accountsToReturnList.remove(i);
4248 }
4249 }
4250 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004251 } finally {
4252 restoreCallingIdentity(identityToken);
4253 }
4254 }
4255
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004256 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004257 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004258 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004259 int callingUid,
4260 String callingPackage,
4261 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004262 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004263 ArrayList<Account> visibleAccounts = new ArrayList<>();
4264 for (String visibleType : visibleAccountTypes) {
4265 Account[] accountsForType = getAccountsFromCacheLocked(
4266 userAccounts, visibleType, callingUid, callingPackage);
4267 if (accountsForType != null) {
4268 visibleAccounts.addAll(Arrays.asList(accountsForType));
4269 }
4270 }
4271 Account[] result = new Account[visibleAccounts.size()];
4272 for (int i = 0; i < visibleAccounts.size(); i++) {
4273 result[i] = visibleAccounts.get(i);
4274 }
4275 return result;
4276 }
4277 }
4278
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004279 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004280 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004281 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004282 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4283 for (Account account : accounts) {
4284 addSharedAccountAsUser(account, userId);
4285 }
4286 }
4287
4288 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004289 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004290 UserAccounts accounts = getUserAccounts(userId);
4291 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004292 AccountsDbUtils.deleteSharedAccount(db, account);
4293 long accountId = AccountsDbUtils.insertSharedAccount(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004294 if (accountId < 0) {
4295 Log.w(TAG, "insertAccountIntoDatabase: " + account
4296 + ", skipping the DB insert failed");
4297 return false;
4298 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004299 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004300 return true;
4301 }
4302
4303 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004304 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4305 userId = handleIncomingUser(userId);
4306 UserAccounts accounts = getUserAccounts(userId);
4307 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004308 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4309 int r = AccountsDbUtils.renameSharedAccount(db, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004310 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004311 int callingUid = getCallingUid();
4312 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
4313 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004314 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004315 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004316 }
4317 return r > 0;
4318 }
4319
4320 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004321 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004322 return removeSharedAccountAsUser(account, userId, getCallingUid());
4323 }
4324
4325 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004326 userId = handleIncomingUser(userId);
4327 UserAccounts accounts = getUserAccounts(userId);
4328 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004329 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4330 boolean deleted = AccountsDbUtils.deleteSharedAccount(db, account);
4331 if (deleted) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004332 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
4333 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004334 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004335 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004336 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004337 }
4338
4339 @Override
4340 public Account[] getSharedAccountsAsUser(int userId) {
4341 userId = handleIncomingUser(userId);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004342 SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
4343 List<Account> accountList = AccountsDbUtils.getSharedAccounts(db);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004344 Account[] accountArray = new Account[accountList.size()];
4345 accountList.toArray(accountArray);
4346 return accountArray;
4347 }
4348
4349 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004350 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004351 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004352 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004353 }
4354
Amith Yamasani27db4682013-03-30 17:07:47 -07004355 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004356 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004357 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004358 int callingUid = Binder.getCallingUid();
4359 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4360 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4361 + callingUid + " with uid=" + uid);
4362 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004363 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4364 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004365 }
4366
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004367 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004368 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004369 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4370 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004371 int packageUid = -1;
4372 try {
4373 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004374 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4375 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004376 } catch (RemoteException re) {
4377 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4378 return new Account[0];
4379 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004380 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4381 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004382 }
4383
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004384 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004385 public void getAccountsByFeatures(
4386 IAccountManagerResponse response,
4387 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004388 String[] features,
4389 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004390 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004391 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4392 Log.v(TAG, "getAccounts: accountType " + type
4393 + ", response " + response
4394 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004395 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004396 + ", pid " + Binder.getCallingPid());
4397 }
Fred Quintana382601f2010-03-25 12:25:10 -07004398 if (response == null) throw new IllegalArgumentException("response is null");
4399 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004400 int userId = UserHandle.getCallingUserId();
4401
Svetoslavf3f02ac2015-09-08 14:36:35 -07004402 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4403 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004404 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004405 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004406 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004407 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4408 try {
4409 response.onResult(result);
4410 } catch (RemoteException e) {
4411 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4412 }
4413 return;
4414 }
Fred Quintana33269202009-04-20 16:05:10 -07004415 long identityToken = clearCallingIdentity();
4416 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004417 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004418 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004419 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004420 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004421 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004422 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004423 Bundle result = new Bundle();
4424 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4425 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004426 return;
4427 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004428 new GetAccountsByTypeAndFeatureSession(
4429 userAccounts,
4430 response,
4431 type,
4432 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004433 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004434 } finally {
4435 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004436 }
4437 }
4438
Fred Quintanaa698f422009-04-08 19:14:54 -07004439 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004440 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004441 IAccountManagerResponse mResponse;
4442 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004443 final boolean mExpectActivityLaunch;
4444 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004445 final String mAccountName;
4446 // Indicates if we need to add auth details(like last credential time)
4447 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004448 // If set, we need to update the last authenticated time. This is
4449 // currently
4450 // used on
4451 // successful confirming credentials.
4452 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004453
Fred Quintana33269202009-04-20 16:05:10 -07004454 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004455 private int mNumRequestContinued = 0;
4456 private int mNumErrors = 0;
4457
Fred Quintana60307342009-03-24 22:48:12 -07004458 IAccountAuthenticator mAuthenticator = null;
4459
Fred Quintana8570f742010-02-18 10:32:54 -08004460 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004461 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004462
Amith Yamasani04e0d262012-02-14 11:50:53 -08004463 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004464 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4465 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004466 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4467 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4468 }
4469
4470 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4471 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4472 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004473 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004474 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004475 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004476 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004477 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004478 mResponse = response;
4479 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004480 mExpectActivityLaunch = expectActivityLaunch;
4481 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004482 mAccountName = accountName;
4483 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004484 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004485
Fred Quintanaa698f422009-04-08 19:14:54 -07004486 synchronized (mSessions) {
4487 mSessions.put(toString(), this);
4488 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004489 if (response != null) {
4490 try {
4491 response.asBinder().linkToDeath(this, 0 /* flags */);
4492 } catch (RemoteException e) {
4493 mResponse = null;
4494 binderDied();
4495 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004496 }
Fred Quintana60307342009-03-24 22:48:12 -07004497 }
4498
Fred Quintanaa698f422009-04-08 19:14:54 -07004499 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004500 if (mResponse == null) {
4501 // this session has already been closed
4502 return null;
4503 }
Fred Quintana60307342009-03-24 22:48:12 -07004504 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004505 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004506 return response;
4507 }
4508
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004509 /**
4510 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4511 * security policy.
4512 *
4513 * In particular we want to make sure that the Authenticator doesn't try to trick users
4514 * into launching aribtrary intents on the device via by tricking to click authenticator
4515 * supplied entries in the system Settings app.
4516 */
4517 protected void checkKeyIntent(
4518 int authUid,
4519 Intent intent) throws SecurityException {
4520 long bid = Binder.clearCallingIdentity();
4521 try {
4522 PackageManager pm = mContext.getPackageManager();
4523 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4524 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4525 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004526 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4527 targetActivityInfo.getClass().getName())
4528 && !CantAddAccountActivity.class
4529 .equals(targetActivityInfo.getClass().getName())
4530 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4531 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004532 String pkgName = targetActivityInfo.packageName;
4533 String activityName = targetActivityInfo.name;
4534 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4535 + "does not share a signature with the supplying authenticator (%s).";
4536 throw new SecurityException(
4537 String.format(tmpl, activityName, pkgName, mAccountType));
4538 }
4539 } finally {
4540 Binder.restoreCallingIdentity(bid);
4541 }
4542 }
4543
Fred Quintanaa698f422009-04-08 19:14:54 -07004544 private void close() {
4545 synchronized (mSessions) {
4546 if (mSessions.remove(toString()) == null) {
4547 // the session was already closed, so bail out now
4548 return;
4549 }
4550 }
4551 if (mResponse != null) {
4552 // stop listening for response deaths
4553 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4554
4555 // clear this so that we don't accidentally send any further results
4556 mResponse = null;
4557 }
4558 cancelTimeout();
4559 unbind();
4560 }
4561
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004562 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004563 public void binderDied() {
4564 mResponse = null;
4565 close();
4566 }
4567
4568 protected String toDebugString() {
4569 return toDebugString(SystemClock.elapsedRealtime());
4570 }
4571
4572 protected String toDebugString(long now) {
4573 return "Session: expectLaunch " + mExpectActivityLaunch
4574 + ", connected " + (mAuthenticator != null)
4575 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4576 + "/" + mNumErrors + ")"
4577 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4578 }
4579
Fred Quintana60307342009-03-24 22:48:12 -07004580 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004581 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4582 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4583 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004584 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004585 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004586 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004587 }
4588 }
4589
4590 private void unbind() {
4591 if (mAuthenticator != null) {
4592 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004593 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004594 }
4595 }
4596
Fred Quintana60307342009-03-24 22:48:12 -07004597 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004598 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004599 }
4600
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004601 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004602 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004603 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004604 try {
4605 run();
4606 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004607 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004608 "remote exception");
4609 }
Fred Quintana60307342009-03-24 22:48:12 -07004610 }
4611
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004612 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004613 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004614 mAuthenticator = null;
4615 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004616 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004617 try {
4618 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4619 "disconnected");
4620 } catch (RemoteException e) {
4621 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4622 Log.v(TAG, "Session.onServiceDisconnected: "
4623 + "caught RemoteException while responding", e);
4624 }
4625 }
Fred Quintana60307342009-03-24 22:48:12 -07004626 }
4627 }
4628
Fred Quintanab839afc2009-10-14 15:57:28 -07004629 public abstract void run() throws RemoteException;
4630
Fred Quintana60307342009-03-24 22:48:12 -07004631 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004632 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004633 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004634 try {
4635 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4636 "timeout");
4637 } catch (RemoteException e) {
4638 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4639 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4640 e);
4641 }
4642 }
Fred Quintana60307342009-03-24 22:48:12 -07004643 }
4644 }
4645
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004646 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004647 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004648 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004649 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004650 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004651 if (result != null) {
4652 boolean isSuccessfulConfirmCreds = result.getBoolean(
4653 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004654 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004655 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4656 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004657 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004658 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4659 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004660 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004661 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004662 if (needUpdate || mAuthDetailsRequired) {
4663 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4664 if (needUpdate && accountPresent) {
4665 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4666 }
4667 if (mAuthDetailsRequired) {
4668 long lastAuthenticatedTime = -1;
4669 if (accountPresent) {
4670 lastAuthenticatedTime = DatabaseUtils.longForQuery(
4671 mAccounts.openHelper.getReadableDatabase(),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004672 "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4673 + " FROM " +
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004674 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4675 + ACCOUNTS_TYPE + "=?",
4676 new String[] {
4677 mAccountName, mAccountType
4678 });
4679 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004680 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004681 lastAuthenticatedTime);
4682 }
4683 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004684 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004685 if (result != null
4686 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004687 checkKeyIntent(
4688 Binder.getCallingUid(),
4689 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004690 }
4691 if (result != null
4692 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004693 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4694 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004695 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4696 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004697 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4698 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004699 }
Fred Quintana60307342009-03-24 22:48:12 -07004700 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004701 IAccountManagerResponse response;
4702 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004703 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004704 response = mResponse;
4705 } else {
4706 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004707 }
Fred Quintana60307342009-03-24 22:48:12 -07004708 if (response != null) {
4709 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004710 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004711 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4712 Log.v(TAG, getClass().getSimpleName()
4713 + " calling onError() on response " + response);
4714 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004715 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004716 "null bundle returned");
4717 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004718 if (mStripAuthTokenFromResult) {
4719 result.remove(AccountManager.KEY_AUTHTOKEN);
4720 }
Fred Quintana56285a62010-12-02 14:20:51 -08004721 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4722 Log.v(TAG, getClass().getSimpleName()
4723 + " calling onResult() on response " + response);
4724 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004725 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4726 (intent == null)) {
4727 // All AccountManager error codes are greater than 0
4728 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4729 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4730 } else {
4731 response.onResult(result);
4732 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004733 }
Fred Quintana60307342009-03-24 22:48:12 -07004734 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004735 // if the caller is dead then there is no one to care about remote exceptions
4736 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4737 Log.v(TAG, "failure while notifying response", e);
4738 }
Fred Quintana60307342009-03-24 22:48:12 -07004739 }
4740 }
4741 }
Fred Quintana60307342009-03-24 22:48:12 -07004742
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004743 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004744 public void onRequestContinued() {
4745 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004746 }
4747
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004748 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004749 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004750 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004751 IAccountManagerResponse response = getResponseAndClose();
4752 if (response != null) {
4753 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004754 Log.v(TAG, getClass().getSimpleName()
4755 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004756 }
4757 try {
4758 response.onError(errorCode, errorMessage);
4759 } catch (RemoteException e) {
4760 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4761 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4762 }
4763 }
4764 } else {
4765 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4766 Log.v(TAG, "Session.onError: already closed");
4767 }
Fred Quintana60307342009-03-24 22:48:12 -07004768 }
4769 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004770
4771 /**
4772 * find the component name for the authenticator and initiate a bind
4773 * if no authenticator or the bind fails then return false, otherwise return true
4774 */
4775 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004776 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4777 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4778 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004779 if (authenticatorInfo == null) {
4780 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4781 Log.v(TAG, "there is no authenticator for " + authenticatorType
4782 + ", bailing out");
4783 }
4784 return false;
4785 }
4786
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004787 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004788 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004789 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4790 + " which isn't encryption aware");
4791 return false;
4792 }
4793
Fred Quintanab839afc2009-10-14 15:57:28 -07004794 Intent intent = new Intent();
4795 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4796 intent.setComponent(authenticatorInfo.componentName);
4797 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4798 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4799 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004800 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004801 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004802 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4803 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4804 }
4805 return false;
4806 }
4807
Fred Quintanab839afc2009-10-14 15:57:28 -07004808 return true;
4809 }
Fred Quintana60307342009-03-24 22:48:12 -07004810 }
4811
4812 private class MessageHandler extends Handler {
4813 MessageHandler(Looper looper) {
4814 super(looper);
4815 }
Costin Manolache3348f142009-09-29 18:58:36 -07004816
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004817 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004818 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004819 switch (msg.what) {
4820 case MESSAGE_TIMED_OUT:
4821 Session session = (Session)msg.obj;
4822 session.onTimedOut();
4823 break;
4824
Amith Yamasani5be347b2013-03-31 17:44:31 -07004825 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004826 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004827 break;
4828
Fred Quintana60307342009-03-24 22:48:12 -07004829 default:
4830 throw new IllegalStateException("unhandled message: " + msg.what);
4831 }
4832 }
4833 }
4834
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004835 @VisibleForTesting
4836 String getPreNDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07004837 File systemDir = Environment.getDataSystemDirectory();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004838 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4839 PRE_N_DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004840 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07004841 // Migrate old file, if it exists, to the new location.
4842 // Make sure the new file doesn't already exist. A dummy file could have been
4843 // accidentally created in the old location, causing the new one to become corrupted
4844 // as well.
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004845 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07004846 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07004847 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07004848 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07004849 if (!userDir.exists()) {
4850 if (!userDir.mkdirs()) {
4851 throw new IllegalStateException("User dir cannot be created: " + userDir);
4852 }
4853 }
4854 if (!oldFile.renameTo(databaseFile)) {
4855 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4856 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004857 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08004858 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004859 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08004860 }
4861
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004862 @VisibleForTesting
4863 String getDeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004864 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4865 DE_DATABASE_NAME);
4866 return databaseFile.getPath();
4867 }
4868
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004869 @VisibleForTesting
4870 String getCeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004871 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4872 CE_DATABASE_NAME);
4873 return databaseFile.getPath();
4874 }
4875
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004876 private static class DebugDbHelper{
4877 private DebugDbHelper() {
4878 }
4879
4880 private static String TABLE_DEBUG = "debug_table";
4881
4882 // Columns for the table
4883 private static String ACTION_TYPE = "action_type";
4884 private static String TIMESTAMP = "time";
4885 private static String CALLER_UID = "caller_uid";
4886 private static String TABLE_NAME = "table_name";
4887 private static String KEY = "primary_key";
4888
4889 // These actions correspond to the occurrence of real actions. Since
4890 // these are called by the authenticators, the uid associated will be
4891 // of the authenticator.
4892 private static String ACTION_SET_PASSWORD = "action_set_password";
4893 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4894 private static String ACTION_ACCOUNT_ADD = "action_account_add";
4895 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004896 private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004897 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4898 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4899
4900 // These actions don't necessarily correspond to any action on
4901 // accountDb taking place. As an example, there might be a request for
4902 // addingAccount, which might not lead to addition of account on grounds
4903 // of bad authentication. We will still be logging it to keep track of
4904 // who called.
4905 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4906 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004907 private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004908
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004909 //This action doesn't add account to accountdb. Account is only
4910 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08004911 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004912 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4913 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08004914
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004915 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4916
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004917 private static void createDebugTable(SQLiteDatabase db) {
4918 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4919 + ACCOUNTS_ID + " INTEGER,"
4920 + ACTION_TYPE + " TEXT NOT NULL, "
4921 + TIMESTAMP + " DATETIME,"
4922 + CALLER_UID + " INTEGER NOT NULL,"
4923 + TABLE_NAME + " TEXT NOT NULL,"
4924 + KEY + " INTEGER PRIMARY KEY)");
4925 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4926 }
4927 }
4928
4929 private void logRecord(UserAccounts accounts, String action, String tableName) {
4930 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4931 logRecord(db, action, tableName, -1, accounts);
4932 }
4933
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004934 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4935 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4936 logRecord(db, action, tableName, -1, accounts, uid);
4937 }
4938
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004939 /*
4940 * This function receives an opened writable database.
4941 */
4942 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4943 UserAccounts userAccount) {
4944 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4945 }
4946
4947 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004948 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004949 */
4950 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4951 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004952
4953 class LogRecordTask implements Runnable {
4954 private final String action;
4955 private final String tableName;
4956 private final long accountId;
4957 private final UserAccounts userAccount;
4958 private final int callingUid;
4959 private final long userDebugDbInsertionPoint;
4960
4961 LogRecordTask(final String action,
4962 final String tableName,
4963 final long accountId,
4964 final UserAccounts userAccount,
4965 final int callingUid,
4966 final long userDebugDbInsertionPoint) {
4967 this.action = action;
4968 this.tableName = tableName;
4969 this.accountId = accountId;
4970 this.userAccount = userAccount;
4971 this.callingUid = callingUid;
4972 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4973 }
4974
4975 public void run() {
4976 SQLiteStatement logStatement = userAccount.statementForLogging;
4977 logStatement.bindLong(1, accountId);
4978 logStatement.bindString(2, action);
4979 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
4980 logStatement.bindLong(4, callingUid);
4981 logStatement.bindString(5, tableName);
4982 logStatement.bindLong(6, userDebugDbInsertionPoint);
4983 logStatement.execute();
4984 logStatement.clearBindings();
4985 }
4986 }
4987
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004988 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
4989 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004990 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
4991 % MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004992 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004993 }
4994
4995 /*
4996 * This should only be called once to compile the sql statement for logging
4997 * and to find the insertion point.
4998 */
4999 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
5000 UserAccounts userAccount) {
5001 // Initialize the count if not done earlier.
5002 int size = (int) getDebugTableRowCount(db);
5003 if (size >= MAX_DEBUG_DB_SIZE) {
5004 // Table is full, and we need to find the point where to insert.
5005 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
5006 } else {
5007 userAccount.debugDbInsertionPoint = size;
5008 }
5009 compileSqlStatementForLogging(db, userAccount);
5010 }
5011
5012 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
5013 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
5014 + " VALUES (?,?,?,?,?,?)";
5015 userAccount.statementForLogging = db.compileStatement(sql);
5016 }
5017
5018 private long getDebugTableRowCount(SQLiteDatabase db) {
5019 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
5020 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5021 }
5022
5023 /*
5024 * Finds the row key where the next insertion should take place. This should
5025 * be invoked only if the table has reached its full capacity.
5026 */
5027 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
5028 // This query finds the smallest timestamp value (and if 2 records have
5029 // same timestamp, the choose the lower id).
5030 String queryCountDebugDbRows = new StringBuilder()
5031 .append("SELECT ").append(DebugDbHelper.KEY)
5032 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
5033 .append(" ORDER BY ")
5034 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
5035 .append(" LIMIT 1")
5036 .toString();
5037 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5038 }
5039
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005040 static class PreNDatabaseHelper extends SQLiteOpenHelper {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005041 private final Context mContext;
5042 private final int mUserId;
5043
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005044 public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
5045 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005046 mContext = context;
5047 mUserId = userId;
Fred Quintana60307342009-03-24 22:48:12 -07005048 }
5049
5050 @Override
5051 public void onCreate(SQLiteDatabase db) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005052 // We use PreNDatabaseHelper only if pre-N db exists
5053 throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005054 }
5055
Amith Yamasani67df64b2012-12-14 12:09:36 -08005056 private void createSharedAccountsTable(SQLiteDatabase db) {
5057 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5058 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5059 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5060 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5061 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5062 }
5063
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005064 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
5065 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
5066 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
5067 }
5068
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005069 private void addOldAccountNameColumn(SQLiteDatabase db) {
5070 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
5071 }
5072
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005073 private void addDebugTable(SQLiteDatabase db) {
5074 DebugDbHelper.createDebugTable(db);
5075 }
5076
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005077 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005078 db.execSQL(""
5079 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5080 + " BEGIN"
5081 + " DELETE FROM " + TABLE_AUTHTOKENS
5082 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5083 + " DELETE FROM " + TABLE_EXTRAS
5084 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005085 + " DELETE FROM " + TABLE_GRANTS
5086 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07005087 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07005088 }
5089
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005090 private void createGrantsTable(SQLiteDatabase db) {
5091 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5092 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5093 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5094 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5095 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5096 + "," + GRANTS_GRANTEE_UID + "))");
5097 }
5098
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005099 private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db,
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005100 Map<String, Integer> authTypeAndUIDMap) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005101 for (Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) {
5102 AccountsDbUtils.insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005103 }
5104 }
5105
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005106 /**
5107 * Pre-N database may need an upgrade before splitting
5108 */
Fred Quintana60307342009-03-24 22:48:12 -07005109 @Override
5110 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005111 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07005112
Fred Quintanaa698f422009-04-08 19:14:54 -07005113 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005114 // no longer need to do anything since the work is done
5115 // when upgrading from version 2
5116 oldVersion++;
5117 }
5118
5119 if (oldVersion == 2) {
5120 createGrantsTable(db);
5121 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
5122 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07005123 oldVersion++;
5124 }
Costin Manolache3348f142009-09-29 18:58:36 -07005125
5126 if (oldVersion == 3) {
5127 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
5128 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
5129 oldVersion++;
5130 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08005131
5132 if (oldVersion == 4) {
5133 createSharedAccountsTable(db);
5134 oldVersion++;
5135 }
5136
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005137 if (oldVersion == 5) {
5138 addOldAccountNameColumn(db);
5139 oldVersion++;
5140 }
5141
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005142 if (oldVersion == 6) {
5143 addLastSuccessfullAuthenticatedTimeColumn(db);
5144 oldVersion++;
5145 }
5146
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005147 if (oldVersion == 7) {
5148 addDebugTable(db);
5149 oldVersion++;
5150 }
5151
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005152 if (oldVersion == 8) {
5153 populateMetaTableWithAuthTypeAndUID(
5154 db,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005155 getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005156 oldVersion++;
5157 }
5158
Amith Yamasani67df64b2012-12-14 12:09:36 -08005159 if (oldVersion != newVersion) {
5160 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5161 }
Fred Quintana60307342009-03-24 22:48:12 -07005162 }
5163
5164 @Override
5165 public void onOpen(SQLiteDatabase db) {
5166 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
5167 }
5168 }
5169
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005170 static class DeDatabaseHelper extends SQLiteOpenHelper {
5171
5172 private final int mUserId;
5173 private volatile boolean mCeAttached;
5174
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005175 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
5176 super(context, deDatabaseName, null, DE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005177 mUserId = userId;
5178 }
5179
5180 /**
5181 * This call needs to be made while the mCacheLock is held. The way to
5182 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
5183 * @param db The database.
5184 */
5185 @Override
5186 public void onCreate(SQLiteDatabase db) {
5187 Log.i(TAG, "Creating DE database for user " + mUserId);
5188 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5189 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
5190 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5191 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5192 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
5193 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
5194 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5195
5196 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
5197 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
5198 + META_VALUE + " TEXT)");
5199
5200 createGrantsTable(db);
5201 createSharedAccountsTable(db);
5202 createAccountsDeletionTrigger(db);
5203 DebugDbHelper.createDebugTable(db);
5204 }
5205
5206 private void createSharedAccountsTable(SQLiteDatabase db) {
5207 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5208 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5209 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5210 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5211 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5212 }
5213
5214 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5215 db.execSQL(""
5216 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5217 + " BEGIN"
5218 + " DELETE FROM " + TABLE_GRANTS
5219 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5220 + " END");
5221 }
5222
5223 private void createGrantsTable(SQLiteDatabase db) {
5224 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5225 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5226 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5227 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5228 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5229 + "," + GRANTS_GRANTEE_UID + "))");
5230 }
5231
5232 @Override
5233 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5234 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
5235
5236 if (oldVersion != newVersion) {
5237 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5238 }
5239 }
5240
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005241 public void attachCeDatabase(File ceDbFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005242 SQLiteDatabase db = getWritableDatabase();
5243 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
5244 mCeAttached = true;
5245 }
5246
5247 public boolean isCeDatabaseAttached() {
5248 return mCeAttached;
5249 }
5250
5251
5252 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
5253 if(!mCeAttached) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005254 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
5255 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005256 }
5257 return super.getReadableDatabase();
5258 }
5259
5260 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
5261 if(!mCeAttached) {
5262 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005263 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005264 }
5265 return super.getWritableDatabase();
5266 }
5267
5268 @Override
5269 public void onOpen(SQLiteDatabase db) {
5270 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
5271 }
5272
5273 private void migratePreNDbToDe(File preNDbFile) {
5274 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
5275 SQLiteDatabase db = getWritableDatabase();
5276 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb");
5277 db.beginTransaction();
5278 // Copy accounts fields
5279 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
5280 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5281 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5282 + ") "
5283 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5284 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5285 + " FROM preNDb." + TABLE_ACCOUNTS);
5286 // Copy SHARED_ACCOUNTS
5287 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
5288 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
5289 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
5290 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
5291 // Copy DEBUG_TABLE
5292 db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
5293 + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5294 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5295 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
5296 "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5297 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5298 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
5299 + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
5300 // Copy GRANTS
5301 db.execSQL("INSERT INTO " + TABLE_GRANTS
5302 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5303 + GRANTS_GRANTEE_UID + ") " +
5304 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5305 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
5306 // Copy META
5307 db.execSQL("INSERT INTO " + TABLE_META
5308 + "(" + META_KEY + "," + META_VALUE + ") "
5309 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
5310 db.setTransactionSuccessful();
5311 db.endTransaction();
5312
5313 db.execSQL("DETACH DATABASE preNDb");
5314 }
5315
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005316 static DeDatabaseHelper create(
5317 Context context,
5318 int userId,
5319 File preNDatabaseFile,
5320 File deDatabaseFile) {
5321 boolean newDbExists = deDatabaseFile.exists();
5322 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
5323 deDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005324 // If the db just created, and there is a legacy db, migrate it
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005325 if (!newDbExists && preNDatabaseFile.exists()) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005326 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005327 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
5328 preNDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005329 // Open the database to force upgrade if required
5330 preNDatabaseHelper.getWritableDatabase();
5331 preNDatabaseHelper.close();
5332 // Move data without SPII to DE
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005333 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005334 }
5335 return deDatabaseHelper;
5336 }
5337 }
5338
5339 static class CeDatabaseHelper extends SQLiteOpenHelper {
5340
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005341 public CeDatabaseHelper(Context context, String ceDatabaseName) {
5342 super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005343 }
5344
5345 /**
5346 * This call needs to be made while the mCacheLock is held.
5347 * @param db The database.
5348 */
5349 @Override
5350 public void onCreate(SQLiteDatabase db) {
5351 Log.i(TAG, "Creating CE database " + getDatabaseName());
5352 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5353 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5354 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5355 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5356 + ACCOUNTS_PASSWORD + " TEXT, "
5357 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5358
5359 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
5360 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5361 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5362 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
5363 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
5364 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
5365
5366 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
5367 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5368 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
5369 + EXTRAS_KEY + " TEXT NOT NULL, "
5370 + EXTRAS_VALUE + " TEXT, "
5371 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
5372
5373 createAccountsDeletionTrigger(db);
5374 }
5375
5376 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5377 db.execSQL(""
5378 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5379 + " BEGIN"
5380 + " DELETE FROM " + TABLE_AUTHTOKENS
5381 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5382 + " DELETE FROM " + TABLE_EXTRAS
5383 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5384 + " END");
5385 }
5386
5387 @Override
5388 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5389 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
5390
5391 if (oldVersion == 9) {
5392 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5393 Log.v(TAG, "onUpgrade upgrading to v10");
5394 }
5395 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
5396 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
5397 // Recreate the trigger, since the old one references the table to be removed
5398 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
5399 createAccountsDeletionTrigger(db);
5400 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
5401 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
5402 oldVersion ++;
5403 }
5404
5405 if (oldVersion != newVersion) {
5406 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5407 }
5408 }
5409
5410 @Override
5411 public void onOpen(SQLiteDatabase db) {
5412 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
5413 }
5414
Fyodor Kupolov35f68082016-04-06 12:14:17 -07005415
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005416 /**
5417 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
5418 * it also performs migration to the new CE database.
5419 * @param context
5420 * @param userId id of the user where the database is located
5421 */
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005422 static CeDatabaseHelper create(
5423 Context context,
5424 int userId,
5425 File preNDatabaseFile,
5426 File ceDatabaseFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005427 boolean newDbExists = ceDatabaseFile.exists();
5428 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5429 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005430 + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005431 }
5432 boolean removeOldDb = false;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005433 if (!newDbExists && preNDatabaseFile.exists()) {
5434 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005435 }
5436 // Try to open and upgrade if necessary
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005437 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005438 ceHelper.getWritableDatabase();
5439 ceHelper.close();
5440 if (removeOldDb) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005441 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
5442 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
5443 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
5444 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005445 }
5446 return ceHelper;
5447 }
5448
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005449 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005450 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005451 try {
5452 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
5453 } catch (IOException e) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005454 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005455 // Try to remove potentially damaged file if I/O error occurred
5456 deleteDbFileWarnIfFailed(ceDbFile);
5457 return false;
5458 }
5459 return true;
5460 }
5461 }
5462
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005463 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07005464 return asBinder();
5465 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005466
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005467 /**
5468 * Searches array of arguments for the specified string
5469 * @param args array of argument strings
5470 * @param value value to search for
5471 * @return true if the value is contained in the array
5472 */
5473 private static boolean scanArgs(String[] args, String value) {
5474 if (args != null) {
5475 for (String arg : args) {
5476 if (value.equals(arg)) {
5477 return true;
5478 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005479 }
5480 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005481 return false;
5482 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005483
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005484 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005485 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07005486 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
5487 != PackageManager.PERMISSION_GRANTED) {
5488 fout.println("Permission Denial: can't dump AccountsManager from from pid="
5489 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
5490 + " without permission " + android.Manifest.permission.DUMP);
5491 return;
5492 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005493 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005494 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07005495
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005496 final List<UserInfo> users = getUserManager().getUsers();
5497 for (UserInfo user : users) {
5498 ipw.println("User " + user + ":");
5499 ipw.increaseIndent();
5500 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5501 ipw.println();
5502 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08005503 }
5504 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005505
Amith Yamasani04e0d262012-02-14 11:50:53 -08005506 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5507 String[] args, boolean isCheckinRequest) {
5508 synchronized (userAccounts.cacheLock) {
5509 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005510
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005511 if (isCheckinRequest) {
5512 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005513 AccountsDbUtils.dumpAccountsTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005514 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005515 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07005516 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005517 fout.println("Accounts: " + accounts.length);
5518 for (Account account : accounts) {
5519 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005520 }
Fred Quintana307da1a2010-01-21 14:24:20 -08005521
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005522 // Add debug information.
5523 fout.println();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005524 AccountsDbUtils.dumpDebugTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005525 fout.println();
5526 synchronized (mSessions) {
5527 final long now = SystemClock.elapsedRealtime();
5528 fout.println("Active Sessions: " + mSessions.size());
5529 for (Session session : mSessions.values()) {
5530 fout.println(" " + session.toDebugString(now));
5531 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005532 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005533
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005534 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005535 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005536 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005537 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005538 }
5539
Amith Yamasani04e0d262012-02-14 11:50:53 -08005540 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005541 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005542 long identityToken = clearCallingIdentity();
5543 try {
5544 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5545 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5546 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005547
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005548 if (intent.getComponent() != null &&
5549 GrantCredentialsPermissionActivity.class.getName().equals(
5550 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005551 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005552 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005553 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005554 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07005555 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005556
Fred Quintana33f889a2009-09-14 17:31:26 -07005557 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005558 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005559 Notification n = new Notification.Builder(contextForUser)
5560 .setWhen(0)
5561 .setSmallIcon(android.R.drawable.stat_sys_warning)
5562 .setColor(contextForUser.getColor(
5563 com.android.internal.R.color.system_notification_accent_color))
5564 .setContentTitle(String.format(notificationTitleFormat, account.name))
5565 .setContentText(message)
5566 .setContentIntent(PendingIntent.getActivityAsUser(
5567 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005568 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005569 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005570 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005571 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005572 } finally {
5573 restoreCallingIdentity(identityToken);
5574 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005575 }
5576
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005577 @VisibleForTesting
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005578 protected void installNotification(int notificationId, final Notification notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005579 UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005580 installNotification(notificationId, notification, "android", user.getIdentifier());
5581 }
5582
5583 private void installNotification(int notificationId, final Notification notification,
5584 String packageName, int userId) {
5585 final long token = clearCallingIdentity();
5586 try {
5587 INotificationManager notificationManager = NotificationManager.getService();
5588 try {
5589 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
5590 notificationId, notification, new int[1], userId);
5591 } catch (RemoteException e) {
5592 /* ignore - local call */
5593 }
5594 } finally {
5595 Binder.restoreCallingIdentity(token);
5596 }
Fred Quintana56285a62010-12-02 14:20:51 -08005597 }
5598
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005599 @VisibleForTesting
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005600 protected void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005601 cancelNotification(id, mContext.getPackageName(), user);
5602 }
5603
5604 protected void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005605 long identityToken = clearCallingIdentity();
5606 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005607 INotificationManager service = INotificationManager.Stub.asInterface(
5608 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
5609 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
5610 } catch (RemoteException e) {
5611 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005612 } finally {
5613 restoreCallingIdentity(identityToken);
5614 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005615 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005616
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005617 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
5618 for (String perm : permissions) {
5619 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5620 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5621 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
5622 }
5623 final int opCode = AppOpsManager.permissionToOpCode(perm);
5624 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
5625 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5626 return true;
5627 }
5628 }
5629 }
5630 return false;
5631 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005632
Amith Yamasani67df64b2012-12-14 12:09:36 -08005633 private int handleIncomingUser(int userId) {
5634 try {
5635 return ActivityManagerNative.getDefault().handleIncomingUser(
5636 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5637 } catch (RemoteException re) {
5638 // Shouldn't happen, local.
5639 }
5640 return userId;
5641 }
5642
Christopher Tateccbf84f2013-05-08 15:25:41 -07005643 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005644 final int callingUserId = UserHandle.getUserId(callingUid);
5645
5646 final PackageManager userPackageManager;
5647 try {
5648 userPackageManager = mContext.createPackageContextAsUser(
5649 "android", 0, new UserHandle(callingUserId)).getPackageManager();
5650 } catch (NameNotFoundException e) {
5651 return false;
5652 }
5653
5654 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07005655 for (String name : packages) {
5656 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005657 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08005658 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08005659 && (packageInfo.applicationInfo.privateFlags
5660 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07005661 return true;
5662 }
5663 } catch (PackageManager.NameNotFoundException e) {
5664 return false;
5665 }
5666 }
5667 return false;
5668 }
5669
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005670 private boolean permissionIsGranted(
5671 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005672 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5673 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5674 Log.v(TAG, "Access to " + account + " granted calling uid is system");
5675 }
5676 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005677 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005678
5679 if (isPrivileged(callerUid)) {
5680 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5681 Log.v(TAG, "Access to " + account + " granted calling uid "
5682 + callerUid + " privileged");
5683 }
5684 return true;
5685 }
5686 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5687 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5688 Log.v(TAG, "Access to " + account + " granted calling uid "
5689 + callerUid + " manages the account");
5690 }
5691 return true;
5692 }
5693 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5694 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5695 Log.v(TAG, "Access to " + account + " granted calling uid "
5696 + callerUid + " user granted access");
5697 }
5698 return true;
5699 }
5700
5701 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5702 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5703 }
5704
5705 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005706 }
5707
Svetoslavf3f02ac2015-09-08 14:36:35 -07005708 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5709 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005710 if (accountType == null) {
5711 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005712 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005713 return getTypesVisibleToCaller(callingUid, userId,
5714 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005715 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005716 }
5717
5718 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5719 if (accountType == null) {
5720 return false;
5721 } else {
5722 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5723 }
5724 }
5725
Svetoslavf3f02ac2015-09-08 14:36:35 -07005726 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5727 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005728 boolean isPermitted =
5729 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5730 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005731 return getTypesForCaller(callingUid, userId, isPermitted);
5732 }
5733
5734 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5735 return getTypesForCaller(callingUid, userId, false);
5736 }
5737
5738 private List<String> getTypesForCaller(
5739 int callingUid, int userId, boolean isOtherwisePermitted) {
5740 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005741 long identityToken = Binder.clearCallingIdentity();
5742 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5743 try {
5744 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5745 } finally {
5746 Binder.restoreCallingIdentity(identityToken);
5747 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005748 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005749 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005750 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5751 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5752 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005753 }
5754 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005755 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005756 }
5757
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005758 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5759 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5760 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5761 if (account.name.equals(accountName)) {
5762 return true;
5763 }
5764 }
5765 }
5766 return false;
5767 }
5768
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005769 private static void checkManageUsersPermission(String message) {
5770 if (ActivityManager.checkComponentPermission(
5771 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5772 != PackageManager.PERMISSION_GRANTED) {
5773 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5774 }
5775 }
5776
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005777 private static void checkManageOrCreateUsersPermission(String message) {
5778 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5779 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5780 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5781 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5782 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5783 + message);
5784 }
5785 }
5786
Amith Yamasani04e0d262012-02-14 11:50:53 -08005787 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5788 int callerUid) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005789 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005790 return true;
5791 }
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005792 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005793 synchronized (accounts.cacheLock) {
5794 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005795 final String query;
5796 final String[] args;
5797
5798 if (authTokenType != null) {
5799 query = COUNT_OF_MATCHING_GRANTS;
5800 args = new String[] {String.valueOf(callerUid), authTokenType,
5801 account.name, account.type};
5802 } else {
5803 query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
5804 args = new String[] {String.valueOf(callerUid), account.name,
5805 account.type};
5806 }
5807 final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005808
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005809 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5810 // TODO: Skip this check when running automated tests. Replace this
5811 // with a more general solution.
5812 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005813 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005814 + " but ignoring since device is in test harness.");
5815 return true;
5816 }
5817 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005818 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005819 }
5820
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005821 private boolean isSystemUid(int callingUid) {
5822 String[] packages = null;
5823 long ident = Binder.clearCallingIdentity();
5824 try {
5825 packages = mPackageManager.getPackagesForUid(callingUid);
5826 } finally {
5827 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005828 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005829 if (packages != null) {
5830 for (String name : packages) {
5831 try {
5832 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5833 if (packageInfo != null
5834 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5835 != 0) {
5836 return true;
5837 }
5838 } catch (PackageManager.NameNotFoundException e) {
5839 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5840 }
5841 }
5842 } else {
5843 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005844 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005845 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005846 }
5847
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005848 /** Succeeds if any of the specified permissions are granted. */
5849 private void checkReadAccountsPermitted(
5850 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005851 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005852 int userId,
5853 String opPackageName) {
5854 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005855 String msg = String.format(
5856 "caller uid %s cannot access %s accounts",
5857 callingUid,
5858 accountType);
5859 Log.w(TAG, " " + msg);
5860 throw new SecurityException(msg);
5861 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005862 }
5863
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005864 private boolean canUserModifyAccounts(int userId, int callingUid) {
5865 // the managing app can always modify accounts
5866 if (isProfileOwner(callingUid)) {
5867 return true;
5868 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005869 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5870 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5871 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005872 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005873 return true;
5874 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005875
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005876 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5877 // the managing app can always modify accounts
5878 if (isProfileOwner(callingUid)) {
5879 return true;
5880 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005881 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5882 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005883 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005884 if (typesArray == null) {
5885 return true;
5886 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005887 for (String forbiddenType : typesArray) {
5888 if (forbiddenType.equals(accountType)) {
5889 return false;
5890 }
5891 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005892 return true;
5893 }
5894
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005895 private boolean isProfileOwner(int uid) {
5896 final DevicePolicyManagerInternal dpmi =
5897 LocalServices.getService(DevicePolicyManagerInternal.class);
5898 return (dpmi != null)
5899 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5900 }
5901
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005902 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005903 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5904 throws RemoteException {
5905 final int callingUid = getCallingUid();
5906
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005907 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005908 throw new SecurityException();
5909 }
5910
5911 if (value) {
5912 grantAppPermission(account, authTokenType, uid);
5913 } else {
5914 revokeAppPermission(account, authTokenType, uid);
5915 }
5916 }
5917
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005918 /**
5919 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5920 * <p>
5921 * Although this is public it can only be accessed via the AccountManagerService object
5922 * which is in the system. This means we don't need to protect it with permissions.
5923 * @hide
5924 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005925 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005926 if (account == null || authTokenType == null) {
5927 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005928 return;
5929 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005930 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005931 synchronized (accounts.cacheLock) {
5932 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005933 long accountId = AccountsDbUtils.findAccountId(db, account);
5934 if (accountId >= 0) {
5935 AccountsDbUtils.insertGrant(db, accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005936 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005937 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005938 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005939
5940 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005941 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005942 }
5943
5944 /**
5945 * Don't allow callers with the given uid permission to get credentials for
5946 * account/authTokenType.
5947 * <p>
5948 * Although this is public it can only be accessed via the AccountManagerService object
5949 * which is in the system. This means we don't need to protect it with permissions.
5950 * @hide
5951 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005952 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005953 if (account == null || authTokenType == null) {
5954 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005955 return;
5956 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005957 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005958 synchronized (accounts.cacheLock) {
5959 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005960 db.beginTransaction();
5961 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005962 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005963 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005964 AccountsDbUtils.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5965 db, accountId, authTokenType, uid);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005966 db.setTransactionSuccessful();
5967 }
5968 } finally {
5969 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005970 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005971 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5972 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005973 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005974 }
Fred Quintana56285a62010-12-02 14:20:51 -08005975
5976 static final private String stringArrayToString(String[] value) {
5977 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
5978 }
5979
Amith Yamasani04e0d262012-02-14 11:50:53 -08005980 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5981 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005982 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005983 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005984 for (Account curAccount : oldAccountsForType) {
5985 if (!curAccount.equals(account)) {
5986 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005987 }
5988 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005989 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005990 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005991 } else {
5992 Account[] newAccountsForType = new Account[newAccountsList.size()];
5993 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005994 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005995 }
Fred Quintana56285a62010-12-02 14:20:51 -08005996 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005997 accounts.userDataCache.remove(account);
5998 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005999 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006000 }
6001
6002 /**
6003 * This assumes that the caller has already checked that the account is not already present.
6004 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08006005 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
6006 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006007 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
6008 Account[] newAccountsForType = new Account[oldLength + 1];
6009 if (accountsForType != null) {
6010 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08006011 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006012 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006013 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08006014 }
6015
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006016 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07006017 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006018 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07006019 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006020 return unfiltered;
6021 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07006022 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07006023 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006024 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006025 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006026 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006027 // This might be a temporary way to specify a visible list
6028 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006029 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
6030 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006031 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006032 return unfiltered;
6033 }
6034 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006035 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006036 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
6037 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006038 String requiredAccountType = "";
6039 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07006040 // If there's an explicit callingPackage specified, check if that package
6041 // opted in to see restricted accounts.
6042 if (callingPackage != null) {
6043 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006044 if (pi != null && pi.restrictedAccountType != null) {
6045 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07006046 }
6047 } else {
6048 // Otherwise check if the callingUid has a package that has opted in
6049 for (String packageName : packages) {
6050 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
6051 if (pi != null && pi.restrictedAccountType != null) {
6052 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07006053 break;
6054 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006055 }
6056 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006057 } catch (NameNotFoundException nnfe) {
6058 }
6059 for (Account account : unfiltered) {
6060 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006061 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006062 } else {
6063 boolean found = false;
6064 for (Account shared : sharedAccounts) {
6065 if (shared.equals(account)) {
6066 found = true;
6067 break;
6068 }
6069 }
6070 if (!found) {
6071 allowed.add(account);
6072 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006073 }
6074 }
6075 Account[] filtered = new Account[allowed.size()];
6076 allowed.toArray(filtered);
6077 return filtered;
6078 } else {
6079 return unfiltered;
6080 }
6081 }
6082
Amith Yamasani27db4682013-03-30 17:07:47 -07006083 /*
6084 * packageName can be null. If not null, it should be used to filter out restricted accounts
6085 * that the package is not allowed to access.
6086 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006087 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07006088 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006089 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006090 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006091 if (accounts == null) {
6092 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08006093 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006094 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07006095 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006096 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006097 } else {
6098 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006099 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006100 totalLength += accounts.length;
6101 }
6102 if (totalLength == 0) {
6103 return EMPTY_ACCOUNT_ARRAY;
6104 }
6105 Account[] accounts = new Account[totalLength];
6106 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006107 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006108 System.arraycopy(accountsOfType, 0, accounts, totalLength,
6109 accountsOfType.length);
6110 totalLength += accountsOfType.length;
6111 }
Amith Yamasani27db4682013-03-30 17:07:47 -07006112 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006113 }
6114 }
6115
Amith Yamasani04e0d262012-02-14 11:50:53 -08006116 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6117 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006118 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006119 if (userDataForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006120 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006121 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006122 }
6123 if (value == null) {
6124 userDataForAccount.remove(key);
6125 } else {
6126 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006127 }
6128 }
6129
Carlos Valdivia91979be2015-05-22 14:11:35 -07006130 protected String readCachedTokenInternal(
6131 UserAccounts accounts,
6132 Account account,
6133 String tokenType,
6134 String callingPackage,
6135 byte[] pkgSigDigest) {
6136 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07006137 return accounts.accountTokenCaches.get(
6138 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07006139 }
6140 }
6141
Amith Yamasani04e0d262012-02-14 11:50:53 -08006142 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6143 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006144 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006145 if (authTokensForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006146 authTokensForAccount = AccountsDbUtils.findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006147 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006148 }
6149 if (value == null) {
6150 authTokensForAccount.remove(key);
6151 } else {
6152 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006153 }
6154 }
6155
Amith Yamasani04e0d262012-02-14 11:50:53 -08006156 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
6157 String authTokenType) {
6158 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006159 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006160 if (authTokensForAccount == null) {
6161 // need to populate the cache for this account
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07006162 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006163 authTokensForAccount = AccountsDbUtils
6164 .findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006165 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006166 }
6167 return authTokensForAccount.get(authTokenType);
6168 }
6169 }
6170
Simranjit Kohli858511c2016-03-10 18:36:11 +00006171 protected String readUserDataInternalLocked(
6172 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006173 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006174 if (userDataForAccount == null) {
6175 // need to populate the cache for this account
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07006176 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006177 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006178 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006179 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00006180 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08006181 }
6182
Kenny Guy07ad8dc2014-09-01 20:56:12 +01006183 private Context getContextForUser(UserHandle user) {
6184 try {
6185 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
6186 } catch (NameNotFoundException e) {
6187 // Default to mContext, not finding the package system is running as is unlikely.
6188 return mContext;
6189 }
6190 }
Sandra Kwan78812282015-11-04 11:19:47 -08006191
6192 private void sendResponse(IAccountManagerResponse response, Bundle result) {
6193 try {
6194 response.onResult(result);
6195 } catch (RemoteException e) {
6196 // if the caller is dead then there is no one to care about remote
6197 // exceptions
6198 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6199 Log.v(TAG, "failure while notifying response", e);
6200 }
6201 }
6202 }
6203
6204 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
6205 String errorMessage) {
6206 try {
6207 response.onError(errorCode, errorMessage);
6208 } catch (RemoteException e) {
6209 // if the caller is dead then there is no one to care about remote
6210 // exceptions
6211 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6212 Log.v(TAG, "failure while notifying response", e);
6213 }
6214 }
6215 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006216
6217 static class AccountsDbUtils {
6218
6219 static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
6220 String type) {
6221 Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
6222 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6223 new String[]{name, type}, null, null, null);
6224 try {
6225 if (cursor.moveToNext()) {
6226 return cursor.getString(0);
6227 }
6228 return null;
6229 } finally {
6230 cursor.close();
6231 }
6232 }
6233
6234 static Map<Long, Account> findAllAccounts(SQLiteDatabase db) {
6235 LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
6236 Cursor cursor = db.query(TABLE_ACCOUNTS,
6237 new String[] {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
6238 null, null, null, null, ACCOUNTS_ID);
6239 try {
6240 while (cursor.moveToNext()) {
6241 final long accountId = cursor.getLong(0);
6242 final String accountType = cursor.getString(1);
6243 final String accountName = cursor.getString(2);
6244
6245 final Account account = new Account(accountName, accountType);
6246 map.put(accountId, account);
6247 }
6248 } finally {
6249 cursor.close();
6250 }
6251 return map;
6252 }
6253
6254 static String findAccountPreviousName(SQLiteDatabase db, Account account) {
6255 Cursor cursor = db.query(
6256 TABLE_ACCOUNTS,
6257 new String[]{ ACCOUNTS_PREVIOUS_NAME },
6258 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6259 new String[] { account.name, account.type },
6260 null,
6261 null,
6262 null);
6263 try {
6264 if (cursor.moveToNext()) {
6265 return cursor.getString(0);
6266 }
6267 } finally {
6268 cursor.close();
6269 }
6270 return null;
6271 }
6272
6273 static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
6274 // Select accounts from CE that do not exist in DE
6275 Cursor cursor = db.rawQuery(
6276 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
6277 + " FROM " + CE_TABLE_ACCOUNTS
6278 + " WHERE NOT EXISTS "
6279 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
6280 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6281 + " )", null);
6282 try {
6283 List<Account> accounts = new ArrayList<>(cursor.getCount());
6284 while (cursor.moveToNext()) {
6285 String accountName = cursor.getString(0);
6286 String accountType = cursor.getString(1);
6287 accounts.add(new Account(accountName, accountType));
6288 }
6289 return accounts;
6290 } finally {
6291 cursor.close();
6292 }
6293 }
6294
6295 static boolean deleteAccount(SQLiteDatabase db, long accountId) {
6296 return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6297 }
6298
6299 static boolean deleteCeAccount(SQLiteDatabase db, long accountId) {
6300 return db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6301 }
6302
6303 /**
6304 * Returns information about auth tokens and their account for the specified query parameters.
6305 * Output is in the format:
6306 * <pre><code> | AUTHTOKEN_ID | ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
6307 */
6308 static Cursor findAuthtokenForAllAccounts(SQLiteDatabase db, String accountType,
6309 String authToken) {
6310 return db.rawQuery(
6311 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
6312 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
6313 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
6314 + " FROM " + CE_TABLE_ACCOUNTS
6315 + " JOIN " + CE_TABLE_AUTHTOKENS
6316 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6317 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
6318 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
6319 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
6320 new String[]{authToken, accountType});
6321 }
6322
6323 static boolean deleteAuthtokensByAccountIdAndType(SQLiteDatabase db, long accountId,
6324 String authtokenType) {
6325 return db.delete(CE_TABLE_AUTHTOKENS,
6326 AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
6327 new String[] {String.valueOf(accountId), authtokenType}) > 0;
6328 }
6329
6330 static boolean deleteAuthToken(SQLiteDatabase db, String authTokenId) {
6331 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
6332 new String[] {authTokenId}) > 0;
6333 }
6334
6335 static long insertAuthToken(SQLiteDatabase db, long accountId, String authTokenType,
6336 String authToken) {
6337 ContentValues values = new ContentValues();
6338 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
6339 values.put(AUTHTOKENS_TYPE, authTokenType);
6340 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
6341 return db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
6342 }
6343
6344 static Map<String, String> findAuthTokensByAccount(final SQLiteDatabase db, Account account) {
6345 HashMap<String, String> authTokensForAccount = new HashMap<>();
6346 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
6347 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
6348 SELECTION_AUTHTOKENS_BY_ACCOUNT,
6349 new String[]{account.name, account.type},
6350 null, null, null);
6351 try {
6352 while (cursor.moveToNext()) {
6353 final String type = cursor.getString(0);
6354 final String authToken = cursor.getString(1);
6355 authTokensForAccount.put(type, authToken);
6356 }
6357 } finally {
6358 cursor.close();
6359 }
6360 return authTokensForAccount;
6361 }
6362
6363 static int updateAccountPassword(SQLiteDatabase db, long accountId, String password) {
6364 final ContentValues values = new ContentValues();
6365 values.put(ACCOUNTS_PASSWORD, password);
6366 return db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
6367 new String[] {String.valueOf(accountId)});
6368 }
6369
6370 static boolean deleteAuthTokensByAccountId(SQLiteDatabase db, long accountId) {
6371 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
6372 new String[] {String.valueOf(accountId)}) > 0;
6373 }
6374
6375 static long insertSharedAccount(SQLiteDatabase db, Account account) {
6376 ContentValues values = new ContentValues();
6377 values.put(ACCOUNTS_NAME, account.name);
6378 values.put(ACCOUNTS_TYPE, account.type);
6379 return db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
6380 }
6381
6382 static boolean deleteSharedAccount(SQLiteDatabase db, Account account) {
6383 return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6384 new String[] {account.name, account.type}) > 0;
6385 }
6386
6387 static int renameSharedAccount(SQLiteDatabase db, Account account, String newName) {
6388 final ContentValues values = new ContentValues();
6389 values.put(ACCOUNTS_NAME, newName);
6390 return db.update(TABLE_SHARED_ACCOUNTS,
6391 values,
6392 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6393 new String[] { account.name, account.type });
6394 }
6395
6396 static List<Account> getSharedAccounts(SQLiteDatabase db) {
6397 ArrayList<Account> accountList = new ArrayList<>();
6398 Cursor cursor = null;
6399 try {
6400 cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE},
6401 null, null, null, null, null);
6402 if (cursor != null && cursor.moveToFirst()) {
6403 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
6404 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
6405 do {
6406 accountList.add(new Account(cursor.getString(nameIndex),
6407 cursor.getString(typeIndex)));
6408 } while (cursor.moveToNext());
6409 }
6410 } finally {
6411 if (cursor != null) {
6412 cursor.close();
6413 }
6414 }
6415 return accountList;
6416 }
6417
6418 static long findSharedAccountId(SQLiteDatabase db, Account account) {
6419 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
6420 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6421 try {
6422 if (cursor.moveToNext()) {
6423 return cursor.getLong(0);
6424 }
6425 return -1;
6426 } finally {
6427 cursor.close();
6428 }
6429 }
6430
6431 static long findAccountId(SQLiteDatabase db, Account account) {
6432 Cursor cursor = db.query(
6433 TABLE_ACCOUNTS, new String[] {ACCOUNTS_ID},
6434 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6435 try {
6436 if (cursor.moveToNext()) {
6437 return cursor.getLong(0);
6438 }
6439 return -1;
6440 } finally {
6441 cursor.close();
6442 }
6443 }
6444
6445 static long findExtrasIdByAccountId(SQLiteDatabase db, long accountId, String key) {
6446 Cursor cursor = db.query(
6447 CE_TABLE_EXTRAS, new String[] {EXTRAS_ID},
6448 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
6449 new String[]{key}, null, null, null);
6450 try {
6451 if (cursor.moveToNext()) {
6452 return cursor.getLong(0);
6453 }
6454 return -1;
6455 } finally {
6456 cursor.close();
6457 }
6458 }
6459
6460 static boolean updateExtra(SQLiteDatabase db, long extrasId, String value) {
6461 ContentValues values = new ContentValues();
6462 values.put(EXTRAS_VALUE, value);
6463 int rows = db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=?",
6464 new String[]{String.valueOf(extrasId)});
6465 return rows == 1;
6466 }
6467
6468 static long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
6469 ContentValues values = new ContentValues();
6470 values.put(EXTRAS_KEY, key);
6471 values.put(EXTRAS_ACCOUNTS_ID, accountId);
6472 values.put(EXTRAS_VALUE, value);
6473 return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
6474 }
6475
6476 static Map<String, String> findUserExtrasForAccount(SQLiteDatabase db, Account account) {
6477 Map<String, String> userExtrasForAccount = new HashMap<>();
6478 Cursor cursor = db.query(CE_TABLE_EXTRAS,
6479 COLUMNS_EXTRAS_KEY_AND_VALUE,
6480 SELECTION_USERDATA_BY_ACCOUNT,
6481 new String[] {account.name, account.type},
6482 null, null, null);
6483 try {
6484 while (cursor.moveToNext()) {
6485 final String tmpkey = cursor.getString(0);
6486 final String value = cursor.getString(1);
6487 userExtrasForAccount.put(tmpkey, value);
6488 }
6489 } finally {
6490 cursor.close();
6491 }
6492 return userExtrasForAccount;
6493 }
6494
6495 static long insertGrant(SQLiteDatabase db, long accountId, String authTokenType, int uid) {
6496 ContentValues values = new ContentValues();
6497 values.put(GRANTS_ACCOUNTS_ID, accountId);
6498 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
6499 values.put(GRANTS_GRANTEE_UID, uid);
6500 return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
6501 }
6502
6503 static boolean deleteGrantsByUid(SQLiteDatabase db, int uid) {
6504 return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
6505 new String[] {Integer.toString(uid)}) > 0;
6506 }
6507
6508 static boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(SQLiteDatabase db, long accountId, String authTokenType, long uid) {
6509 return db.delete(TABLE_GRANTS,
6510 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
6511 + GRANTS_GRANTEE_UID + "=?",
6512 new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0;
6513 }
6514
6515 static List<Integer> findAllUidGrants(SQLiteDatabase db) {
6516 List<Integer> result = new ArrayList<>();
6517 final Cursor cursor = db.query(TABLE_GRANTS,
6518 new String[] {GRANTS_GRANTEE_UID},
6519 null, null, GRANTS_GRANTEE_UID, null, null);
6520 try {
6521 while (cursor.moveToNext()) {
6522 final int uid = cursor.getInt(0);
6523 result.add(uid);
6524 }
6525 } finally {
6526 cursor.close();
6527 }
6528 return result;
6529 }
6530
6531 static long findMatchingGrantsCount(SQLiteDatabase db,
6532 int uid, String authTokenType, Account account) {
6533 String[] args = { String.valueOf(uid), authTokenType,
6534 account.name, account.type};
6535 return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
6536 }
6537
6538 static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
6539 ContentValues values = new ContentValues();
6540 values.put(META_KEY,
6541 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6542 values.put(META_VALUE, uid);
6543 return db.insert(TABLE_META, null, values);
6544 }
6545
6546 static long insertOrReplaceMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType,
6547 int uid) {
6548 ContentValues values = new ContentValues();
6549 values.put(META_KEY,
6550 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6551 values.put(META_VALUE, uid);
6552 return db.insertWithOnConflict(TABLE_META, null, values,
6553 SQLiteDatabase.CONFLICT_REPLACE);
6554 }
6555
6556
6557 static Map<String, Integer> findMetaAuthUid(SQLiteDatabase db) {
6558 Cursor metaCursor = db.query(
6559 TABLE_META,
6560 new String[] {META_KEY, META_VALUE},
6561 SELECTION_META_BY_AUTHENTICATOR_TYPE,
6562 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
6563 null /* groupBy */,
6564 null /* having */,
6565 META_KEY);
6566 Map<String, Integer> map = new LinkedHashMap<>();
6567 try {
6568 while (metaCursor.moveToNext()) {
6569 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
6570 String uidStr = metaCursor.getString(1);
6571 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
6572 // Should never happen.
6573 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
6574 + ", uid empty: " + TextUtils.isEmpty(uidStr));
6575 continue;
6576 }
6577 int uid = Integer.parseInt(metaCursor.getString(1));
6578 map.put(type, uid);
6579 }
6580 } finally {
6581 metaCursor.close();
6582 }
6583 return map;
6584 }
6585
6586 static boolean deleteMetaByAuthTypeAndUid(SQLiteDatabase db, String type, int uid) {
6587 return db.delete(
6588 TABLE_META,
6589 META_KEY + "=? AND " + META_VALUE + "=?",
6590 new String[] {
6591 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
6592 String.valueOf(uid)}
6593 ) > 0;
6594 }
6595
6596 static void dumpAccountsTable(SQLiteDatabase db, PrintWriter pw) {
6597 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
6598 null, null, ACCOUNTS_TYPE, null, null);
6599 try {
6600 while (cursor.moveToNext()) {
6601 // print type,count
6602 pw.println(cursor.getString(0) + "," + cursor.getString(1));
6603 }
6604 } finally {
6605 if (cursor != null) {
6606 cursor.close();
6607 }
6608 }
6609 }
6610
6611 static void dumpDebugTable(SQLiteDatabase db, PrintWriter pw) {
6612 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
6613 null, null, null, null, DebugDbHelper.TIMESTAMP);
6614 pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
6615 pw.println("Accounts History");
6616 try {
6617 while (cursor.moveToNext()) {
6618 // print type,count
6619 pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
6620 cursor.getString(2) + "," + cursor.getString(3) + ","
6621 + cursor.getString(4) + "," + cursor.getString(5));
6622 }
6623 } finally {
6624 cursor.close();
6625 }
6626 }
Svet Ganov890a2102016-08-24 00:08:00 -07006627 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006628
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006629 private final class AccountManagerInternalImpl extends AccountManagerInternal {
6630 @Override
6631 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
6632 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
6633 if (account == null) {
6634 Slog.w(TAG, "account cannot be null");
6635 return;
6636 }
6637 if (packageName == null) {
6638 Slog.w(TAG, "packageName cannot be null");
6639 return;
6640 }
6641 if (userId < UserHandle.USER_SYSTEM) {
6642 Slog.w(TAG, "user id must be concrete");
6643 return;
6644 }
6645 if (callback == null) {
6646 Slog.w(TAG, "callback cannot be null");
6647 return;
6648 }
6649
6650 if (hasAccountAccess(account, packageName, new UserHandle(userId))) {
6651 Bundle result = new Bundle();
6652 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
6653 callback.sendResult(result);
6654 return;
6655 }
6656
6657 final int uid;
6658 try {
6659 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
6660 } catch (NameNotFoundException e) {
6661 Slog.e(TAG, "Unknown package " + packageName);
6662 return;
6663 }
6664
6665 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
6666 doNotification(mUsers.get(userId), account, null, intent, packageName, userId);
6667 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006668 }
Fred Quintana60307342009-03-24 22:48:12 -07006669}