blob: a275773dec74e82812631658949e20fa24b2a44e [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;
Svet Ganovf6d424f12016-09-20 20:18:53 -070029import android.accounts.IAccountAccessTracker;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080030import android.accounts.IAccountAuthenticator;
31import android.accounts.IAccountAuthenticatorResponse;
32import android.accounts.IAccountManager;
33import android.accounts.IAccountManagerResponse;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070034import android.annotation.IntRange;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070035import android.annotation.NonNull;
Svet Ganovf6d424f12016-09-20 20:18:53 -070036import android.annotation.Nullable;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080037import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070038import android.app.ActivityManagerNative;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070039import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070040import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070041import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070042import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070043import android.app.Notification;
44import android.app.NotificationManager;
45import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000046import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010047import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000048import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070049import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070050import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070051import android.content.ContentValues;
52import android.content.Context;
53import android.content.Intent;
54import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070055import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070056import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080057import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070059import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070060import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070061import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070062import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070063import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070064import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070065import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070066import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070067import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070068import android.database.Cursor;
69import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070070import android.database.sqlite.SQLiteDatabase;
71import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070072import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070073import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070074import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080075import android.os.Environment;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070076import android.os.FileUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070077import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070078import android.os.IBinder;
79import android.os.Looper;
80import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070081import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070082import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070083import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070084import android.os.RemoteException;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070085import android.os.ServiceManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070086import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070087import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070088import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070089import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070090import android.text.TextUtils;
91import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070092import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070093import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080094import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070095import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070096
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070097import com.android.internal.R;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -070098import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070099import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800100import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800101import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -0700102import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000103import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700104import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600105import com.android.server.SystemService;
106
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700107import com.google.android.collect.Lists;
108import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700109
Oscar Montemayora8529f62009-11-18 10:14:20 -0800110import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700111import java.io.FileDescriptor;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700112import java.io.IOException;
Fred Quintanaa698f422009-04-08 19:14:54 -0700113import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800114import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700115import java.security.MessageDigest;
116import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700117import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700118import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800119import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700120import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700121import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700122import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700123import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800124import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700125import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800126import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800127import java.util.Map.Entry;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700128import java.util.Set;
Svet Ganovf6d424f12016-09-20 20:18:53 -0700129import java.util.concurrent.CopyOnWriteArrayList;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700130import java.util.concurrent.atomic.AtomicInteger;
131import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700132
Fred Quintana60307342009-03-24 22:48:12 -0700133/**
134 * A system service that provides account, password, and authtoken management for all
135 * accounts on the device. Some of these calls are implemented with the help of the corresponding
136 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
137 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700138 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700139 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700140 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700141public class AccountManagerService
142 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800143 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700144 private static final String TAG = "AccountManagerService";
145
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600146 public static class Lifecycle extends SystemService {
147 private AccountManagerService mService;
148
149 public Lifecycle(Context context) {
150 super(context);
151 }
152
153 @Override
154 public void onStart() {
155 mService = new AccountManagerService(getContext());
156 publishBinderService(Context.ACCOUNT_SERVICE, mService);
157 }
158
159 @Override
160 public void onBootPhase(int phase) {
161 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
162 mService.systemReady();
163 }
164 }
165
166 @Override
167 public void onUnlockUser(int userHandle) {
168 mService.onUnlockUser(userHandle);
169 }
170 }
171
Fred Quintana60307342009-03-24 22:48:12 -0700172 private static final String DATABASE_NAME = "accounts.db";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700173 private static final int PRE_N_DATABASE_VERSION = 9;
174 private static final int CE_DATABASE_VERSION = 10;
175 private static final int DE_DATABASE_VERSION = 1;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700176
177 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700178
179 private final Context mContext;
180
Fred Quintana56285a62010-12-02 14:20:51 -0800181 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700182 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700183 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800184
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700185 private final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700186
Fred Quintana60307342009-03-24 22:48:12 -0700187 // Messages that can be sent on mHandler
188 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700189 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700190
Fred Quintana56285a62010-12-02 14:20:51 -0800191 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700192
193 private static final String TABLE_ACCOUNTS = "accounts";
194 private static final String ACCOUNTS_ID = "_id";
195 private static final String ACCOUNTS_NAME = "name";
196 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700197 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700198 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700199 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800200 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
201 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700202
203 private static final String TABLE_AUTHTOKENS = "authtokens";
204 private static final String AUTHTOKENS_ID = "_id";
205 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
206 private static final String AUTHTOKENS_TYPE = "type";
207 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
208
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700209 private static final String TABLE_GRANTS = "grants";
210 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
211 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
212 private static final String GRANTS_GRANTEE_UID = "uid";
213
Fred Quintana60307342009-03-24 22:48:12 -0700214 private static final String TABLE_EXTRAS = "extras";
215 private static final String EXTRAS_ID = "_id";
216 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
217 private static final String EXTRAS_KEY = "key";
218 private static final String EXTRAS_VALUE = "value";
219
220 private static final String TABLE_META = "meta";
221 private static final String META_KEY = "key";
222 private static final String META_VALUE = "value";
223
Amith Yamasani67df64b2012-12-14 12:09:36 -0800224 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700225 private static final String SHARED_ACCOUNTS_ID = "_id";
226
227 private static final String PRE_N_DATABASE_NAME = "accounts.db";
228 private static final String CE_DATABASE_NAME = "accounts_ce.db";
229 private static final String DE_DATABASE_NAME = "accounts_de.db";
230 private static final String CE_DB_PREFIX = "ceDb.";
231 private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
232 private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
233 private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800234
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700235 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
236 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700237 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800238
Carlos Valdivia91979be2015-05-22 14:11:35 -0700239 static {
240 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
241 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
242 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700243
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700244 private static final String COUNT_OF_MATCHING_GRANTS = ""
245 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
246 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
247 + " AND " + GRANTS_GRANTEE_UID + "=?"
248 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
249 + " AND " + ACCOUNTS_NAME + "=?"
250 + " AND " + ACCOUNTS_TYPE + "=?";
251
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700252 private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = ""
253 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
254 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
255 + " AND " + GRANTS_GRANTEE_UID + "=?"
256 + " AND " + ACCOUNTS_NAME + "=?"
257 + " AND " + ACCOUNTS_TYPE + "=?";
258
Fred Quintana56285a62010-12-02 14:20:51 -0800259 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
260 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700261
Fred Quintana56285a62010-12-02 14:20:51 -0800262 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
263 AUTHTOKENS_AUTHTOKEN};
264
265 private static final String SELECTION_USERDATA_BY_ACCOUNT =
266 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
267 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
268
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800269 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
270 "auth_uid_for_type:";
271 private static final String META_KEY_DELIMITER = ":";
272 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
273
Fred Quintanaa698f422009-04-08 19:14:54 -0700274 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700275 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
276
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700277 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
278
Amith Yamasani04e0d262012-02-14 11:50:53 -0800279 static class UserAccounts {
280 private final int userId;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700281 private final DeDatabaseHelper openHelper;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800282 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
283 credentialsPermissionNotificationIds =
284 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
285 private final HashMap<Account, Integer> signinRequiredNotificationIds =
286 new HashMap<Account, Integer>();
287 private final Object cacheLock = new Object();
288 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700289 private final HashMap<String, Account[]> accountCache =
Svet Ganovf6d424f12016-09-20 20:18:53 -0700290 new LinkedHashMap<>();
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>> userDataCache = new HashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800293 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700294 private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700295
296 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700297 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700298
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700299 /** protected by the {@link #cacheLock} */
300 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
301 new HashMap<>();
302
303 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
304 number. Another maps Account number to Account.*/
305
306 /** protected by the {@link #cacheLock} */
307 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
308 new SparseArray<>();
309
310 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
311 /** protected by the {@link #cacheLock} */
312 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
313
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700314 /**
315 * protected by the {@link #cacheLock}
316 *
317 * Caches the previous names associated with an account. Previous names
318 * should be cached because we expect that when an Account is renamed,
319 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
320 * want to know if the accounts they care about have been renamed.
321 *
322 * The previous names are wrapped in an {@link AtomicReference} so that
323 * we can distinguish between those accounts with no previous names and
324 * those whose previous names haven't been cached (yet).
325 */
326 private final HashMap<Account, AtomicReference<String>> previousNameCache =
327 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800328
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700329 private int debugDbInsertionPoint = -1;
330 private SQLiteStatement statementForLogging;
331
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700332 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800333 this.userId = userId;
334 synchronized (cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700335 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800336 }
337 }
338 }
339
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700340 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600341 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Svet Ganovf6d424f12016-09-20 20:18:53 -0700342 private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
343 mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800344
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700345 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700346 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700347
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700348 /**
349 * This should only be called by system code. One should only call this after the service
350 * has started.
351 * @return a reference to the AccountManagerService instance
352 * @hide
353 */
354 public static AccountManagerService getSingleton() {
355 return sThis.get();
356 }
Fred Quintana60307342009-03-24 22:48:12 -0700357
Fred Quintana56285a62010-12-02 14:20:51 -0800358 public AccountManagerService(Context context) {
359 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700360 }
361
Fred Quintana56285a62010-12-02 14:20:51 -0800362 public AccountManagerService(Context context, PackageManager packageManager,
363 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700364 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800365 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700366 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700367
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700368 ServiceThread serviceThread = new ServiceThread(TAG,
369 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
370 serviceThread.start();
371 mHandler = new MessageHandler(serviceThread.getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700372
Fred Quintana56285a62010-12-02 14:20:51 -0800373 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800374 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700375
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700376 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800377
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700378 addRequestsForPreInstalledApplications();
379
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800380 IntentFilter intentFilter = new IntentFilter();
381 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
382 intentFilter.addDataScheme("package");
383 mContext.registerReceiver(new BroadcastReceiver() {
384 @Override
385 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700386 // Don't delete accounts when updating a authenticator's
387 // package.
388 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700389 /* Purging data requires file io, don't block the main thread. This is probably
390 * less than ideal because we are introducing a race condition where old grants
391 * could be exercised until they are purged. But that race condition existed
392 * anyway with the broadcast receiver.
393 *
394 * Ideally, we would completely clear the cache, purge data from the database,
395 * and then rebuild the cache. All under the cache lock. But that change is too
396 * large at this point.
397 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700398 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700399 @Override
400 public void run() {
401 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700402
403 /* clears application request's for account types supported */
404 int uidOfUninstalledApplication =
405 intent.getIntExtra(Intent.EXTRA_UID, -1);
406 if(uidOfUninstalledApplication != -1) {
407 clearRequestedAccountVisibility(uidOfUninstalledApplication,
408 getUserAccounts(UserHandle.getUserId(
409 uidOfUninstalledApplication)));
410 }
411
412 /* removes visibility of previous UID of this uninstalled application*/
413 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
414 getUserAccounts(UserHandle.getUserId(
415 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700416 }
417 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700418 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700419 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700420
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800421 }
422 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800423
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700424 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
425 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
426 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
427 packageAddedOrChangedFilter.addDataScheme("package");
428 mContext.registerReceiverAsUser(new BroadcastReceiver() {
429 @Override
430 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700431 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700432 @Override
433 public void run() {
434 int uidOfInstalledApplication =
435 intent.getIntExtra(Intent.EXTRA_UID, -1);
436 if(uidOfInstalledApplication != -1) {
437 registerAccountTypesSupported(
438 uidOfInstalledApplication,
439 getUserAccounts(
440 UserHandle.getUserId(uidOfInstalledApplication)));
441 }
442 }
443 });
444 }
445 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
446
Amith Yamasani13593602012-03-22 16:16:17 -0700447 IntentFilter userFilter = new IntentFilter();
448 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800449 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700450 @Override
451 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800452 String action = intent.getAction();
453 if (Intent.ACTION_USER_REMOVED.equals(action)) {
454 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800455 }
Amith Yamasani13593602012-03-22 16:16:17 -0700456 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800457 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700458
459 LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl());
460
461 // Need to cancel account request notifications if the update/install can access the account
462 new PackageMonitor() {
463 @Override
464 public void onPackageAdded(String packageName, int uid) {
465 // Called on a handler, and running as the system
466 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
467 }
468
469 @Override
470 public void onPackageUpdateFinished(String packageName, int uid) {
471 // Called on a handler, and running as the system
472 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
473 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700474 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700475
476 // Cancel account request notification if an app op was preventing the account access
477 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
478 new AppOpsManager.OnOpChangedInternalListener() {
479 @Override
480 public void onOpChanged(int op, String packageName) {
481 try {
482 final int userId = ActivityManager.getCurrentUser();
483 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
484 final int mode = mAppOpsManager.checkOpNoThrow(
485 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
486 if (mode == AppOpsManager.MODE_ALLOWED) {
487 final long identity = Binder.clearCallingIdentity();
488 try {
489 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
490 } finally {
491 Binder.restoreCallingIdentity(identity);
492 }
493 }
494 } catch (NameNotFoundException e) {
495 /* ignore */
496 }
497 }
498 });
499
500 // Cancel account request notification if a permission was preventing the account access
501 mPackageManager.addOnPermissionsChangeListener(
502 (int uid) -> {
503 Account[] accounts = null;
504 String[] packageNames = mPackageManager.getPackagesForUid(uid);
505 if (packageNames != null) {
506 final int userId = UserHandle.getUserId(uid);
507 final long identity = Binder.clearCallingIdentity();
508 try {
509 for (String packageName : packageNames) {
510 if (mContext.getPackageManager().checkPermission(
511 Manifest.permission.GET_ACCOUNTS, packageName)
512 != PackageManager.PERMISSION_GRANTED) {
513 continue;
514 }
515
516 if (accounts == null) {
517 accounts = getAccountsAsUser(null, userId, "android");
518 if (ArrayUtils.isEmpty(accounts)) {
519 return;
520 }
521 }
522
523 for (Account account : accounts) {
524 cancelAccountAccessRequestNotificationIfNeeded(
525 account, uid, packageName, true);
526 }
527 }
528 } finally {
529 Binder.restoreCallingIdentity(identity);
530 }
531 }
532 });
533 }
534
535 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
536 boolean checkAccess) {
537 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
538 for (Account account : accounts) {
539 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
540 }
541 }
542
543 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
544 boolean checkAccess) {
545 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
546 for (Account account : accounts) {
547 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
548 }
549 }
550
551 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
552 boolean checkAccess) {
553 String[] packageNames = mPackageManager.getPackagesForUid(uid);
554 if (packageNames != null) {
555 for (String packageName : packageNames) {
556 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
557 packageName, checkAccess);
558 }
559 }
560 }
561
562 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
563 int uid, String packageName, boolean checkAccess) {
564 if (!checkAccess || hasAccountAccess(account, packageName,
565 UserHandle.getUserHandleForUid(uid))) {
566 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -0700567 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700568 UserHandle.getUserHandleForUid(uid));
569 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800570 }
571
Dianne Hackborn164371f2013-10-01 19:10:13 -0700572 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700573 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
574 int[] selectedUids) {
575 if(addAccountExplicitly(account,password,extras)) {
576 for(int thisUid : selectedUids) {
577 makeAccountVisible(account, thisUid);
578 }
579 return true;
580 }
581 return false;
582 }
583
584 @Override
585 public int[] getRequestingUidsForType(String accountType) {
586 int callingUid = Binder.getCallingUid();
587 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
588 String msg = String.format(
589 "uid %s cannot get secrets for accounts of type: %s",
590 callingUid,
591 accountType);
592 throw new SecurityException(msg);
593 }
594 return getRequestingUidsForType(accountType, getUserAccounts(
595 UserHandle.getUserId(callingUid)));
596 }
597
598 /**
599 * Returns all UIDs for applications that requested the account type. This method
600 * is called indirectly by the Authenticator and AccountManager
601 *
602 * @param accountType authenticator would like to know the requesting apps of
603 * @param ua UserAccount that currently hosts the account and application
604 *
605 * @return ArrayList of all UIDs that support accounts of this
606 * account type that seek approval (to be used to know which accounts for
607 * the authenticator to include in addAccountExplicitly). Null if none.
608 */
609 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
610 synchronized(ua.cacheLock) {
611 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
612 ua.mApplicationAccountRequestMappings;
613 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
614 accountType);
615 if(allUidsForAccountType == null) {
616 return null;
617 }
618 int[] toReturn = new int[allUidsForAccountType.size()];
619 for(int i = 0 ; i < toReturn.length ; i++) {
620 toReturn[i] = allUidsForAccountType.get(i);
621 }
622 return toReturn;
623 }
624 }
625
626 @Override
627 public boolean isAccountVisible(Account a, int uid) {
628 int callingUid = Binder.getCallingUid();
629 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
630 String msg = String.format(
631 "uid %s cannot get secrets for accounts of type: %s",
632 callingUid,
633 a.type);
634 throw new SecurityException(msg);
635 }
636 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
637 }
638
639 /**
640 * Checks visibility of certain account of a process identified
641 * by a given UID. This is called by the Authenticator indirectly.
642 *
643 * @param a The account to check visibility of
644 * @param uid UID to check visibility of
645 * @param ua UserAccount that currently hosts the account and application
646 *
647 * @return True if application has access to the account
648 *
649 */
650 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700651 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
652 return true;
653 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700654 int accountMapping = getMockAccountNumber(a, ua);
655 if(accountMapping < 0) {
656 return true;
657 }
658 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700659 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700660 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
661 ua.mVisibleListUidToMockAccountNumbers;
662 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700663 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
664 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
665 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700666 }
667 }
668
669 @Override
670 public boolean makeAccountVisible(Account a, int uid) {
671 int callingUid = Binder.getCallingUid();
672 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
673 String msg = String.format(
674 "uid %s cannot get secrets for accounts of type: %s",
675 callingUid,
676 a.type);
677 throw new SecurityException(msg);
678 }
679 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
680 }
681
682 /**
683 * Gives a certain UID, represented a application, access to an account. This method
684 * is called indirectly by the Authenticator.
685 *
686 * @param a Account to make visible
687 * @param uid to add visibility of the Account from
688 * @param ua UserAccount that currently hosts the account and application
689 *
690 * @return True if account made visible to application and was not previously visible.
691 */
692 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
693 int accountMapping = getMockAccountNumber(a, ua);
694 if(accountMapping < 0) {
695 accountMapping = makeAccountNumber(a, ua);
696 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700697 synchronized(ua.cacheLock) {
698 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
699 ua.mVisibleListUidToMockAccountNumbers;
700 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
701 if(linkedAccountsToUid == null) {
702 linkedAccountsToUid = new ArrayList<>();
703 linkedAccountsToUid.add(accountMapping);
704 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
705 } else if(!linkedAccountsToUid.contains(accountMapping)) {
706 linkedAccountsToUid.add(accountMapping);
707 } else {
708 return false;
709 }
710 }
711
712 String[] subPackages = mPackageManager.getPackagesForUid(uid);
713 if(subPackages != null) {
714 for(String subPackage : subPackages) {
715 sendNotification(subPackage, a);
716 }
717 }
718 return true;
719 }
720
721 @Override
722 public boolean removeAccountVisibility(Account a, int uid) {
723 int callingUid = Binder.getCallingUid();
724 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
725 String msg = String.format(
726 "uid %s cannot get secrets for accounts of type: %s",
727 callingUid,
728 a.type);
729 throw new SecurityException(msg);
730 }
731 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
732 }
733
734 /**
735 * Removes visibility of certain account of a process identified
736 * by a given UID to an application. This is called directly by the
737 * AccountManager and indirectly by the Authenticator.
738 *
739 * @param a Account to remove visibility from
740 * @param uid UID to remove visibility of the Account from
741 * @param ua UserAccount that hosts the account and application
742 *
743 * @return True if application access to account removed and was previously visible.
744 */
745 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
746 int accountMapping = getMockAccountNumber(a, ua);
747 if(accountMapping < 0) {
748 return false;
749 }
750 synchronized(ua.cacheLock) {
751 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
752 ua.mVisibleListUidToMockAccountNumbers;
753 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
754 if(linkedAccountsToUid != null) {
755 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
756 if(linkedAccountsToUid.size() == 0) {
757 userWlUidToMockAccountNums.remove(uid);
758 }
759 return toReturn;
760 }
761 }
762 return false;
763 }
764
765 /**
766 * Registers an application's preferences for supported account types for login. This is
767 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
768 *
769 * @param accountTypes account types third party app is willing to support
770 * @param uid of application requesting account visibility
771 * @param ua UserAccount that hosts the account and application
772 */
773 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
774 synchronized(ua.cacheLock) {
775 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
776 ua.mApplicationAccountRequestMappings;
777 for(String accountType : accountTypes) {
778 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
779 .get(accountType);
780 if(accountUidAppList == null) {
781 accountUidAppList = new ArrayList<>();
782 accountUidAppList.add(uid);
783 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
784 } else if (!accountUidAppList.contains(uid)) {
785 accountUidAppList.add(uid);
786 }
787 }
788 }
789 }
790
791 /**
792 * Registers the requested login account types requested by all the applications already
793 * installed on the device.
794 */
795 private void addRequestsForPreInstalledApplications() {
796 List<PackageInfo> allInstalledPackages = mContext.getPackageManager().
797 getInstalledPackages(0);
798 for(PackageInfo pi : allInstalledPackages) {
799 int currentUid = pi.applicationInfo.uid;
800 if(currentUid != -1) {
801 registerAccountTypesSupported(currentUid,
802 getUserAccounts(UserHandle.getUserId(currentUid)));
803 }
804 }
805 }
806
807 /**
808 * Clears all preferences an application had for login account types it offered
809 * support for. This method is used by AccountManager after application is
810 * uninstalled.
811 *
812 * @param uid Uid of the application to clear account type preferences
813 * @param ua UserAccount that hosted the account and application
814 *
815 * @return true if any previous settings were overridden.
816 */
817 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
818 boolean accountsDeleted = false;
819 ArrayList<String> accountTypesToRemove = new ArrayList<>();
820 synchronized(ua.cacheLock) {
821 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
822 ua.mApplicationAccountRequestMappings;
823 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
824 userApplicationAccountRequestMappings.entrySet();
825
826 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
827 ArrayList<Integer> supportedApps = entry.getValue();
828 if(supportedApps.remove((Integer) uid)) {
829 accountsDeleted = true;
830 }
831
832 if(supportedApps.isEmpty()) {
833 accountTypesToRemove.add(entry.getKey());
834 }
835 }
836
837 for(String s : accountTypesToRemove) {
838 userApplicationAccountRequestMappings.remove(s);
839 }
840 }
841
842 return accountsDeleted;
843 }
844
845 /**
846 * Retrieves the mock account number associated with an Account in order to later retrieve
847 * the account from the Integer-Account Mapping. An account number is not the same as
848 * accountId in the database. This method can be indirectly called by AccountManager and
849 * indirectly by the Authenticator.
850 *
851 * @param a account to retrieve account number mapping
852 * @param ua UserAccount that currently hosts the account and application
853 *
854 * @return account number affiliated with the Account in question. Negative number if none.
855 */
856 private int getMockAccountNumber(Account a, UserAccounts ua) {
857 //TODO: Each account is linked to AccountId rather than generated mock account numbers
858 SparseArray<Account> userAcctIdToAcctMap =
859 ua.mMockAccountIdToAccount;
860 synchronized(ua.cacheLock) {
861 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
862 if(indexOfAccount < 0) {
863 return -1;
864 }
865 return userAcctIdToAcctMap.keyAt(indexOfAccount);
866 }
867 }
868
869 /**
870 * Returns a full list of accounts that a certain UID is allowed access
871 * based on the visible list entries.
872 *
873 * @param uid of application to retrieve visible listed accounts for
874 * @param ua UserAccount that currently hosts the account and application
875 *
876 * @return array of Account values that are accessible by the given uids
877 */
878 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
879 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
880 synchronized(ua.cacheLock) {
881 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
882 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
883 ua.mVisibleListUidToMockAccountNumbers;
884 ArrayList<Integer> visibleListedUidAccountNumbers =
885 userWlUidToMockAccountNums.get(uid);
886 if(visibleListedUidAccountNumbers != null) {
887 for(Integer accountNumber : visibleListedUidAccountNumbers) {
888 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
889 visibleListedAccounts.add(currentAccount);
890 }
891 }
892 }
893 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
894 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
895 }
896
897 /**
898 * Makes an account number for a given Account to be mapped to.
899 * This method is called by makeVisible if an Account does not have
900 * a mapping for the visible list. This method is thus indirectly
901 * called by the Authenticator.
902 *
903 * @param a account to make an account number mapping of
904 * @param ua UserAccount that currently hosts the account and application
905 *
906 * @return account number created to map to the given account
907 */
908 // TODO: Remove this method and use accountId from DB.
909 private int makeAccountNumber(Account a, UserAccounts ua) {
910 synchronized(ua.cacheLock) {
911 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
912 int newAccountMapping = 0;
913 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
914 newAccountMapping++;
915 }
916 userAcctIdToAcctMap.put(newAccountMapping, a);
917 return newAccountMapping;
918 }
919 }
920
921
922
923 /**
924 * Registers an application, represented by a UID, to support account types detailed in
925 * the applications manifest as well as allowing it to opt for notifications.
926 *
927 * @param uid UID of application
928 * @param ua UserAccount that currently hosts the account and application
929 */
930 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
931 /* Account types supported are drawn from the Android Manifest of the Application */
932 String interestedPackages = null;
933 try {
934 String[] allPackages = mPackageManager.getPackagesForUid(uid);
Nicolas Prevotf7d8df12016-09-16 17:45:34 +0100935 if (allPackages != null) {
936 for(String aPackage : allPackages) {
937 ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
938 PackageManager.GET_META_DATA);
939 Bundle b = ai.metaData;
940 if(b == null) {
941 return;
942 }
943 interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700944 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700945 }
946 } catch (PackageManager.NameNotFoundException e) {
947 Log.d("NameNotFoundException", e.getMessage());
948 }
949 if(interestedPackages != null) {
950 /* request remote account types directly from here. Reads from Android Manifest */
951 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
952 }
953 }
954
955 /**
956 * Allows AccountManager to register account types that an application has login
957 * support for. This method over-writes all of the application's previous settings
958 * for accounts it supported.
959 *
960 * @param accountTypes array of account types application wishes to support
961 * @param uid of application registering requested account types
962 * @param ua UserAccount that hosts the account and application
963 */
964 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
965 if(accountTypes.length > 0) {
966 clearRequestedAccountVisibility(uid, ua);
967 addRequestedAccountsVisibility(accountTypes, uid, ua);
968 }
969 }
970
971 /**
972 * Removes visibility of all Accounts to this particular UID. This is called when an
973 * application is uninstalled so another application that is installed with the same
974 * UID cannot access Accounts. This is called by AccountManager.
975 *
976 * @param uid of application to remove all Account visibility to
977 * @param ua UserAccount that hosts the current Account
978 */
979 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
980 synchronized(ua.cacheLock) {
981 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
982 ua.mVisibleListUidToMockAccountNumbers;
983 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
984 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
985 if(allAccountNumbersList != null) {
986 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
987 new Integer[allAccountNumbersList.size()]);
988 for(int accountNum : allAccountNumbers) {
989 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
990 }
991 }
992 }
993 }
994
995 /**
996 * Removes visible list functionality of a certain Account.
997 * This method is currently called by (1) addAccountExplicitly (as opposed to
998 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
999 *
1000 * @param a the account to clear the visible list functionality for
1001 * @param ua currently UserAccounts profile containing Account
1002 *
1003 * @return true if account previously had visible list functionality
1004 */
1005 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
1006 int mockAccountNum = getMockAccountNumber(a, ua);
1007 if(mockAccountNum < 0) {
1008 return false;
1009 }
1010 synchronized(ua.cacheLock) {
1011 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
1012 ua.mVisibleListUidToMockAccountNumbers;
1013 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
1014
1015 /* Removing mapping from account number to account removes visible list functionality*/
1016 userAcctIdToAcctMap.remove(mockAccountNum);
1017
1018 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
1019 int uidKey = userWlUidToMockAccountNums.keyAt(i);
1020 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
1021 if(allAccountNumbers != null) {
1022 allAccountNumbers.remove(mockAccountNum);
1023 if(allAccountNumbers.isEmpty()) {
1024 userWlUidToMockAccountNums.remove(uidKey);
1025 }
1026 }
1027 }
1028 }
1029 return true;
1030 }
1031
1032 /**
1033 * Sends a direct intent to a package, notifying it of a visible account. This
1034 * method is a helper method of makeAccountVisible.
1035 *
1036 * @param desiredPackage to send Account to
1037 * @param visibleAccount to send to package
1038 */
1039 private void sendNotification(String desiredPackage, Account visibleAccount) {
1040 Intent intent = new Intent();
1041 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
1042 intent.setAction(NEW_ACCOUNT_VISIBLE);
1043 intent.setPackage(desiredPackage);
1044 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
1045 mContext.sendBroadcast(intent);
1046 }
1047
1048 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -07001049 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1050 throws RemoteException {
1051 try {
1052 return super.onTransact(code, data, reply, flags);
1053 } catch (RuntimeException e) {
1054 // The account manager only throws security exceptions, so let's
1055 // log all others.
1056 if (!(e instanceof SecurityException)) {
1057 Slog.wtf(TAG, "Account Manager Crash", e);
1058 }
1059 throw e;
1060 }
1061 }
1062
Kenny Root26ff6622012-07-30 12:58:03 -07001063 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -07001064 }
1065
Amith Yamasani258848d2012-08-10 17:06:33 -07001066 private UserManager getUserManager() {
1067 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001068 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -07001069 }
1070 return mUserManager;
1071 }
1072
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001073 /**
1074 * Validate internal set of accounts against installed authenticators for
1075 * given user. Clears cached authenticators before validating.
1076 */
1077 public void validateAccounts(int userId) {
1078 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001079 // Invalidate user-specific cache to make sure we catch any
1080 // removed authenticators.
1081 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
1082 }
1083
1084 /**
1085 * Validate internal set of accounts against installed authenticators for
1086 * given user. Clear cached authenticators before validating when requested.
1087 */
1088 private void validateAccountsInternal(
1089 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001090 if (Log.isLoggable(TAG, Log.DEBUG)) {
1091 Log.d(TAG, "validateAccountsInternal " + accounts.userId
1092 + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001093 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001094 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001095
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001096 if (invalidateAuthenticatorCache) {
1097 mAuthenticatorCache.invalidateCache(accounts.userId);
1098 }
1099
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001100 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1101 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001102 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001103
Amith Yamasani04e0d262012-02-14 11:50:53 -08001104 synchronized (accounts.cacheLock) {
1105 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001106 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001107
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001108 // Get a map of stored authenticator types to UID
1109 Map<String, Integer> metaAuthUid = AccountsDbUtils.findMetaAuthUid(db);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001110 // Create a list of authenticator type whose previous uid no longer exists
1111 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001112 SparseBooleanArray knownUids = null;
1113 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1114 String type = authToUidEntry.getKey();
1115 int uid = authToUidEntry.getValue();
1116 Integer knownUid = knownAuth.get(type);
1117 if (knownUid != null && uid == knownUid) {
1118 // Remove it from the knownAuth list if it's unchanged.
1119 knownAuth.remove(type);
1120 } else {
1121 /*
1122 * The authenticator is presently not cached and should only be triggered
1123 * when we think an authenticator has been removed (or is being updated).
1124 * But we still want to check if any data with the associated uid is
1125 * around. This is an (imperfect) signal that the package may be updating.
1126 *
1127 * A side effect of this is that an authenticator sharing a uid with
1128 * multiple apps won't get its credentials wiped as long as some app with
1129 * that uid is still on the device. But I suspect that this is a rare case.
1130 * And it isn't clear to me how an attacker could really exploit that
1131 * feature.
1132 *
1133 * The upshot is that we don't have to worry about accounts getting
1134 * uninstalled while the authenticator's package is being updated.
1135 *
1136 */
1137 if (knownUids == null) {
1138 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001139 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001140 if (!knownUids.get(uid)) {
1141 // The authenticator is not presently available to the cache. And the
1142 // package no longer has a data directory (so we surmise it isn't updating).
1143 // So purge its data from the account databases.
1144 obsoleteAuthType.add(type);
1145 // And delete it from the TABLE_META
1146 AccountsDbUtils.deleteMetaByAuthTypeAndUid(db, type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001147 }
1148 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001149 }
1150
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001151 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001152 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001153 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001154 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
1155 AccountsDbUtils.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
1156 entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001157 }
1158
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001159 final Map<Long, Account> accountsMap = AccountsDbUtils.findAllAccounts(db);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001160 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001161 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001162 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001163 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1164 final long accountId = accountEntry.getKey();
1165 final Account account = accountEntry.getValue();
1166 if (obsoleteAuthType.contains(account.type)) {
1167 Slog.w(TAG, "deleting account " + account.name + " because type "
1168 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001169 db.beginTransaction();
1170 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001171 AccountsDbUtils.deleteAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001172 // Also delete from CE table if user is unlocked; if user is currently
1173 // locked the account will be removed later by syncDeCeAccountsLocked
1174 if (userUnlocked) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001175 AccountsDbUtils.deleteCeAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001176 }
1177 db.setTransactionSuccessful();
1178 } finally {
1179 db.endTransaction();
1180 }
Fred Quintana56285a62010-12-02 14:20:51 -08001181 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001182
1183 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
1184 accountId, accounts);
1185
Amith Yamasani04e0d262012-02-14 11:50:53 -08001186 accounts.userDataCache.remove(account);
1187 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001188 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001189 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001190 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001191 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001192 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001193 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001194 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001195 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001196 }
1197 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001198 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001199 final String accountType = cur.getKey();
1200 final ArrayList<String> accountNames = cur.getValue();
1201 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001202 for (int i = 0; i < accountsForType.length; i++) {
Svet Ganovf6d424f12016-09-20 20:18:53 -07001203 accountsForType[i] = new Account(accountNames.get(i), accountType,
1204 new AccountAccessTracker());
Fred Quintana56285a62010-12-02 14:20:51 -08001205 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001206 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001207 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001208 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001209 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001210 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001211 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001212 }
1213 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001214 }
1215
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001216 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1217 // Get the UIDs of all apps that might have data on the device. We want
1218 // to preserve user data if the app might otherwise be storing data.
1219 List<PackageInfo> pkgsWithData =
1220 mPackageManager.getInstalledPackagesAsUser(
1221 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1222 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1223 for (PackageInfo pkgInfo : pkgsWithData) {
1224 if (pkgInfo.applicationInfo != null
1225 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1226 knownUids.put(pkgInfo.applicationInfo.uid, true);
1227 }
1228 }
1229 return knownUids;
1230 }
1231
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001232 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1233 Context context,
1234 int userId) {
1235 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001236 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1237 }
1238
1239 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1240 IAccountAuthenticatorCache authCache,
1241 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001242 HashMap<String, Integer> knownAuth = new HashMap<>();
1243 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1244 .getAllServices(userId)) {
1245 knownAuth.put(service.type.type, service.uid);
1246 }
1247 return knownAuth;
1248 }
1249
Amith Yamasani04e0d262012-02-14 11:50:53 -08001250 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001251 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001252 }
1253
1254 protected UserAccounts getUserAccounts(int userId) {
1255 synchronized (mUsers) {
1256 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001257 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001258 if (accounts == null) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001259 File preNDbFile = new File(getPreNDatabaseName(userId));
1260 File deDbFile = new File(getDeDatabaseName(userId));
1261 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001262 initializeDebugDbSizeAndCompileSqlStatementForLogging(
1263 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001264 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001265 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001266 validateAccounts = true;
1267 }
1268 // open CE database if necessary
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001269 if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001270 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1271 synchronized (accounts.cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001272 File preNDatabaseFile = new File(getPreNDatabaseName(userId));
1273 File ceDatabaseFile = new File(getCeDatabaseName(userId));
1274 CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
1275 accounts.openHelper.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001276 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001277 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001278 }
1279 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001280 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001281 }
1282 return accounts;
1283 }
1284 }
1285
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001286 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1287 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
1288 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001289 List<Account> accountsToRemove = AccountsDbUtils.findCeAccountsNotInDe(db);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001290 if (!accountsToRemove.isEmpty()) {
1291 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1292 + accounts.userId + " was locked. Removing accounts from CE tables");
1293 logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
1294
1295 for (Account account : accountsToRemove) {
1296 removeAccountInternal(accounts, account, Process.myUid());
1297 }
1298 }
1299 }
1300
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001301 private void purgeOldGrantsAll() {
1302 synchronized (mUsers) {
1303 for (int i = 0; i < mUsers.size(); i++) {
1304 purgeOldGrants(mUsers.valueAt(i));
1305 }
1306 }
1307 }
1308
1309 private void purgeOldGrants(UserAccounts accounts) {
1310 synchronized (accounts.cacheLock) {
1311 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001312 List<Integer> uids = AccountsDbUtils.findAllUidGrants(db);
1313 for (int uid : uids) {
1314 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1315 if (packageExists) {
1316 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001317 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001318 Log.d(TAG, "deleting grants for UID " + uid
1319 + " because its package is no longer installed");
1320 AccountsDbUtils.deleteGrantsByUid(db, uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001321 }
1322 }
1323 }
1324
Amith Yamasani13593602012-03-22 16:16:17 -07001325 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001326 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001327 if (userId < 1) return;
1328
1329 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001330 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001331 synchronized (mUsers) {
1332 accounts = mUsers.get(userId);
1333 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001334 userUnlocked = mLocalUnlockedUsers.get(userId);
1335 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001336 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001337 if (accounts != null) {
1338 synchronized (accounts.cacheLock) {
1339 accounts.openHelper.close();
1340 }
Amith Yamasani13593602012-03-22 16:16:17 -07001341 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001342 Log.i(TAG, "Removing database files for user " + userId);
1343 File dbFile = new File(getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001344
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001345 deleteDbFileWarnIfFailed(dbFile);
1346 // Remove CE file if user is unlocked, or FBE is not enabled
1347 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1348 if (!fbeEnabled || userUnlocked) {
1349 File ceDb = new File(getCeDatabaseName(userId));
1350 if (ceDb.exists()) {
1351 deleteDbFileWarnIfFailed(ceDb);
1352 }
1353 }
1354 }
1355
1356 private static void deleteDbFileWarnIfFailed(File dbFile) {
1357 if (!SQLiteDatabase.deleteDatabase(dbFile)) {
1358 Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
Amith Yamasani13593602012-03-22 16:16:17 -07001359 }
1360 }
1361
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001362 @VisibleForTesting
1363 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001364 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1365 }
1366
1367 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001368 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1369 Log.v(TAG, "onUserUnlocked " + userId);
1370 }
1371 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001372 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001373 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001374 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001375 syncSharedAccounts(userId);
1376 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001377
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001378 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001379 // Check if there's a shared account that needs to be created as an account
1380 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1381 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001382 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001383 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001384 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001385 : UserHandle.USER_SYSTEM;
1386 if (parentUserId < 0) {
1387 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1388 return;
1389 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001390 for (Account sa : sharedAccounts) {
1391 if (ArrayUtils.contains(accounts, sa)) continue;
1392 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001393 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001394 }
1395 }
1396
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001397 @Override
1398 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001399 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001400 }
1401
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001402 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001403 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001404 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001405 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1406 Log.v(TAG, "getPassword: " + account
1407 + ", caller's uid " + Binder.getCallingUid()
1408 + ", pid " + Binder.getCallingPid());
1409 }
Fred Quintana382601f2010-03-25 12:25:10 -07001410 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001411 int userId = UserHandle.getCallingUserId();
1412 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001413 String msg = String.format(
1414 "uid %s cannot get secrets for accounts of type: %s",
1415 callingUid,
1416 account.type);
1417 throw new SecurityException(msg);
1418 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001419 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001420 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001421 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001422 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001423 } finally {
1424 restoreCallingIdentity(identityToken);
1425 }
1426 }
1427
Amith Yamasani04e0d262012-02-14 11:50:53 -08001428 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001429 if (account == null) {
1430 return null;
1431 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001432 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001433 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1434 return null;
1435 }
Fred Quintana31957f12009-10-21 13:43:10 -07001436
Amith Yamasani04e0d262012-02-14 11:50:53 -08001437 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001438 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001439 return AccountsDbUtils.findAccountPasswordByNameAndType(db, account.name,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001440 account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001441 }
1442 }
1443
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001444 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001445 public String getPreviousName(Account account) {
1446 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1447 Log.v(TAG, "getPreviousName: " + account
1448 + ", caller's uid " + Binder.getCallingUid()
1449 + ", pid " + Binder.getCallingPid());
1450 }
1451 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001452 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001453 long identityToken = clearCallingIdentity();
1454 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001455 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001456 return readPreviousNameInternal(accounts, account);
1457 } finally {
1458 restoreCallingIdentity(identityToken);
1459 }
1460 }
1461
1462 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1463 if (account == null) {
1464 return null;
1465 }
1466 synchronized (accounts.cacheLock) {
1467 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1468 if (previousNameRef == null) {
1469 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001470 String previousName = AccountsDbUtils.findAccountPreviousName(db, account);
1471 previousNameRef = new AtomicReference<>(previousName);
1472 accounts.previousNameCache.put(account, previousNameRef);
1473 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001474 } else {
1475 return previousNameRef.get();
1476 }
1477 }
1478 }
1479
1480 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001481 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001482 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001483 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001484 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1485 account, key, callingUid, Binder.getCallingPid());
1486 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001487 }
Fred Quintana382601f2010-03-25 12:25:10 -07001488 if (account == null) throw new IllegalArgumentException("account is null");
1489 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001490 int userId = UserHandle.getCallingUserId();
1491 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001492 String msg = String.format(
1493 "uid %s cannot get user data for accounts of type: %s",
1494 callingUid,
1495 account.type);
1496 throw new SecurityException(msg);
1497 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001498 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001499 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1500 return null;
1501 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001502 long identityToken = clearCallingIdentity();
1503 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001504 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001505 synchronized (accounts.cacheLock) {
1506 if (!accountExistsCacheLocked(accounts, account)) {
1507 return null;
1508 }
1509 return readUserDataInternalLocked(accounts, account, key);
1510 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001511 } finally {
1512 restoreCallingIdentity(identityToken);
1513 }
1514 }
1515
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001516 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001517 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001518 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001519 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1520 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001521 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001522 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001523 + ", pid " + Binder.getCallingPid());
1524 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001525 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001526 if (isCrossUser(callingUid, userId)) {
1527 throw new SecurityException(
1528 String.format(
1529 "User %s tying to get authenticator types for %s" ,
1530 UserHandle.getCallingUserId(),
1531 userId));
1532 }
1533
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001534 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001535 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001536 return getAuthenticatorTypesInternal(userId);
1537
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001538 } finally {
1539 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001540 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001541 }
1542
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001543 /**
1544 * Should only be called inside of a clearCallingIdentity block.
1545 */
1546 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
Fyodor Kupolov81446482016-08-24 11:27:49 -07001547 mAuthenticatorCache.updateServices(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001548 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1549 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1550 AuthenticatorDescription[] types =
1551 new AuthenticatorDescription[authenticatorCollection.size()];
1552 int i = 0;
1553 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1554 : authenticatorCollection) {
1555 types[i] = authenticator.type;
1556 i++;
1557 }
1558 return types;
1559 }
1560
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001561 private boolean isCrossUser(int callingUid, int userId) {
1562 return (userId != UserHandle.getCallingUserId()
1563 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001564 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001565 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1566 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001567 }
1568
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001569 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001570 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001571 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001572 // clears the visible list functionality for this account because this method allows
1573 // default account access to all applications for account.
1574
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001575 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001576 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001577 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001578 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001579 + ", pid " + Binder.getCallingPid());
1580 }
Fred Quintana382601f2010-03-25 12:25:10 -07001581 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001582 int userId = UserHandle.getCallingUserId();
1583 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001584 String msg = String.format(
1585 "uid %s cannot explicitly add accounts of type: %s",
1586 callingUid,
1587 account.type);
1588 throw new SecurityException(msg);
1589 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001590 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001591 /*
1592 * Child users are not allowed to add accounts. Only the accounts that are
1593 * shared by the parent profile can be added to child profile.
1594 *
1595 * TODO: Only allow accounts that were shared to be added by
1596 * a limited user.
1597 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001598
Fred Quintana60307342009-03-24 22:48:12 -07001599 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001600 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001601 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001602 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001603 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001604 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001605 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001606 }
1607 }
1608
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001609 @Override
1610 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001611 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001612 int callingUid = Binder.getCallingUid();
1613 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1614 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001615 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001616 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001617 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1618 final UserAccounts toAccounts = getUserAccounts(userTo);
1619 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001620 if (response != null) {
1621 Bundle result = new Bundle();
1622 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1623 try {
1624 response.onResult(result);
1625 } catch (RemoteException e) {
1626 Slog.w(TAG, "Failed to report error back to the client." + e);
1627 }
1628 }
1629 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001630 }
1631
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001632 Slog.d(TAG, "Copying account " + account.name
1633 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001634 long identityToken = clearCallingIdentity();
1635 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001636 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001637 false /* stripAuthTokenFromResult */, account.name,
1638 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001639 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001640 protected String toDebugString(long now) {
1641 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1642 + ", " + account.type;
1643 }
1644
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001645 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001646 public void run() throws RemoteException {
1647 mAuthenticator.getAccountCredentialsForCloning(this, account);
1648 }
1649
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001650 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001651 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001652 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001653 if (result != null
1654 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1655 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001656 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001657 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001658 super.onResult(result);
1659 }
1660 }
1661 }.bind();
1662 } finally {
1663 restoreCallingIdentity(identityToken);
1664 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001665 }
1666
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001667 @Override
1668 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001669 final int callingUid = Binder.getCallingUid();
1670 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1671 String msg = String.format(
1672 "accountAuthenticated( account: %s, callerUid: %s)",
1673 account,
1674 callingUid);
1675 Log.v(TAG, msg);
1676 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001677 if (account == null) {
1678 throw new IllegalArgumentException("account is null");
1679 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001680 int userId = UserHandle.getCallingUserId();
1681 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001682 String msg = String.format(
1683 "uid %s cannot notify authentication for accounts of type: %s",
1684 callingUid,
1685 account.type);
1686 throw new SecurityException(msg);
1687 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001688
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001689 if (!canUserModifyAccounts(userId, callingUid) ||
1690 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001691 return false;
1692 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001693
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001694 long identityToken = clearCallingIdentity();
1695 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001696 UserAccounts accounts = getUserAccounts(userId);
1697 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001698 } finally {
1699 restoreCallingIdentity(identityToken);
1700 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001701 }
1702
1703 private boolean updateLastAuthenticatedTime(Account account) {
1704 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001705 synchronized (accounts.cacheLock) {
1706 final ContentValues values = new ContentValues();
1707 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1708 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1709 int i = db.update(
1710 TABLE_ACCOUNTS,
1711 values,
1712 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1713 new String[] {
1714 account.name, account.type
1715 });
1716 if (i > 0) {
1717 return true;
1718 }
1719 }
1720 return false;
1721 }
1722
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001723 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001724 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1725 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001726 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001727 long id = clearCallingIdentity();
1728 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001729 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001730 false /* stripAuthTokenFromResult */, account.name,
1731 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001732 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001733 protected String toDebugString(long now) {
1734 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1735 + ", " + account.type;
1736 }
1737
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001738 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001739 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001740 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001741 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001742 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001743 for (Account acc : getAccounts(parentUserId,
1744 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001745 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001746 mAuthenticator.addAccountFromCredentials(
1747 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001748 break;
1749 }
1750 }
1751 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001752 }
1753
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001754 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001755 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001756 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001757 // TODO: Anything to do if if succedded?
1758 // TODO: If it failed: Show error notification? Should we remove the shadow
1759 // account to avoid retries?
1760 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001761 }
1762
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001763 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001764 public void onError(int errorCode, String errorMessage) {
1765 super.onError(errorCode, errorMessage);
1766 // TODO: Show error notification to user
1767 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1768 }
1769
1770 }.bind();
1771 } finally {
1772 restoreCallingIdentity(id);
1773 }
1774 }
1775
Amith Yamasani04e0d262012-02-14 11:50:53 -08001776 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001777 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001778 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001779 if (account == null) {
1780 return false;
1781 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001782 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001783 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1784 + " is locked. callingUid=" + callingUid);
1785 return false;
1786 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001787 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001788 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001789 db.beginTransaction();
1790 try {
1791 long numMatches = DatabaseUtils.longForQuery(db,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001792 "select count(*) from " + CE_TABLE_ACCOUNTS
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001793 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1794 new String[]{account.name, account.type});
1795 if (numMatches > 0) {
1796 Log.w(TAG, "insertAccountIntoDatabase: " + account
1797 + ", skipping since the account already exists");
1798 return false;
1799 }
1800 ContentValues values = new ContentValues();
1801 values.put(ACCOUNTS_NAME, account.name);
1802 values.put(ACCOUNTS_TYPE, account.type);
1803 values.put(ACCOUNTS_PASSWORD, password);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001804 long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001805 if (accountId < 0) {
1806 Log.w(TAG, "insertAccountIntoDatabase: " + account
1807 + ", skipping the DB insert failed");
1808 return false;
1809 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001810 // Insert into DE table
1811 values = new ContentValues();
1812 values.put(ACCOUNTS_ID, accountId);
1813 values.put(ACCOUNTS_NAME, account.name);
1814 values.put(ACCOUNTS_TYPE, account.type);
1815 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
1816 System.currentTimeMillis());
1817 if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
1818 Log.w(TAG, "insertAccountIntoDatabase: " + account
1819 + ", skipping the DB insert failed");
1820 return false;
1821 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001822 if (extras != null) {
1823 for (String key : extras.keySet()) {
1824 final String value = extras.getString(key);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001825 if (AccountsDbUtils.insertExtra(db, accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001826 Log.w(TAG, "insertAccountIntoDatabase: " + account
1827 + ", skipping since insertExtra failed for key " + key);
1828 return false;
1829 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001830 }
1831 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001832 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001833
1834 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1835 accounts, callingUid);
1836
Amith Yamasani04e0d262012-02-14 11:50:53 -08001837 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001838 } finally {
1839 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001840 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001841 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001842 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1843 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001844 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001845
1846 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1847 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001848 return true;
1849 }
1850
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001851 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001852 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001853 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001854 }
1855 }
1856
Amith Yamasani5be347b2013-03-31 17:44:31 -07001857 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001858 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001859 * running, then clone the account too.
1860 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001861 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001862 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001863 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001864 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001865 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001866 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001867 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001868 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001869 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001870 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001871 }
1872 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001873 }
1874 }
1875
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001876 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001877 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001878 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001879 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001880 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1881 Log.v(TAG, "hasFeatures: " + account
1882 + ", response " + response
1883 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001884 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001885 + ", pid " + Binder.getCallingPid());
1886 }
Fred Quintana382601f2010-03-25 12:25:10 -07001887 if (response == null) throw new IllegalArgumentException("response is null");
1888 if (account == null) throw new IllegalArgumentException("account is null");
1889 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001890 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001891 checkReadAccountsPermitted(callingUid, account.type, userId,
1892 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001893
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001894 long identityToken = clearCallingIdentity();
1895 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001896 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001897 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001898 } finally {
1899 restoreCallingIdentity(identityToken);
1900 }
1901 }
1902
1903 private class TestFeaturesSession extends Session {
1904 private final String[] mFeatures;
1905 private final Account mAccount;
1906
Amith Yamasani04e0d262012-02-14 11:50:53 -08001907 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001908 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001909 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001910 true /* stripAuthTokenFromResult */, account.name,
1911 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001912 mFeatures = features;
1913 mAccount = account;
1914 }
1915
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001916 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001917 public void run() throws RemoteException {
1918 try {
1919 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1920 } catch (RemoteException e) {
1921 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1922 }
1923 }
1924
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001925 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001926 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001927 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001928 IAccountManagerResponse response = getResponseAndClose();
1929 if (response != null) {
1930 try {
1931 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001932 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001933 return;
1934 }
Fred Quintana56285a62010-12-02 14:20:51 -08001935 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1936 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1937 + response);
1938 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001939 final Bundle newResult = new Bundle();
1940 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1941 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1942 response.onResult(newResult);
1943 } catch (RemoteException e) {
1944 // if the caller is dead then there is no one to care about remote exceptions
1945 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1946 Log.v(TAG, "failure while notifying response", e);
1947 }
1948 }
1949 }
1950 }
1951
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001952 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001953 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001954 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001955 + ", " + mAccount
1956 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1957 }
1958 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001959
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001960 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001961 public void renameAccount(
1962 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001963 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001964 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1965 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001966 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001967 + ", pid " + Binder.getCallingPid());
1968 }
1969 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001970 int userId = UserHandle.getCallingUserId();
1971 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001972 String msg = String.format(
1973 "uid %s cannot rename accounts of type: %s",
1974 callingUid,
1975 accountToRename.type);
1976 throw new SecurityException(msg);
1977 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001978 long identityToken = clearCallingIdentity();
1979 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001980 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001981 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001982 Bundle result = new Bundle();
1983 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1984 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
Svet Ganovf6d424f12016-09-20 20:18:53 -07001985 result.putBinder(AccountManager.KEY_ACCOUNT_ACCESS_TRACKER,
1986 resultingAccount.getAccessTracker().asBinder());
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001987 try {
1988 response.onResult(result);
1989 } catch (RemoteException e) {
1990 Log.w(TAG, e.getMessage());
1991 }
1992 } finally {
1993 restoreCallingIdentity(identityToken);
1994 }
1995 }
1996
1997 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001998 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001999 Account resultAccount = null;
2000 /*
2001 * Cancel existing notifications. Let authenticators
2002 * re-post notifications as required. But we don't know if
2003 * the authenticators have bound their notifications to
2004 * now stale account name data.
2005 *
2006 * With a rename api, we might not need to do this anymore but it
2007 * shouldn't hurt.
2008 */
2009 cancelNotification(
2010 getSigninRequiredNotificationId(accounts, accountToRename),
2011 new UserHandle(accounts.userId));
2012 synchronized(accounts.credentialsPermissionNotificationIds) {
2013 for (Pair<Pair<Account, String>, Integer> pair:
2014 accounts.credentialsPermissionNotificationIds.keySet()) {
2015 if (accountToRename.equals(pair.first.first)) {
2016 int id = accounts.credentialsPermissionNotificationIds.get(pair);
2017 cancelNotification(id, new UserHandle(accounts.userId));
2018 }
2019 }
2020 }
2021 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002022 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002023 db.beginTransaction();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002024 Account renamedAccount = new Account(newName, accountToRename.type);
2025 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002026 final long accountId = AccountsDbUtils.findAccountId(db, accountToRename);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002027 if (accountId >= 0) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002028 final ContentValues values = new ContentValues();
2029 values.put(ACCOUNTS_NAME, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002030 final String[] argsAccountId = { String.valueOf(accountId) };
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002031 db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2032 // Update NAME/PREVIOUS_NAME in DE accounts table
2033 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002034 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2035 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002036 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
2037 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002038 }
2039 } finally {
2040 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002041 }
2042 /*
2043 * Database transaction was successful. Clean up cached
2044 * data associated with the account in the user profile.
2045 */
2046 insertAccountIntoCacheLocked(accounts, renamedAccount);
2047 /*
2048 * Extract the data and token caches before removing the
2049 * old account to preserve the user data associated with
2050 * the account.
2051 */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002052 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
2053 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002054 removeAccountFromCacheLocked(accounts, accountToRename);
2055 /*
2056 * Update the cached data associated with the renamed
2057 * account.
2058 */
2059 accounts.userDataCache.put(renamedAccount, tmpData);
2060 accounts.authTokenCache.put(renamedAccount, tmpTokens);
2061 accounts.previousNameCache.put(
2062 renamedAccount,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002063 new AtomicReference<>(accountToRename.name));
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002064 resultAccount = renamedAccount;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002065
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002066 int parentUserId = accounts.userId;
2067 if (canHaveProfile(parentUserId)) {
2068 /*
2069 * Owner or system user account was renamed, rename the account for
2070 * those users with which the account was shared.
2071 */
2072 List<UserInfo> users = getUserManager().getUsers(true);
2073 for (UserInfo user : users) {
2074 if (user.isRestricted()
2075 && (user.restrictedProfileParentId == parentUserId)) {
2076 renameSharedAccountAsUser(accountToRename, newName, user.id);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002077 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002078 }
2079 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002080 sendAccountsChangedBroadcast(accounts.userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002081 }
2082 return resultAccount;
2083 }
2084
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002085 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002086 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002087 return userInfo != null && userInfo.canHaveProfile();
2088 }
2089
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002090 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002091 public void removeAccount(IAccountManagerResponse response, Account account,
2092 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002093 removeAccountAsUser(
2094 response,
2095 account,
2096 expectActivityLaunch,
2097 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002098 }
2099
2100 @Override
2101 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002102 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002103 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002104 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2105 Log.v(TAG, "removeAccount: " + account
2106 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002107 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002108 + ", pid " + Binder.getCallingPid()
2109 + ", for user id " + userId);
2110 }
2111 if (response == null) throw new IllegalArgumentException("response is null");
2112 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002113 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002114 if (isCrossUser(callingUid, userId)) {
2115 throw new SecurityException(
2116 String.format(
2117 "User %s tying remove account for %s" ,
2118 UserHandle.getCallingUserId(),
2119 userId));
2120 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002121 /*
2122 * Only the system or authenticator should be allowed to remove accounts for that
2123 * authenticator. This will let users remove accounts (via Settings in the system) but not
2124 * arbitrary applications (like competing authenticators).
2125 */
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002126 UserHandle user = UserHandle.of(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00002127 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
2128 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002129 String msg = String.format(
2130 "uid %s cannot remove accounts of type: %s",
2131 callingUid,
2132 account.type);
2133 throw new SecurityException(msg);
2134 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002135 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002136 try {
2137 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2138 "User cannot modify accounts");
2139 } catch (RemoteException re) {
2140 }
2141 return;
2142 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002143 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002144 try {
2145 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2146 "User cannot modify accounts of this type (policy).");
2147 } catch (RemoteException re) {
2148 }
2149 return;
2150 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002151 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002152 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002153 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002154 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002155 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08002156 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002157 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002158 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002159 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002160 }
2161 }
2162 }
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002163 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002164 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002165 logRecord(
2166 db,
2167 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2168 TABLE_ACCOUNTS,
2169 accountId,
2170 accounts,
2171 callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002172 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002173 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2174 } finally {
2175 restoreCallingIdentity(identityToken);
2176 }
2177 }
2178
2179 @Override
2180 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002181 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002182 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2183 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002184 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002185 + ", pid " + Binder.getCallingPid());
2186 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002187 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002188 if (account == null) {
2189 /*
2190 * Null accounts should result in returning false, as per
2191 * AccountManage.addAccountExplicitly(...) java doc.
2192 */
2193 Log.e(TAG, "account is null");
2194 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002195 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002196 String msg = String.format(
2197 "uid %s cannot explicitly add accounts of type: %s",
2198 callingUid,
2199 account.type);
2200 throw new SecurityException(msg);
2201 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07002202 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002203 UserAccounts accounts = getUserAccountsForCaller();
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002204 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002205 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002206 logRecord(
2207 db,
2208 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2209 TABLE_ACCOUNTS,
2210 accountId,
2211 accounts,
2212 callingUid);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002213 long identityToken = clearCallingIdentity();
2214 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002215 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002216 } finally {
2217 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07002218 }
Fred Quintana60307342009-03-24 22:48:12 -07002219 }
2220
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002221 private class RemoveAccountSession extends Session {
2222 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002223 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002224 Account account, boolean expectActivityLaunch) {
2225 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002226 true /* stripAuthTokenFromResult */, account.name,
2227 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002228 mAccount = account;
2229 }
2230
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002231 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002232 protected String toDebugString(long now) {
2233 return super.toDebugString(now) + ", removeAccount"
2234 + ", account " + mAccount;
2235 }
2236
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002237 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002238 public void run() throws RemoteException {
2239 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2240 }
2241
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002242 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002243 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002244 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002245 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2246 && !result.containsKey(AccountManager.KEY_INTENT)) {
2247 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002248 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002249 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002250 }
2251 IAccountManagerResponse response = getResponseAndClose();
2252 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002253 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2254 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2255 + response);
2256 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002257 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002258 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002259 try {
2260 response.onResult(result2);
2261 } catch (RemoteException e) {
2262 // ignore
2263 }
2264 }
2265 }
2266 super.onResult(result);
2267 }
2268 }
2269
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07002270 @VisibleForTesting
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002271 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002272 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08002273 }
2274
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002275 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002276 boolean isChanged = false;
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002277 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002278 if (!userUnlocked) {
2279 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
2280 + " is still locked. CE data will be removed later");
2281 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002282 synchronized (accounts.cacheLock) {
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002283 final SQLiteDatabase db = userUnlocked
2284 ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
2285 : accounts.openHelper.getWritableDatabase();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002286 db.beginTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002287 // Set to a dummy value, this will only be used if the database
2288 // transaction succeeds.
2289 long accountId = -1;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002290 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002291 accountId = AccountsDbUtils.findAccountId(db, account);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002292 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002293 AccountsDbUtils.deleteAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002294 if (userUnlocked) {
2295 // Delete from CE table
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002296 AccountsDbUtils.deleteCeAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002297 }
2298 db.setTransactionSuccessful();
2299 isChanged = true;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002300 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002301 } finally {
2302 db.endTransaction();
2303 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002304 if (isChanged) {
2305 removeAccountFromCacheLocked(accounts, account);
2306 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
2307 sendAccountsChangedBroadcast(accounts.userId);
2308 String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
2309 : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
2310 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
2311 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002312 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002313 long id = Binder.clearCallingIdentity();
2314 try {
2315 int parentUserId = accounts.userId;
2316 if (canHaveProfile(parentUserId)) {
2317 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002318 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002319 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002320 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002321 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002322 }
2323 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002324 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002325 } finally {
2326 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002327 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002328
2329 if (isChanged) {
2330 synchronized (accounts.credentialsPermissionNotificationIds) {
2331 for (Pair<Pair<Account, String>, Integer> key
2332 : accounts.credentialsPermissionNotificationIds.keySet()) {
2333 if (account.equals(key.first.first)
Svet Ganovf6d424f12016-09-20 20:18:53 -07002334 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002335 final int uid = (Integer) key.second;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07002336 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002337 account, uid, false));
2338 }
2339 }
2340 }
2341 }
2342
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002343 return isChanged;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002344 }
2345
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002346 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002347 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002348 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002349 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2350 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07002351 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08002352 + ", pid " + Binder.getCallingPid());
2353 }
Fred Quintana382601f2010-03-25 12:25:10 -07002354 if (accountType == null) throw new IllegalArgumentException("accountType is null");
2355 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002356 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002357 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002358 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002359 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002360 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002361 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002362 db.beginTransaction();
2363 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002364 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002365 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002366 db.setTransactionSuccessful();
2367 } finally {
2368 db.endTransaction();
2369 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002370 }
Fred Quintana60307342009-03-24 22:48:12 -07002371 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002372 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002373 }
2374 }
2375
Carlos Valdivia91979be2015-05-22 14:11:35 -07002376 private void invalidateCustomTokenLocked(
2377 UserAccounts accounts,
2378 String accountType,
2379 String authToken) {
2380 if (authToken == null || accountType == null) {
2381 return;
2382 }
2383 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002384 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002385 }
2386
Amith Yamasani04e0d262012-02-14 11:50:53 -08002387 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
2388 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002389 if (authToken == null || accountType == null) {
2390 return;
2391 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002392 Cursor cursor = AccountsDbUtils.findAuthtokenForAllAccounts(db, accountType, authToken);
Fred Quintana33269202009-04-20 16:05:10 -07002393 try {
2394 while (cursor.moveToNext()) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002395 String authTokenId = cursor.getString(0);
Fred Quintana33269202009-04-20 16:05:10 -07002396 String accountName = cursor.getString(1);
2397 String authTokenType = cursor.getString(2);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002398 AccountsDbUtils.deleteAuthToken(db, authTokenId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002399 writeAuthTokenIntoCacheLocked(
2400 accounts,
2401 db,
2402 new Account(accountName, accountType),
2403 authTokenType,
2404 null);
Fred Quintana60307342009-03-24 22:48:12 -07002405 }
Fred Quintana33269202009-04-20 16:05:10 -07002406 } finally {
2407 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07002408 }
2409 }
2410
Carlos Valdivia91979be2015-05-22 14:11:35 -07002411 private void saveCachedToken(
2412 UserAccounts accounts,
2413 Account account,
2414 String callerPkg,
2415 byte[] callerSigDigest,
2416 String tokenType,
2417 String token,
2418 long expiryMillis) {
2419
2420 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2421 return;
2422 }
2423 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002424 UserHandle.of(accounts.userId));
Carlos Valdivia91979be2015-05-22 14:11:35 -07002425 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002426 accounts.accountTokenCaches.put(
2427 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002428 }
2429 }
2430
Amith Yamasani04e0d262012-02-14 11:50:53 -08002431 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2432 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07002433 if (account == null || type == null) {
2434 return false;
2435 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002436 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002437 UserHandle.of(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002438 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002439 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002440 db.beginTransaction();
2441 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002442 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002443 if (accountId < 0) {
2444 return false;
2445 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002446 AccountsDbUtils.deleteAuthtokensByAccountIdAndType(db, accountId, type);
2447 if (AccountsDbUtils.insertAuthToken(db, accountId, type, authToken) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002448 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002449 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002450 return true;
2451 }
Fred Quintana33269202009-04-20 16:05:10 -07002452 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002453 } finally {
2454 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07002455 }
Fred Quintana60307342009-03-24 22:48:12 -07002456 }
2457 }
2458
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002459 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002460 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002461 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002462 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2463 Log.v(TAG, "peekAuthToken: " + account
2464 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002465 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002466 + ", pid " + Binder.getCallingPid());
2467 }
Fred Quintana382601f2010-03-25 12:25:10 -07002468 if (account == null) throw new IllegalArgumentException("account is null");
2469 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002470 int userId = UserHandle.getCallingUserId();
2471 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002472 String msg = String.format(
2473 "uid %s cannot peek the authtokens associated with accounts of type: %s",
2474 callingUid,
2475 account.type);
2476 throw new SecurityException(msg);
2477 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002478 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07002479 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2480 + callingUid);
2481 return null;
2482 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002483 long identityToken = clearCallingIdentity();
2484 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002485 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002486 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002487 } finally {
2488 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002489 }
Fred Quintana60307342009-03-24 22:48:12 -07002490 }
2491
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002492 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002493 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002494 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002495 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2496 Log.v(TAG, "setAuthToken: " + account
2497 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002498 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002499 + ", pid " + Binder.getCallingPid());
2500 }
Fred Quintana382601f2010-03-25 12:25:10 -07002501 if (account == null) throw new IllegalArgumentException("account is null");
2502 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002503 int userId = UserHandle.getCallingUserId();
2504 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002505 String msg = String.format(
2506 "uid %s cannot set auth tokens associated with accounts of type: %s",
2507 callingUid,
2508 account.type);
2509 throw new SecurityException(msg);
2510 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002511 long identityToken = clearCallingIdentity();
2512 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002513 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002514 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002515 } finally {
2516 restoreCallingIdentity(identityToken);
2517 }
Fred Quintana60307342009-03-24 22:48:12 -07002518 }
2519
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002520 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002521 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002522 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002523 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2524 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002525 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002526 + ", pid " + Binder.getCallingPid());
2527 }
Fred Quintana382601f2010-03-25 12:25:10 -07002528 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002529 int userId = UserHandle.getCallingUserId();
2530 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002531 String msg = String.format(
2532 "uid %s cannot set secrets for accounts of type: %s",
2533 callingUid,
2534 account.type);
2535 throw new SecurityException(msg);
2536 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002537 long identityToken = clearCallingIdentity();
2538 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002539 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002540 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002541 } finally {
2542 restoreCallingIdentity(identityToken);
2543 }
Fred Quintana60307342009-03-24 22:48:12 -07002544 }
2545
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002546 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2547 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07002548 if (account == null) {
2549 return;
2550 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002551 boolean isChanged = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002552 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002553 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002554 db.beginTransaction();
2555 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002556 final long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002557 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002558 AccountsDbUtils.updateAccountPassword(db, accountId, password);
2559 AccountsDbUtils.deleteAuthTokensByAccountId(db, accountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002560 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002561 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002562 db.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002563 // If there is an account whose password will be updated and the database
2564 // transactions succeed, then we say that a change has occured. Even if the
2565 // new password is the same as the old and there were no authtokens to delete.
2566 isChanged = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002567 String action = (password == null || password.length() == 0) ?
2568 DebugDbHelper.ACTION_CLEAR_PASSWORD
2569 : DebugDbHelper.ACTION_SET_PASSWORD;
2570 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08002571 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002572 } finally {
2573 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002574 if (isChanged) {
2575 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2576 sendAccountsChangedBroadcast(accounts.userId);
2577 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002578 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002579 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07002580 }
2581
Amith Yamasani04e0d262012-02-14 11:50:53 -08002582 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08002583 Log.i(TAG, "the accounts changed, sending broadcast of "
2584 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07002585 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07002586 }
2587
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002588 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002589 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002590 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002591 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2592 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002593 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002594 + ", pid " + Binder.getCallingPid());
2595 }
Fred Quintana382601f2010-03-25 12:25:10 -07002596 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002597 int userId = UserHandle.getCallingUserId();
2598 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002599 String msg = String.format(
2600 "uid %s cannot clear passwords for accounts of type: %s",
2601 callingUid,
2602 account.type);
2603 throw new SecurityException(msg);
2604 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002605 long identityToken = clearCallingIdentity();
2606 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002607 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002608 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002609 } finally {
2610 restoreCallingIdentity(identityToken);
2611 }
Fred Quintana60307342009-03-24 22:48:12 -07002612 }
2613
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002614 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002615 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002616 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002617 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2618 Log.v(TAG, "setUserData: " + account
2619 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002620 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002621 + ", pid " + Binder.getCallingPid());
2622 }
Fred Quintana382601f2010-03-25 12:25:10 -07002623 if (key == null) throw new IllegalArgumentException("key is null");
2624 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002625 int userId = UserHandle.getCallingUserId();
2626 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002627 String msg = String.format(
2628 "uid %s cannot set user data for accounts of type: %s",
2629 callingUid,
2630 account.type);
2631 throw new SecurityException(msg);
2632 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002633 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002634 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002635 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002636 synchronized (accounts.cacheLock) {
2637 if (!accountExistsCacheLocked(accounts, account)) {
2638 return;
2639 }
2640 setUserdataInternalLocked(accounts, account, key, value);
2641 }
Fred Quintana60307342009-03-24 22:48:12 -07002642 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002643 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002644 }
2645 }
2646
Simranjit Kohli858511c2016-03-10 18:36:11 +00002647 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2648 if (accounts.accountCache.containsKey(account.type)) {
2649 for (Account acc : accounts.accountCache.get(account.type)) {
2650 if (acc.name.equals(account.name)) {
2651 return true;
2652 }
2653 }
2654 }
2655 return false;
2656 }
2657
2658 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002659 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07002660 if (account == null || key == null) {
2661 return;
2662 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002663 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2664 db.beginTransaction();
2665 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002666 long accountId = AccountsDbUtils.findAccountId(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002667 if (accountId < 0) {
2668 return;
2669 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002670 long extrasId = AccountsDbUtils.findExtrasIdByAccountId(db, accountId, key);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002671 if (extrasId < 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002672 extrasId = AccountsDbUtils.insertExtra(db, accountId, key, value);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002673 if (extrasId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002674 return;
2675 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002676 } else if (!AccountsDbUtils.updateExtra(db, extrasId, value)) {
2677 return;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002678 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002679 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
2680 db.setTransactionSuccessful();
2681 } finally {
2682 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002683 }
2684 }
2685
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002686 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08002687 if (result == null) {
2688 Log.e(TAG, "the result is unexpectedly null", new Exception());
2689 }
2690 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2691 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2692 + response);
2693 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002694 try {
2695 response.onResult(result);
2696 } catch (RemoteException e) {
2697 // if the caller is dead then there is no one to care about remote
2698 // exceptions
2699 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2700 Log.v(TAG, "failure while notifying response", e);
2701 }
2702 }
2703 }
2704
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002705 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07002706 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2707 final String authTokenType)
2708 throws RemoteException {
2709 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08002710 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2711
Fred Quintanad9640ec2012-05-23 12:37:00 -07002712 final int callingUid = getCallingUid();
2713 clearCallingIdentity();
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07002714 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07002715 throw new SecurityException("can only call from system");
2716 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002717 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002718 long identityToken = clearCallingIdentity();
2719 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002720 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002721 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2722 false /* stripAuthTokenFromResult */, null /* accountName */,
2723 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002724 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002725 protected String toDebugString(long now) {
2726 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07002727 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08002728 + ", authTokenType " + authTokenType;
2729 }
2730
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002731 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002732 public void run() throws RemoteException {
2733 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2734 }
2735
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002736 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002737 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002738 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002739 if (result != null) {
2740 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2741 Bundle bundle = new Bundle();
2742 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2743 super.onResult(bundle);
2744 return;
2745 } else {
2746 super.onResult(result);
2747 }
2748 }
2749 }.bind();
2750 } finally {
2751 restoreCallingIdentity(identityToken);
2752 }
2753 }
2754
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002755 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07002756 public void getAuthToken(
2757 IAccountManagerResponse response,
2758 final Account account,
2759 final String authTokenType,
2760 final boolean notifyOnAuthFailure,
2761 final boolean expectActivityLaunch,
2762 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002763 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002764 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2765 Log.v(TAG, "getAuthToken: " + account
2766 + ", response " + response
2767 + ", authTokenType " + authTokenType
2768 + ", notifyOnAuthFailure " + notifyOnAuthFailure
2769 + ", expectActivityLaunch " + expectActivityLaunch
2770 + ", caller's uid " + Binder.getCallingUid()
2771 + ", pid " + Binder.getCallingPid());
2772 }
Fred Quintana382601f2010-03-25 12:25:10 -07002773 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002774 try {
2775 if (account == null) {
2776 Slog.w(TAG, "getAuthToken called with null account");
2777 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2778 return;
2779 }
2780 if (authTokenType == null) {
2781 Slog.w(TAG, "getAuthToken called with null authTokenType");
2782 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2783 return;
2784 }
2785 } catch (RemoteException e) {
2786 Slog.w(TAG, "Failed to report error back to the client." + e);
2787 return;
2788 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002789 int userId = UserHandle.getCallingUserId();
2790 long ident = Binder.clearCallingIdentity();
2791 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002792 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002793 try {
2794 accounts = getUserAccounts(userId);
2795 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2796 AuthenticatorDescription.newKey(account.type), accounts.userId);
2797 } finally {
2798 Binder.restoreCallingIdentity(ident);
2799 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002800
Costin Manolachea40c6302010-12-13 14:50:45 -08002801 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07002802 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08002803
2804 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002805 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002806 final boolean permissionGranted =
2807 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08002808
Carlos Valdivia91979be2015-05-22 14:11:35 -07002809 // Get the calling package. We will use it for the purpose of caching.
2810 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07002811 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002812 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07002813 try {
2814 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2815 } finally {
2816 Binder.restoreCallingIdentity(ident);
2817 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002818 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2819 String msg = String.format(
2820 "Uid %s is attempting to illegally masquerade as package %s!",
2821 callerUid,
2822 callerPkg);
2823 throw new SecurityException(msg);
2824 }
2825
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002826 // let authenticator know the identity of the caller
2827 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2828 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07002829
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002830 if (notifyOnAuthFailure) {
2831 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08002832 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002833
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002834 long identityToken = clearCallingIdentity();
2835 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07002836 // Distill the caller's package signatures into a single digest.
2837 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2838
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002839 // if the caller has permission, do the peek. otherwise go the more expensive
2840 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08002841 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002842 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002843 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002844 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002845 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2846 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2847 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002848 onResult(response, result);
2849 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002850 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002851 }
2852
Carlos Valdivia91979be2015-05-22 14:11:35 -07002853 if (customTokens) {
2854 /*
2855 * Look up tokens in the new cache only if the loginOptions don't have parameters
2856 * outside of those expected to be injected by the AccountManager, e.g.
2857 * ANDORID_PACKAGE_NAME.
2858 */
2859 String token = readCachedTokenInternal(
2860 accounts,
2861 account,
2862 authTokenType,
2863 callerPkg,
2864 callerPkgSigDigest);
2865 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002866 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2867 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2868 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002869 Bundle result = new Bundle();
2870 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2871 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2872 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2873 onResult(response, result);
2874 return;
2875 }
2876 }
2877
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002878 new Session(
2879 accounts,
2880 response,
2881 account.type,
2882 expectActivityLaunch,
2883 false /* stripAuthTokenFromResult */,
2884 account.name,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002885 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002886 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002887 protected String toDebugString(long now) {
2888 if (loginOptions != null) loginOptions.keySet();
2889 return super.toDebugString(now) + ", getAuthToken"
2890 + ", " + account
2891 + ", authTokenType " + authTokenType
2892 + ", loginOptions " + loginOptions
2893 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2894 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002895
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002896 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002897 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002898 // If the caller doesn't have permission then create and return the
2899 // "grant permission" intent instead of the "getAuthToken" intent.
2900 if (!permissionGranted) {
2901 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2902 } else {
2903 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2904 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002905 }
2906
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002907 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002908 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002909 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002910 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002911 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002912 Intent intent = newGrantCredentialsPermissionIntent(
2913 account,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002914 null,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002915 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002916 new AccountAuthenticatorResponse(this),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002917 authTokenType,
2918 true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002919 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002920 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002921 onResult(bundle);
2922 return;
2923 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002924 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002925 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002926 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2927 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002928 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002929 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002930 "the type and name should not be empty");
2931 return;
2932 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002933 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002934 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002935 saveAuthTokenToDatabase(
2936 mAccounts,
2937 resultAccount,
2938 authTokenType,
2939 authToken);
2940 }
2941 long expiryMillis = result.getLong(
2942 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2943 if (customTokens
2944 && expiryMillis > System.currentTimeMillis()) {
2945 saveCachedToken(
2946 mAccounts,
2947 account,
2948 callerPkg,
2949 callerPkgSigDigest,
2950 authTokenType,
2951 authToken,
2952 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002953 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002954 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002955
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002956 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002957 if (intent != null && notifyOnAuthFailure && !customTokens) {
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002958 /*
2959 * Make sure that the supplied intent is owned by the authenticator
2960 * giving it to the system. Otherwise a malicious authenticator could
2961 * have users launching arbitrary activities by tricking users to
2962 * interact with malicious notifications.
2963 */
2964 checkKeyIntent(
2965 Binder.getCallingUid(),
2966 intent);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002967 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002968 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002969 intent, "android", accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002970 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002971 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002972 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002973 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002974 }.bind();
2975 } finally {
2976 restoreCallingIdentity(identityToken);
2977 }
Fred Quintana60307342009-03-24 22:48:12 -07002978 }
2979
Carlos Valdivia91979be2015-05-22 14:11:35 -07002980 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2981 MessageDigest digester;
2982 try {
2983 digester = MessageDigest.getInstance("SHA-256");
2984 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2985 callerPkg, PackageManager.GET_SIGNATURES);
2986 for (Signature sig : pkgInfo.signatures) {
2987 digester.update(sig.toByteArray());
2988 }
2989 } catch (NoSuchAlgorithmException x) {
2990 Log.wtf(TAG, "SHA-256 should be available", x);
2991 digester = null;
2992 } catch (NameNotFoundException e) {
2993 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2994 digester = null;
2995 }
2996 return (digester == null) ? null : digester.digest();
2997 }
2998
Dianne Hackborn41203752012-08-31 14:05:51 -07002999 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003000 String packageName, int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003001 int uid = intent.getIntExtra(
3002 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
3003 String authTokenType = intent.getStringExtra(
3004 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07003005 final String titleAndSubtitle =
3006 mContext.getString(R.string.permission_request_notification_with_subtitle,
3007 account.name);
3008 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07003009 String title = titleAndSubtitle;
3010 String subtitle = "";
3011 if (index > 0) {
3012 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04003013 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07003014 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07003015 UserHandle user = UserHandle.of(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003016 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04003017 Notification n = new Notification.Builder(contextForUser)
3018 .setSmallIcon(android.R.drawable.stat_sys_warning)
3019 .setWhen(0)
3020 .setColor(contextForUser.getColor(
3021 com.android.internal.R.color.system_notification_accent_color))
3022 .setContentTitle(title)
3023 .setContentText(subtitle)
3024 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
3025 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
3026 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003027 installNotification(getCredentialPermissionNotificationId(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003028 account, authTokenType, uid), n, packageName, user.getIdentifier());
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003029 }
3030
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003031 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
3032 int uid, AccountAuthenticatorResponse response, String authTokenType,
3033 boolean startInNewTask) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003034
3035 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003036
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003037 if (startInNewTask) {
3038 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
3039 // Since it was set in Eclair+ we can't change it without breaking apps using
3040 // the intent from a non-Activity context. This is the default behavior.
3041 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3042 }
3043 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
3044 authTokenType, uid) + (packageName != null ? packageName : "")));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003045 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003046 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
3047 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003048 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003049
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003050 return intent;
3051 }
3052
3053 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
3054 int uid) {
3055 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003056 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003057 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003058 final Pair<Pair<Account, String>, Integer> key =
3059 new Pair<Pair<Account, String>, Integer>(
3060 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003061 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003062 if (id == null) {
3063 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003064 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003065 }
3066 }
3067 return id;
3068 }
3069
Amith Yamasani04e0d262012-02-14 11:50:53 -08003070 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003071 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003072 synchronized (accounts.signinRequiredNotificationIds) {
3073 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003074 if (id == null) {
3075 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003076 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003077 }
3078 }
3079 return id;
3080 }
3081
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003082 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07003083 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07003084 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003085 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003086 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003087 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3088 Log.v(TAG, "addAccount: accountType " + accountType
3089 + ", response " + response
3090 + ", authTokenType " + authTokenType
3091 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3092 + ", expectActivityLaunch " + expectActivityLaunch
3093 + ", caller's uid " + Binder.getCallingUid()
3094 + ", pid " + Binder.getCallingPid());
3095 }
Fred Quintana382601f2010-03-25 12:25:10 -07003096 if (response == null) throw new IllegalArgumentException("response is null");
3097 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003098
Amith Yamasani71e6c692013-03-24 17:39:28 -07003099 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003100 final int uid = Binder.getCallingUid();
3101 final int userId = UserHandle.getUserId(uid);
3102 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003103 try {
3104 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3105 "User is not allowed to add an account!");
3106 } catch (RemoteException re) {
3107 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003108 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003109 return;
3110 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003111 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003112 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003113 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3114 "User cannot modify accounts of this type (policy).");
3115 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003116 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003117 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3118 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003119 return;
3120 }
3121
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003122 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003123 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3124 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3125 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3126
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003127 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003128 long identityToken = clearCallingIdentity();
3129 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003130 UserAccounts accounts = getUserAccounts(usrId);
3131 logRecordWithUid(
3132 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003133 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003134 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003135 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003136 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003137 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07003138 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07003139 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003140 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003141
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003142 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003143 protected String toDebugString(long now) {
3144 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07003145 + ", accountType " + accountType
3146 + ", requiredFeatures "
3147 + (requiredFeatures != null
3148 ? TextUtils.join(",", requiredFeatures)
3149 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003150 }
3151 }.bind();
3152 } finally {
3153 restoreCallingIdentity(identityToken);
3154 }
Fred Quintana60307342009-03-24 22:48:12 -07003155 }
3156
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003157 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003158 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
3159 final String authTokenType, final String[] requiredFeatures,
3160 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003161 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003162 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003163 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3164 Log.v(TAG, "addAccount: accountType " + accountType
3165 + ", response " + response
3166 + ", authTokenType " + authTokenType
3167 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3168 + ", expectActivityLaunch " + expectActivityLaunch
3169 + ", caller's uid " + Binder.getCallingUid()
3170 + ", pid " + Binder.getCallingPid()
3171 + ", for user id " + userId);
3172 }
3173 if (response == null) throw new IllegalArgumentException("response is null");
3174 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003175 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003176 if (isCrossUser(callingUid, userId)) {
3177 throw new SecurityException(
3178 String.format(
3179 "User %s trying to add account for %s" ,
3180 UserHandle.getCallingUserId(),
3181 userId));
3182 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003183
3184 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003185 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003186 try {
3187 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3188 "User is not allowed to add an account!");
3189 } catch (RemoteException re) {
3190 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003191 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003192 return;
3193 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003194 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003195 try {
3196 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3197 "User cannot modify accounts of this type (policy).");
3198 } catch (RemoteException re) {
3199 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003200 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3201 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003202 return;
3203 }
3204
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003205 final int pid = Binder.getCallingPid();
3206 final int uid = Binder.getCallingUid();
3207 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3208 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3209 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3210
3211 long identityToken = clearCallingIdentity();
3212 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003213 UserAccounts accounts = getUserAccounts(userId);
3214 logRecordWithUid(
3215 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003216 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003217 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003218 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003219 @Override
3220 public void run() throws RemoteException {
3221 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3222 options);
3223 }
3224
3225 @Override
3226 protected String toDebugString(long now) {
3227 return super.toDebugString(now) + ", addAccount"
3228 + ", accountType " + accountType
3229 + ", requiredFeatures "
3230 + (requiredFeatures != null
3231 ? TextUtils.join(",", requiredFeatures)
3232 : null);
3233 }
3234 }.bind();
3235 } finally {
3236 restoreCallingIdentity(identityToken);
3237 }
3238 }
3239
Sandra Kwan78812282015-11-04 11:19:47 -08003240 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003241 public void startAddAccountSession(
3242 final IAccountManagerResponse response,
3243 final String accountType,
3244 final String authTokenType,
3245 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08003246 final boolean expectActivityLaunch,
3247 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003248 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003249 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3250 Log.v(TAG,
3251 "startAddAccountSession: accountType " + accountType
3252 + ", response " + response
3253 + ", authTokenType " + authTokenType
3254 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3255 + ", expectActivityLaunch " + expectActivityLaunch
3256 + ", caller's uid " + Binder.getCallingUid()
3257 + ", pid " + Binder.getCallingPid());
3258 }
3259 if (response == null) {
3260 throw new IllegalArgumentException("response is null");
3261 }
3262 if (accountType == null) {
3263 throw new IllegalArgumentException("accountType is null");
3264 }
3265
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003266 final int uid = Binder.getCallingUid();
3267 final int userId = UserHandle.getUserId(uid);
3268 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003269 try {
3270 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3271 "User is not allowed to add an account!");
3272 } catch (RemoteException re) {
3273 }
3274 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3275 return;
3276 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003277 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003278 try {
3279 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3280 "User cannot modify accounts of this type (policy).");
3281 } catch (RemoteException re) {
3282 }
3283 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3284 userId);
3285 return;
3286 }
Sandra Kwan78812282015-11-04 11:19:47 -08003287 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08003288 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3289 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3290 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3291
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003292 // Check to see if the Password should be included to the caller.
3293 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3294 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003295 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003296
Sandra Kwan78812282015-11-04 11:19:47 -08003297 long identityToken = clearCallingIdentity();
3298 try {
Hongming Jin368aa192016-07-29 14:29:54 -07003299 UserAccounts accounts = getUserAccounts(userId);
Sandra Kwan78812282015-11-04 11:19:47 -08003300 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
3301 TABLE_ACCOUNTS, uid);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003302 new StartAccountSession(
3303 accounts,
3304 response,
3305 accountType,
3306 expectActivityLaunch,
3307 null /* accountName */,
3308 false /* authDetailsRequired */,
3309 true /* updateLastAuthenticationTime */,
3310 isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003311 @Override
3312 public void run() throws RemoteException {
3313 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3314 requiredFeatures, options);
3315 }
3316
3317 @Override
3318 protected String toDebugString(long now) {
3319 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3320 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3321 + accountType + ", requiredFeatures "
3322 + (requiredFeatures != null ? requiredFeaturesStr : null);
3323 }
3324 }.bind();
3325 } finally {
3326 restoreCallingIdentity(identityToken);
3327 }
3328 }
3329
3330 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3331 private abstract class StartAccountSession extends Session {
3332
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003333 private final boolean mIsPasswordForwardingAllowed;
3334
3335 public StartAccountSession(
3336 UserAccounts accounts,
3337 IAccountManagerResponse response,
3338 String accountType,
3339 boolean expectActivityLaunch,
3340 String accountName,
3341 boolean authDetailsRequired,
3342 boolean updateLastAuthenticationTime,
3343 boolean isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003344 super(accounts, response, accountType, expectActivityLaunch,
3345 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3346 updateLastAuthenticationTime);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003347 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
Sandra Kwan78812282015-11-04 11:19:47 -08003348 }
3349
3350 @Override
3351 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003352 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003353 mNumResults++;
3354 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08003355 if (result != null
3356 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003357 checkKeyIntent(
3358 Binder.getCallingUid(),
3359 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08003360 }
Sandra Kwan78812282015-11-04 11:19:47 -08003361 IAccountManagerResponse response;
3362 if (mExpectActivityLaunch && result != null
3363 && result.containsKey(AccountManager.KEY_INTENT)) {
3364 response = mResponse;
3365 } else {
3366 response = getResponseAndClose();
3367 }
3368 if (response == null) {
3369 return;
3370 }
3371 if (result == null) {
3372 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3373 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3374 + response);
3375 }
3376 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3377 "null bundle returned");
3378 return;
3379 }
3380
3381 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3382 // All AccountManager error codes are greater
3383 // than 0
3384 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3385 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3386 return;
3387 }
3388
Hongming Jin368aa192016-07-29 14:29:54 -07003389 // Omit passwords if the caller isn't permitted to see them.
3390 if (!mIsPasswordForwardingAllowed) {
3391 result.remove(AccountManager.KEY_PASSWORD);
3392 }
3393
Sandra Kwan78812282015-11-04 11:19:47 -08003394 // Strip auth token from result.
3395 result.remove(AccountManager.KEY_AUTHTOKEN);
3396
3397 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3398 Log.v(TAG,
3399 getClass().getSimpleName() + " calling onResult() on response " + response);
3400 }
3401
3402 // Get the session bundle created by authenticator. The
3403 // bundle contains data necessary for finishing the session
3404 // later. The session bundle will be encrypted here and
3405 // decrypted later when trying to finish the session.
3406 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3407 if (sessionBundle != null) {
3408 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3409 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08003410 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003411 Log.w(TAG, "Account type in session bundle doesn't match request.");
3412 }
3413 // Add accountType info to session bundle. This will
3414 // override any value set by authenticator.
3415 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3416
3417 // Encrypt session bundle before returning to caller.
3418 try {
3419 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3420 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3421 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3422 } catch (GeneralSecurityException e) {
3423 if (Log.isLoggable(TAG, Log.DEBUG)) {
3424 Log.v(TAG, "Failed to encrypt session bundle!", e);
3425 }
3426 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3427 "failed to encrypt session bundle");
3428 return;
3429 }
3430 }
3431
3432 sendResponse(response, result);
3433 }
3434 }
3435
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003436 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08003437 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003438 @NonNull Bundle sessionBundle,
3439 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003440 Bundle appInfo,
3441 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003442 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08003443 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003444 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3445 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003446 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003447 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08003448 + ", caller's uid " + callingUid
3449 + ", caller's user id " + UserHandle.getCallingUserId()
3450 + ", pid " + Binder.getCallingPid()
3451 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003452 }
3453 if (response == null) {
3454 throw new IllegalArgumentException("response is null");
3455 }
3456
3457 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3458 // Account type is added to it before encryption.
3459 if (sessionBundle == null || sessionBundle.size() == 0) {
3460 throw new IllegalArgumentException("sessionBundle is empty");
3461 }
3462
Sandra Kwan0b84b452016-01-20 15:25:42 -08003463 // Only allow the system process to finish session for other users
3464 if (isCrossUser(callingUid, userId)) {
3465 throw new SecurityException(
3466 String.format(
3467 "User %s trying to finish session for %s without cross user permission",
3468 UserHandle.getCallingUserId(),
3469 userId));
3470 }
3471
Sandra Kwan0b84b452016-01-20 15:25:42 -08003472 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003473 sendErrorResponse(response,
3474 AccountManager.ERROR_CODE_USER_RESTRICTED,
3475 "User is not allowed to add an account!");
3476 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3477 return;
3478 }
3479
3480 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003481 final Bundle decryptedBundle;
3482 final String accountType;
3483 // First decrypt session bundle to get account type for checking permission.
3484 try {
3485 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3486 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3487 if (decryptedBundle == null) {
3488 sendErrorResponse(
3489 response,
3490 AccountManager.ERROR_CODE_BAD_REQUEST,
3491 "failed to decrypt session bundle");
3492 return;
3493 }
3494 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3495 // Account type cannot be null. This should not happen if session bundle was created
3496 // properly by #StartAccountSession.
3497 if (TextUtils.isEmpty(accountType)) {
3498 sendErrorResponse(
3499 response,
3500 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3501 "accountType is empty");
3502 return;
3503 }
3504
3505 // If by any chances, decryptedBundle contains colliding keys with
3506 // system info
3507 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3508 // update credentials flow, we should replace with the new values of the current call.
3509 if (appInfo != null) {
3510 decryptedBundle.putAll(appInfo);
3511 }
3512
3513 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08003514 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003515 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3516 } catch (GeneralSecurityException e) {
3517 if (Log.isLoggable(TAG, Log.DEBUG)) {
3518 Log.v(TAG, "Failed to decrypt session bundle!", e);
3519 }
3520 sendErrorResponse(
3521 response,
3522 AccountManager.ERROR_CODE_BAD_REQUEST,
3523 "failed to decrypt session bundle");
3524 return;
3525 }
3526
Sandra Kwan0b84b452016-01-20 15:25:42 -08003527 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003528 sendErrorResponse(
3529 response,
3530 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3531 "User cannot modify accounts of this type (policy).");
3532 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3533 userId);
3534 return;
3535 }
3536
3537 long identityToken = clearCallingIdentity();
3538 try {
3539 UserAccounts accounts = getUserAccounts(userId);
3540 logRecordWithUid(
3541 accounts,
3542 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3543 TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003544 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003545 new Session(
3546 accounts,
3547 response,
3548 accountType,
3549 expectActivityLaunch,
3550 true /* stripAuthTokenFromResult */,
3551 null /* accountName */,
3552 false /* authDetailsRequired */,
3553 true /* updateLastAuthenticationTime */) {
3554 @Override
3555 public void run() throws RemoteException {
3556 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3557 }
3558
3559 @Override
3560 protected String toDebugString(long now) {
3561 return super.toDebugString(now)
3562 + ", finishSession"
3563 + ", accountType " + accountType;
3564 }
3565 }.bind();
3566 } finally {
3567 restoreCallingIdentity(identityToken);
3568 }
3569 }
3570
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003571 private void showCantAddAccount(int errorCode, int userId) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003572 final DevicePolicyManagerInternal dpmi =
3573 LocalServices.getService(DevicePolicyManagerInternal.class);
3574 Intent intent = null;
Nicolas Prevot14fc1972016-08-24 14:21:38 +01003575 if (dpmi == null) {
3576 intent = getDefaultCantAddAccountIntent(errorCode);
3577 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003578 intent = dpmi.createUserRestrictionSupportIntent(userId,
3579 UserManager.DISALLOW_MODIFY_ACCOUNTS);
3580 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3581 intent = dpmi.createShowAdminSupportIntent(userId, false);
3582 }
3583 if (intent == null) {
3584 intent = getDefaultCantAddAccountIntent(errorCode);
3585 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003586 long identityToken = clearCallingIdentity();
3587 try {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003588 mContext.startActivityAsUser(intent, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003589 } finally {
3590 restoreCallingIdentity(identityToken);
3591 }
3592 }
3593
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003594 /**
3595 * Called when we don't know precisely who is preventing us from adding an account.
3596 */
3597 private Intent getDefaultCantAddAccountIntent(int errorCode) {
3598 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3599 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3600 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3601 return cantAddAccount;
3602 }
3603
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003604 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003605 public void confirmCredentialsAsUser(
3606 IAccountManagerResponse response,
3607 final Account account,
3608 final Bundle options,
3609 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003610 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003611 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003612 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003613 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3614 Log.v(TAG, "confirmCredentials: " + account
3615 + ", response " + response
3616 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003617 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003618 + ", pid " + Binder.getCallingPid());
3619 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003620 // Only allow the system process to read accounts of other users
3621 if (isCrossUser(callingUid, userId)) {
3622 throw new SecurityException(
3623 String.format(
3624 "User %s trying to confirm account credentials for %s" ,
3625 UserHandle.getCallingUserId(),
3626 userId));
3627 }
Fred Quintana382601f2010-03-25 12:25:10 -07003628 if (response == null) throw new IllegalArgumentException("response is null");
3629 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003630 long identityToken = clearCallingIdentity();
3631 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003632 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003633 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003634 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003635 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003636 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003637 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003638 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003639 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003640 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003641 protected String toDebugString(long now) {
3642 return super.toDebugString(now) + ", confirmCredentials"
3643 + ", " + account;
3644 }
3645 }.bind();
3646 } finally {
3647 restoreCallingIdentity(identityToken);
3648 }
Fred Quintana60307342009-03-24 22:48:12 -07003649 }
3650
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003651 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003652 public void updateCredentials(IAccountManagerResponse response, final Account account,
3653 final String authTokenType, final boolean expectActivityLaunch,
3654 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003655 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003656 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3657 Log.v(TAG, "updateCredentials: " + account
3658 + ", response " + response
3659 + ", authTokenType " + authTokenType
3660 + ", expectActivityLaunch " + expectActivityLaunch
3661 + ", caller's uid " + Binder.getCallingUid()
3662 + ", pid " + Binder.getCallingPid());
3663 }
Fred Quintana382601f2010-03-25 12:25:10 -07003664 if (response == null) throw new IllegalArgumentException("response is null");
3665 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003666 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003667 long identityToken = clearCallingIdentity();
3668 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003669 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003670 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003671 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003672 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003673 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003674 public void run() throws RemoteException {
3675 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3676 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003677 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003678 protected String toDebugString(long now) {
3679 if (loginOptions != null) loginOptions.keySet();
3680 return super.toDebugString(now) + ", updateCredentials"
3681 + ", " + account
3682 + ", authTokenType " + authTokenType
3683 + ", loginOptions " + loginOptions;
3684 }
3685 }.bind();
3686 } finally {
3687 restoreCallingIdentity(identityToken);
3688 }
Fred Quintana60307342009-03-24 22:48:12 -07003689 }
3690
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003691 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003692 public void startUpdateCredentialsSession(
3693 IAccountManagerResponse response,
3694 final Account account,
3695 final String authTokenType,
3696 final boolean expectActivityLaunch,
3697 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003698 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08003699 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3700 Log.v(TAG,
3701 "startUpdateCredentialsSession: " + account + ", response " + response
3702 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3703 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3704 + ", pid " + Binder.getCallingPid());
3705 }
3706 if (response == null) {
3707 throw new IllegalArgumentException("response is null");
3708 }
3709 if (account == null) {
3710 throw new IllegalArgumentException("account is null");
3711 }
Sandra Kwana578d112015-12-16 16:01:43 -08003712
3713 final int uid = Binder.getCallingUid();
Sandra Kwane68c37e2015-11-12 17:11:49 -08003714 int userId = UserHandle.getCallingUserId();
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003715
3716 // Check to see if the Password should be included to the caller.
3717 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3718 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003719 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003720
Sandra Kwane68c37e2015-11-12 17:11:49 -08003721 long identityToken = clearCallingIdentity();
3722 try {
3723 UserAccounts accounts = getUserAccounts(userId);
3724 new StartAccountSession(
3725 accounts,
3726 response,
3727 account.type,
3728 expectActivityLaunch,
3729 account.name,
3730 false /* authDetailsRequired */,
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003731 true /* updateLastCredentialTime */,
3732 isPasswordForwardingAllowed) {
Sandra Kwane68c37e2015-11-12 17:11:49 -08003733 @Override
3734 public void run() throws RemoteException {
3735 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3736 loginOptions);
3737 }
3738
3739 @Override
3740 protected String toDebugString(long now) {
3741 if (loginOptions != null)
3742 loginOptions.keySet();
3743 return super.toDebugString(now)
3744 + ", startUpdateCredentialsSession"
3745 + ", " + account
3746 + ", authTokenType " + authTokenType
3747 + ", loginOptions " + loginOptions;
3748 }
3749 }.bind();
3750 } finally {
3751 restoreCallingIdentity(identityToken);
3752 }
3753 }
3754
3755 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08003756 public void isCredentialsUpdateSuggested(
3757 IAccountManagerResponse response,
3758 final Account account,
3759 final String statusToken) {
3760 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3761 Log.v(TAG,
3762 "isCredentialsUpdateSuggested: " + account + ", response " + response
3763 + ", caller's uid " + Binder.getCallingUid()
3764 + ", pid " + Binder.getCallingPid());
3765 }
3766 if (response == null) {
3767 throw new IllegalArgumentException("response is null");
3768 }
3769 if (account == null) {
3770 throw new IllegalArgumentException("account is null");
3771 }
3772 if (TextUtils.isEmpty(statusToken)) {
3773 throw new IllegalArgumentException("status token is empty");
3774 }
3775
Sandra Kwan390c9d22016-01-12 14:13:37 -08003776 int usrId = UserHandle.getCallingUserId();
3777 long identityToken = clearCallingIdentity();
3778 try {
3779 UserAccounts accounts = getUserAccounts(usrId);
3780 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3781 false /* stripAuthTokenFromResult */, account.name,
3782 false /* authDetailsRequired */) {
3783 @Override
3784 protected String toDebugString(long now) {
3785 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3786 + ", " + account;
3787 }
3788
3789 @Override
3790 public void run() throws RemoteException {
3791 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3792 }
3793
3794 @Override
3795 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003796 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08003797 IAccountManagerResponse response = getResponseAndClose();
3798 if (response == null) {
3799 return;
3800 }
3801
3802 if (result == null) {
3803 sendErrorResponse(
3804 response,
3805 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3806 "null bundle");
3807 return;
3808 }
3809
3810 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3811 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3812 + response);
3813 }
3814 // Check to see if an error occurred. We know if an error occurred because all
3815 // error codes are greater than 0.
3816 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3817 sendErrorResponse(response,
3818 result.getInt(AccountManager.KEY_ERROR_CODE),
3819 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3820 return;
3821 }
3822 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3823 sendErrorResponse(
3824 response,
3825 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3826 "no result in response");
3827 return;
3828 }
3829 final Bundle newResult = new Bundle();
3830 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3831 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3832 sendResponse(response, newResult);
3833 }
3834 }.bind();
3835 } finally {
3836 restoreCallingIdentity(identityToken);
3837 }
3838 }
3839
3840 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003841 public void editProperties(IAccountManagerResponse response, final String accountType,
3842 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003843 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003844 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3845 Log.v(TAG, "editProperties: accountType " + accountType
3846 + ", response " + response
3847 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003848 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003849 + ", pid " + Binder.getCallingPid());
3850 }
Fred Quintana382601f2010-03-25 12:25:10 -07003851 if (response == null) throw new IllegalArgumentException("response is null");
3852 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003853 int userId = UserHandle.getCallingUserId();
3854 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003855 String msg = String.format(
3856 "uid %s cannot edit authenticator properites for account type: %s",
3857 callingUid,
3858 accountType);
3859 throw new SecurityException(msg);
3860 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003861 long identityToken = clearCallingIdentity();
3862 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003863 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003864 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003865 true /* stripAuthTokenFromResult */, null /* accountName */,
3866 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003867 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003868 public void run() throws RemoteException {
3869 mAuthenticator.editProperties(this, mAccountType);
3870 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003871 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003872 protected String toDebugString(long now) {
3873 return super.toDebugString(now) + ", editProperties"
3874 + ", accountType " + accountType;
3875 }
3876 }.bind();
3877 } finally {
3878 restoreCallingIdentity(identityToken);
3879 }
Fred Quintana60307342009-03-24 22:48:12 -07003880 }
3881
Amith Yamasani12747872015-12-07 14:19:49 -08003882 @Override
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003883 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
3884 @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003885 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003886 throw new SecurityException("Can be called only by system UID");
3887 }
3888 Preconditions.checkNotNull(account, "account cannot be null");
3889 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3890 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3891
3892 final int userId = userHandle.getIdentifier();
3893
3894 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3895
3896 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003897 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
Svet Ganovf6d424f12016-09-20 20:18:53 -07003898 return hasAccountAccess(account, packageName, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003899 } catch (NameNotFoundException e) {
3900 return false;
3901 }
3902 }
3903
Svet Ganovf6d424f12016-09-20 20:18:53 -07003904 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3905 int uid) {
3906 if (packageName == null) {
3907 String[] packageNames = mPackageManager.getPackagesForUid(uid);
3908 if (ArrayUtils.isEmpty(packageNames)) {
3909 return false;
3910 }
3911 // For app op checks related to permissions all packages in the UID
3912 // have the same app op state, so doesn't matter which one we pick.
3913 packageName = packageNames[0];
3914 }
3915
3916 // Use null token which means any token. Having a token means the package
3917 // is trusted by the authenticator, hence it is fine to access the account.
3918 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
3919 return true;
3920 }
3921 // In addition to the permissions required to get an auth token we also allow
3922 // the account to be accessed by holders of the get accounts permissions.
3923 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3924 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3925 }
3926
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003927 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3928 final long identity = Binder.clearCallingIdentity();
3929 try {
3930 IPackageManager pm = ActivityThread.getPackageManager();
3931 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3932 return false;
3933 }
3934 final int opCode = AppOpsManager.permissionToOpCode(permission);
3935 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3936 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3937 } catch (RemoteException e) {
3938 /* ignore - local call */
3939 } finally {
3940 Binder.restoreCallingIdentity(identity);
3941 }
3942 return false;
3943 }
3944
3945 @Override
3946 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3947 @NonNull String packageName, @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003948 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003949 throw new SecurityException("Can be called only by system UID");
3950 }
3951
3952 Preconditions.checkNotNull(account, "account cannot be null");
3953 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3954 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3955
3956 final int userId = userHandle.getIdentifier();
3957
3958 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3959
3960 final int uid;
3961 try {
3962 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3963 } catch (NameNotFoundException e) {
3964 Slog.e(TAG, "Unknown package " + packageName);
3965 return null;
3966 }
3967
3968 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3969
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003970 final long identity = Binder.clearCallingIdentity();
3971 try {
3972 return PendingIntent.getActivityAsUser(
3973 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3974 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3975 null, new UserHandle(userId)).getIntentSender();
3976 } finally {
3977 Binder.restoreCallingIdentity(identity);
3978 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003979 }
3980
3981 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3982 int uid, RemoteCallback callback) {
3983 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3984 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3985 @Override
3986 public void onResult(Bundle value) throws RemoteException {
3987 handleAuthenticatorResponse(true);
3988 }
3989
3990 @Override
3991 public void onRequestContinued() {
3992 /* ignore */
3993 }
3994
3995 @Override
3996 public void onError(int errorCode, String errorMessage) throws RemoteException {
3997 handleAuthenticatorResponse(false);
3998 }
3999
4000 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
4001 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -07004002 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004003 UserHandle.getUserHandleForUid(uid));
4004 if (callback != null) {
4005 Bundle result = new Bundle();
4006 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
4007 callback.sendResult(result);
4008 }
4009 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07004010 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004011 }
4012
4013 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08004014 public boolean someUserHasAccount(@NonNull final Account account) {
4015 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
4016 throw new SecurityException("Only system can check for accounts across users");
4017 }
4018 final long token = Binder.clearCallingIdentity();
4019 try {
4020 AccountAndUser[] allAccounts = getAllAccounts();
4021 for (int i = allAccounts.length - 1; i >= 0; i--) {
4022 if (allAccounts[i].account.equals(account)) {
4023 return true;
4024 }
4025 }
4026 return false;
4027 } finally {
4028 Binder.restoreCallingIdentity(token);
4029 }
4030 }
4031
Fred Quintana33269202009-04-20 16:05:10 -07004032 private class GetAccountsByTypeAndFeatureSession extends Session {
4033 private final String[] mFeatures;
4034 private volatile Account[] mAccountsOfType = null;
4035 private volatile ArrayList<Account> mAccountsWithFeatures = null;
4036 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004037 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004038
Amith Yamasani04e0d262012-02-14 11:50:53 -08004039 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004040 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004041 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004042 true /* stripAuthTokenFromResult */, null /* accountName */,
4043 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004044 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004045 mFeatures = features;
4046 }
4047
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004048 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004049 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004050 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004051 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
4052 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004053 }
Fred Quintana33269202009-04-20 16:05:10 -07004054 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07004055 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07004056 mCurrentAccount = 0;
4057
4058 checkAccount();
4059 }
4060
4061 public void checkAccount() {
4062 if (mCurrentAccount >= mAccountsOfType.length) {
4063 sendResult();
4064 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07004065 }
Fred Quintana33269202009-04-20 16:05:10 -07004066
Fred Quintana29e94b82010-03-10 12:11:51 -08004067 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
4068 if (accountAuthenticator == null) {
4069 // It is possible that the authenticator has died, which is indicated by
4070 // mAuthenticator being set to null. If this happens then just abort.
4071 // There is no need to send back a result or error in this case since
4072 // that already happened when mAuthenticator was cleared.
4073 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4074 Log.v(TAG, "checkAccount: aborting session since we are no longer"
4075 + " connected to the authenticator, " + toDebugString());
4076 }
4077 return;
4078 }
Fred Quintana33269202009-04-20 16:05:10 -07004079 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08004080 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07004081 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004082 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07004083 }
4084 }
4085
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004086 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004087 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004088 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07004089 mNumResults++;
4090 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004091 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07004092 return;
4093 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004094 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07004095 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
4096 }
4097 mCurrentAccount++;
4098 checkAccount();
4099 }
4100
4101 public void sendResult() {
4102 IAccountManagerResponse response = getResponseAndClose();
4103 if (response != null) {
4104 try {
4105 Account[] accounts = new Account[mAccountsWithFeatures.size()];
4106 for (int i = 0; i < accounts.length; i++) {
4107 accounts[i] = mAccountsWithFeatures.get(i);
4108 }
Fred Quintana56285a62010-12-02 14:20:51 -08004109 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4110 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
4111 + response);
4112 }
Fred Quintana33269202009-04-20 16:05:10 -07004113 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004114 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07004115 response.onResult(result);
4116 } catch (RemoteException e) {
4117 // if the caller is dead then there is no one to care about remote exceptions
4118 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4119 Log.v(TAG, "failure while notifying response", e);
4120 }
4121 }
4122 }
4123 }
4124
4125
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004126 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004127 protected String toDebugString(long now) {
4128 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
4129 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
4130 }
4131 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004132
Amith Yamasani04e0d262012-02-14 11:50:53 -08004133 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004134 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08004135 * @hide
4136 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004137 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004138 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004139 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07004140 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4141 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004142 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004143 return new Account[0];
4144 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004145 long identityToken = clearCallingIdentity();
4146 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004147 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004148 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004149 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004150 callingUid,
4151 null, // packageName
4152 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004153 } finally {
4154 restoreCallingIdentity(identityToken);
4155 }
4156 }
4157
Amith Yamasanif29f2362012-04-05 18:29:52 -07004158 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004159 * Returns accounts for all running users.
4160 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07004161 * @hide
4162 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004163 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004164 public AccountAndUser[] getRunningAccounts() {
4165 final int[] runningUserIds;
4166 try {
4167 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
4168 } catch (RemoteException e) {
4169 // Running in system_server; should never happen
4170 throw new RuntimeException(e);
4171 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004172 return getAccounts(runningUserIds);
4173 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004174
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004175 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004176 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004177 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004178 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004179 final int[] userIds = new int[users.size()];
4180 for (int i = 0; i < userIds.length; i++) {
4181 userIds[i] = users.get(i).id;
4182 }
4183 return getAccounts(userIds);
4184 }
4185
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004186 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004187 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004188 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004189 for (int userId : userIds) {
4190 UserAccounts userAccounts = getUserAccounts(userId);
4191 if (userAccounts == null) continue;
4192 synchronized (userAccounts.cacheLock) {
4193 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4194 Binder.getCallingUid(), null);
4195 for (int a = 0; a < accounts.length; a++) {
4196 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004197 }
4198 }
4199 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004200
4201 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4202 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004203 }
4204
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004205 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004206 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004207 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4208 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004209 }
4210
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004211 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004212 private Account[] getAccountsAsUser(
4213 String type,
4214 int userId,
4215 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004216 int packageUid,
4217 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004218 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004219 // Only allow the system process to read accounts of other users
4220 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004221 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004222 && mContext.checkCallingOrSelfPermission(
4223 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4224 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004225 throw new SecurityException("User " + UserHandle.getCallingUserId()
4226 + " trying to get account for " + userId);
4227 }
4228
Fred Quintana56285a62010-12-02 14:20:51 -08004229 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4230 Log.v(TAG, "getAccounts: accountType " + type
4231 + ", caller's uid " + Binder.getCallingUid()
4232 + ", pid " + Binder.getCallingPid());
4233 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004234 // If the original calling app was using the framework account chooser activity, we'll
4235 // be passed in the original caller's uid here, which is what should be used for filtering.
4236 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4237 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004238 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004239 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004240 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4241 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004242 if (visibleAccountTypes.isEmpty()
4243 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004244 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004245 } else if (visibleAccountTypes.contains(type)) {
4246 // Prune the list down to just the requested type.
4247 visibleAccountTypes = new ArrayList<>();
4248 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004249 } // else aggregate all the visible accounts (it won't matter if the
4250 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004251
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004252 long identityToken = clearCallingIdentity();
4253 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004254 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004255 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004256 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004257 callingUid,
4258 callingPackage,
4259 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004260 ArrayList<Account> accountsToReturnList = new
4261 ArrayList<Account>(Arrays.asList(accountsToReturn));
4262 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4263 // if account not visible to caller or managed by caller, remove from
4264 // accounts to return. Note that all accounts visible by default unless
4265 // visible list functionality implemented
4266 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4267 getUserAccounts(userId)))) {
4268 accountsToReturnList.remove(i);
4269 }
4270 }
4271 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004272 } finally {
4273 restoreCallingIdentity(identityToken);
4274 }
4275 }
4276
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004277 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004278 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004279 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004280 int callingUid,
4281 String callingPackage,
4282 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004283 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004284 ArrayList<Account> visibleAccounts = new ArrayList<>();
4285 for (String visibleType : visibleAccountTypes) {
4286 Account[] accountsForType = getAccountsFromCacheLocked(
4287 userAccounts, visibleType, callingUid, callingPackage);
4288 if (accountsForType != null) {
4289 visibleAccounts.addAll(Arrays.asList(accountsForType));
4290 }
4291 }
4292 Account[] result = new Account[visibleAccounts.size()];
4293 for (int i = 0; i < visibleAccounts.size(); i++) {
4294 result[i] = visibleAccounts.get(i);
4295 }
4296 return result;
4297 }
4298 }
4299
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004300 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004301 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004302 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004303 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4304 for (Account account : accounts) {
4305 addSharedAccountAsUser(account, userId);
4306 }
4307 }
4308
4309 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004310 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004311 UserAccounts accounts = getUserAccounts(userId);
4312 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004313 AccountsDbUtils.deleteSharedAccount(db, account);
4314 long accountId = AccountsDbUtils.insertSharedAccount(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004315 if (accountId < 0) {
4316 Log.w(TAG, "insertAccountIntoDatabase: " + account
4317 + ", skipping the DB insert failed");
4318 return false;
4319 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004320 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004321 return true;
4322 }
4323
4324 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004325 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4326 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 int r = AccountsDbUtils.renameSharedAccount(db, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004331 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004332 int callingUid = getCallingUid();
4333 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
4334 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004335 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004336 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004337 }
4338 return r > 0;
4339 }
4340
4341 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004342 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004343 return removeSharedAccountAsUser(account, userId, getCallingUid());
4344 }
4345
4346 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004347 userId = handleIncomingUser(userId);
4348 UserAccounts accounts = getUserAccounts(userId);
4349 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004350 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4351 boolean deleted = AccountsDbUtils.deleteSharedAccount(db, account);
4352 if (deleted) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004353 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
4354 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004355 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004356 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004357 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004358 }
4359
4360 @Override
4361 public Account[] getSharedAccountsAsUser(int userId) {
4362 userId = handleIncomingUser(userId);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004363 SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
4364 List<Account> accountList = AccountsDbUtils.getSharedAccounts(db);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004365 Account[] accountArray = new Account[accountList.size()];
4366 accountList.toArray(accountArray);
4367 return accountArray;
4368 }
4369
4370 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004371 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004372 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004373 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004374 }
4375
Amith Yamasani27db4682013-03-30 17:07:47 -07004376 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004377 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004378 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004379 int callingUid = Binder.getCallingUid();
4380 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4381 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4382 + callingUid + " with uid=" + uid);
4383 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004384 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4385 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004386 }
4387
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004388 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004389 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004390 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4391 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004392 int packageUid = -1;
4393 try {
4394 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004395 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4396 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004397 } catch (RemoteException re) {
4398 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4399 return new Account[0];
4400 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004401 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4402 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004403 }
4404
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004405 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004406 public void getAccountsByFeatures(
4407 IAccountManagerResponse response,
4408 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004409 String[] features,
4410 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004411 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004412 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4413 Log.v(TAG, "getAccounts: accountType " + type
4414 + ", response " + response
4415 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004416 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004417 + ", pid " + Binder.getCallingPid());
4418 }
Fred Quintana382601f2010-03-25 12:25:10 -07004419 if (response == null) throw new IllegalArgumentException("response is null");
4420 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004421 int userId = UserHandle.getCallingUserId();
4422
Svetoslavf3f02ac2015-09-08 14:36:35 -07004423 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4424 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004425 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004426 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004427 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004428 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4429 try {
4430 response.onResult(result);
4431 } catch (RemoteException e) {
4432 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4433 }
4434 return;
4435 }
Fred Quintana33269202009-04-20 16:05:10 -07004436 long identityToken = clearCallingIdentity();
4437 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004438 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004439 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004440 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004441 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004442 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004443 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004444 Bundle result = new Bundle();
4445 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4446 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004447 return;
4448 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004449 new GetAccountsByTypeAndFeatureSession(
4450 userAccounts,
4451 response,
4452 type,
4453 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004454 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004455 } finally {
4456 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004457 }
4458 }
4459
Fred Quintanaa698f422009-04-08 19:14:54 -07004460 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004461 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004462 IAccountManagerResponse mResponse;
4463 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004464 final boolean mExpectActivityLaunch;
4465 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004466 final String mAccountName;
4467 // Indicates if we need to add auth details(like last credential time)
4468 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004469 // If set, we need to update the last authenticated time. This is
4470 // currently
4471 // used on
4472 // successful confirming credentials.
4473 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004474
Fred Quintana33269202009-04-20 16:05:10 -07004475 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004476 private int mNumRequestContinued = 0;
4477 private int mNumErrors = 0;
4478
Fred Quintana60307342009-03-24 22:48:12 -07004479 IAccountAuthenticator mAuthenticator = null;
4480
Fred Quintana8570f742010-02-18 10:32:54 -08004481 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004482 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004483
Amith Yamasani04e0d262012-02-14 11:50:53 -08004484 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004485 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4486 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004487 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4488 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4489 }
4490
4491 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4492 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4493 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004494 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004495 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004496 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004497 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004498 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004499 mResponse = response;
4500 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004501 mExpectActivityLaunch = expectActivityLaunch;
4502 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004503 mAccountName = accountName;
4504 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004505 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004506
Fred Quintanaa698f422009-04-08 19:14:54 -07004507 synchronized (mSessions) {
4508 mSessions.put(toString(), this);
4509 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004510 if (response != null) {
4511 try {
4512 response.asBinder().linkToDeath(this, 0 /* flags */);
4513 } catch (RemoteException e) {
4514 mResponse = null;
4515 binderDied();
4516 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004517 }
Fred Quintana60307342009-03-24 22:48:12 -07004518 }
4519
Fred Quintanaa698f422009-04-08 19:14:54 -07004520 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004521 if (mResponse == null) {
4522 // this session has already been closed
4523 return null;
4524 }
Fred Quintana60307342009-03-24 22:48:12 -07004525 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004526 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004527 return response;
4528 }
4529
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004530 /**
4531 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4532 * security policy.
4533 *
4534 * In particular we want to make sure that the Authenticator doesn't try to trick users
4535 * into launching aribtrary intents on the device via by tricking to click authenticator
4536 * supplied entries in the system Settings app.
4537 */
4538 protected void checkKeyIntent(
4539 int authUid,
4540 Intent intent) throws SecurityException {
4541 long bid = Binder.clearCallingIdentity();
4542 try {
4543 PackageManager pm = mContext.getPackageManager();
4544 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4545 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4546 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004547 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4548 targetActivityInfo.getClass().getName())
4549 && !CantAddAccountActivity.class
4550 .equals(targetActivityInfo.getClass().getName())
4551 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4552 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004553 String pkgName = targetActivityInfo.packageName;
4554 String activityName = targetActivityInfo.name;
4555 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4556 + "does not share a signature with the supplying authenticator (%s).";
4557 throw new SecurityException(
4558 String.format(tmpl, activityName, pkgName, mAccountType));
4559 }
4560 } finally {
4561 Binder.restoreCallingIdentity(bid);
4562 }
4563 }
4564
Fred Quintanaa698f422009-04-08 19:14:54 -07004565 private void close() {
4566 synchronized (mSessions) {
4567 if (mSessions.remove(toString()) == null) {
4568 // the session was already closed, so bail out now
4569 return;
4570 }
4571 }
4572 if (mResponse != null) {
4573 // stop listening for response deaths
4574 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4575
4576 // clear this so that we don't accidentally send any further results
4577 mResponse = null;
4578 }
4579 cancelTimeout();
4580 unbind();
4581 }
4582
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004583 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004584 public void binderDied() {
4585 mResponse = null;
4586 close();
4587 }
4588
4589 protected String toDebugString() {
4590 return toDebugString(SystemClock.elapsedRealtime());
4591 }
4592
4593 protected String toDebugString(long now) {
4594 return "Session: expectLaunch " + mExpectActivityLaunch
4595 + ", connected " + (mAuthenticator != null)
4596 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4597 + "/" + mNumErrors + ")"
4598 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4599 }
4600
Fred Quintana60307342009-03-24 22:48:12 -07004601 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004602 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4603 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4604 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004605 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004606 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004607 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004608 }
4609 }
4610
4611 private void unbind() {
4612 if (mAuthenticator != null) {
4613 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004614 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004615 }
4616 }
4617
Fred Quintana60307342009-03-24 22:48:12 -07004618 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004619 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004620 }
4621
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004622 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004623 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004624 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004625 try {
4626 run();
4627 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004628 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004629 "remote exception");
4630 }
Fred Quintana60307342009-03-24 22:48:12 -07004631 }
4632
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004633 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004634 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004635 mAuthenticator = null;
4636 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004637 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004638 try {
4639 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4640 "disconnected");
4641 } catch (RemoteException e) {
4642 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4643 Log.v(TAG, "Session.onServiceDisconnected: "
4644 + "caught RemoteException while responding", e);
4645 }
4646 }
Fred Quintana60307342009-03-24 22:48:12 -07004647 }
4648 }
4649
Fred Quintanab839afc2009-10-14 15:57:28 -07004650 public abstract void run() throws RemoteException;
4651
Fred Quintana60307342009-03-24 22:48:12 -07004652 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004653 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004654 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004655 try {
4656 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4657 "timeout");
4658 } catch (RemoteException e) {
4659 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4660 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4661 e);
4662 }
4663 }
Fred Quintana60307342009-03-24 22:48:12 -07004664 }
4665 }
4666
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004667 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004668 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004669 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004670 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004671 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004672 if (result != null) {
4673 boolean isSuccessfulConfirmCreds = result.getBoolean(
4674 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004675 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004676 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4677 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004678 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004679 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4680 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004681 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004682 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004683 if (needUpdate || mAuthDetailsRequired) {
4684 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4685 if (needUpdate && accountPresent) {
4686 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4687 }
4688 if (mAuthDetailsRequired) {
4689 long lastAuthenticatedTime = -1;
4690 if (accountPresent) {
4691 lastAuthenticatedTime = DatabaseUtils.longForQuery(
4692 mAccounts.openHelper.getReadableDatabase(),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004693 "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4694 + " FROM " +
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004695 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4696 + ACCOUNTS_TYPE + "=?",
4697 new String[] {
4698 mAccountName, mAccountType
4699 });
4700 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004701 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004702 lastAuthenticatedTime);
4703 }
4704 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004705 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004706 if (result != null
4707 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004708 checkKeyIntent(
4709 Binder.getCallingUid(),
4710 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004711 }
4712 if (result != null
4713 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004714 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4715 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004716 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4717 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004718 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4719 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004720 }
Fred Quintana60307342009-03-24 22:48:12 -07004721 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004722 IAccountManagerResponse response;
4723 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004724 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004725 response = mResponse;
4726 } else {
4727 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004728 }
Fred Quintana60307342009-03-24 22:48:12 -07004729 if (response != null) {
4730 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004731 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004732 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4733 Log.v(TAG, getClass().getSimpleName()
4734 + " calling onError() on response " + response);
4735 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004736 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004737 "null bundle returned");
4738 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004739 if (mStripAuthTokenFromResult) {
4740 result.remove(AccountManager.KEY_AUTHTOKEN);
4741 }
Fred Quintana56285a62010-12-02 14:20:51 -08004742 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4743 Log.v(TAG, getClass().getSimpleName()
4744 + " calling onResult() on response " + response);
4745 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004746 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4747 (intent == null)) {
4748 // All AccountManager error codes are greater than 0
4749 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4750 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4751 } else {
4752 response.onResult(result);
4753 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004754 }
Fred Quintana60307342009-03-24 22:48:12 -07004755 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004756 // if the caller is dead then there is no one to care about remote exceptions
4757 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4758 Log.v(TAG, "failure while notifying response", e);
4759 }
Fred Quintana60307342009-03-24 22:48:12 -07004760 }
4761 }
4762 }
Fred Quintana60307342009-03-24 22:48:12 -07004763
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004764 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004765 public void onRequestContinued() {
4766 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004767 }
4768
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004769 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004770 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004771 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004772 IAccountManagerResponse response = getResponseAndClose();
4773 if (response != null) {
4774 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004775 Log.v(TAG, getClass().getSimpleName()
4776 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004777 }
4778 try {
4779 response.onError(errorCode, errorMessage);
4780 } catch (RemoteException e) {
4781 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4782 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4783 }
4784 }
4785 } else {
4786 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4787 Log.v(TAG, "Session.onError: already closed");
4788 }
Fred Quintana60307342009-03-24 22:48:12 -07004789 }
4790 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004791
4792 /**
4793 * find the component name for the authenticator and initiate a bind
4794 * if no authenticator or the bind fails then return false, otherwise return true
4795 */
4796 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004797 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4798 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4799 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004800 if (authenticatorInfo == null) {
4801 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4802 Log.v(TAG, "there is no authenticator for " + authenticatorType
4803 + ", bailing out");
4804 }
4805 return false;
4806 }
4807
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004808 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004809 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004810 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4811 + " which isn't encryption aware");
4812 return false;
4813 }
4814
Fred Quintanab839afc2009-10-14 15:57:28 -07004815 Intent intent = new Intent();
4816 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4817 intent.setComponent(authenticatorInfo.componentName);
4818 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4819 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4820 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004821 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004822 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004823 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4824 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4825 }
4826 return false;
4827 }
4828
Fred Quintanab839afc2009-10-14 15:57:28 -07004829 return true;
4830 }
Fred Quintana60307342009-03-24 22:48:12 -07004831 }
4832
4833 private class MessageHandler extends Handler {
4834 MessageHandler(Looper looper) {
4835 super(looper);
4836 }
Costin Manolache3348f142009-09-29 18:58:36 -07004837
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004838 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004839 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004840 switch (msg.what) {
4841 case MESSAGE_TIMED_OUT:
4842 Session session = (Session)msg.obj;
4843 session.onTimedOut();
4844 break;
4845
Amith Yamasani5be347b2013-03-31 17:44:31 -07004846 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004847 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004848 break;
4849
Fred Quintana60307342009-03-24 22:48:12 -07004850 default:
4851 throw new IllegalStateException("unhandled message: " + msg.what);
4852 }
4853 }
4854 }
4855
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004856 @VisibleForTesting
4857 String getPreNDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07004858 File systemDir = Environment.getDataSystemDirectory();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004859 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4860 PRE_N_DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004861 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07004862 // Migrate old file, if it exists, to the new location.
4863 // Make sure the new file doesn't already exist. A dummy file could have been
4864 // accidentally created in the old location, causing the new one to become corrupted
4865 // as well.
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004866 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07004867 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07004868 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07004869 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07004870 if (!userDir.exists()) {
4871 if (!userDir.mkdirs()) {
4872 throw new IllegalStateException("User dir cannot be created: " + userDir);
4873 }
4874 }
4875 if (!oldFile.renameTo(databaseFile)) {
4876 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4877 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004878 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08004879 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004880 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08004881 }
4882
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004883 @VisibleForTesting
4884 String getDeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004885 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4886 DE_DATABASE_NAME);
4887 return databaseFile.getPath();
4888 }
4889
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004890 @VisibleForTesting
4891 String getCeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004892 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4893 CE_DATABASE_NAME);
4894 return databaseFile.getPath();
4895 }
4896
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004897 private static class DebugDbHelper{
4898 private DebugDbHelper() {
4899 }
4900
4901 private static String TABLE_DEBUG = "debug_table";
4902
4903 // Columns for the table
4904 private static String ACTION_TYPE = "action_type";
4905 private static String TIMESTAMP = "time";
4906 private static String CALLER_UID = "caller_uid";
4907 private static String TABLE_NAME = "table_name";
4908 private static String KEY = "primary_key";
4909
4910 // These actions correspond to the occurrence of real actions. Since
4911 // these are called by the authenticators, the uid associated will be
4912 // of the authenticator.
4913 private static String ACTION_SET_PASSWORD = "action_set_password";
4914 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4915 private static String ACTION_ACCOUNT_ADD = "action_account_add";
4916 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004917 private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004918 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4919 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4920
4921 // These actions don't necessarily correspond to any action on
4922 // accountDb taking place. As an example, there might be a request for
4923 // addingAccount, which might not lead to addition of account on grounds
4924 // of bad authentication. We will still be logging it to keep track of
4925 // who called.
4926 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4927 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004928 private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004929
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004930 //This action doesn't add account to accountdb. Account is only
4931 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08004932 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004933 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4934 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08004935
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004936 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4937
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004938 private static void createDebugTable(SQLiteDatabase db) {
4939 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4940 + ACCOUNTS_ID + " INTEGER,"
4941 + ACTION_TYPE + " TEXT NOT NULL, "
4942 + TIMESTAMP + " DATETIME,"
4943 + CALLER_UID + " INTEGER NOT NULL,"
4944 + TABLE_NAME + " TEXT NOT NULL,"
4945 + KEY + " INTEGER PRIMARY KEY)");
4946 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4947 }
4948 }
4949
4950 private void logRecord(UserAccounts accounts, String action, String tableName) {
4951 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4952 logRecord(db, action, tableName, -1, accounts);
4953 }
4954
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004955 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4956 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4957 logRecord(db, action, tableName, -1, accounts, uid);
4958 }
4959
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004960 /*
4961 * This function receives an opened writable database.
4962 */
4963 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4964 UserAccounts userAccount) {
4965 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4966 }
4967
4968 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004969 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004970 */
4971 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4972 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004973
4974 class LogRecordTask implements Runnable {
4975 private final String action;
4976 private final String tableName;
4977 private final long accountId;
4978 private final UserAccounts userAccount;
4979 private final int callingUid;
4980 private final long userDebugDbInsertionPoint;
4981
4982 LogRecordTask(final String action,
4983 final String tableName,
4984 final long accountId,
4985 final UserAccounts userAccount,
4986 final int callingUid,
4987 final long userDebugDbInsertionPoint) {
4988 this.action = action;
4989 this.tableName = tableName;
4990 this.accountId = accountId;
4991 this.userAccount = userAccount;
4992 this.callingUid = callingUid;
4993 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4994 }
4995
4996 public void run() {
4997 SQLiteStatement logStatement = userAccount.statementForLogging;
4998 logStatement.bindLong(1, accountId);
4999 logStatement.bindString(2, action);
5000 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
5001 logStatement.bindLong(4, callingUid);
5002 logStatement.bindString(5, tableName);
5003 logStatement.bindLong(6, userDebugDbInsertionPoint);
5004 logStatement.execute();
5005 logStatement.clearBindings();
5006 }
5007 }
5008
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07005009 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
5010 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005011 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
5012 % MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07005013 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005014 }
5015
5016 /*
5017 * This should only be called once to compile the sql statement for logging
5018 * and to find the insertion point.
5019 */
5020 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
5021 UserAccounts userAccount) {
5022 // Initialize the count if not done earlier.
5023 int size = (int) getDebugTableRowCount(db);
5024 if (size >= MAX_DEBUG_DB_SIZE) {
5025 // Table is full, and we need to find the point where to insert.
5026 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
5027 } else {
5028 userAccount.debugDbInsertionPoint = size;
5029 }
5030 compileSqlStatementForLogging(db, userAccount);
5031 }
5032
5033 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
5034 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
5035 + " VALUES (?,?,?,?,?,?)";
5036 userAccount.statementForLogging = db.compileStatement(sql);
5037 }
5038
5039 private long getDebugTableRowCount(SQLiteDatabase db) {
5040 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
5041 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5042 }
5043
5044 /*
5045 * Finds the row key where the next insertion should take place. This should
5046 * be invoked only if the table has reached its full capacity.
5047 */
5048 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
5049 // This query finds the smallest timestamp value (and if 2 records have
5050 // same timestamp, the choose the lower id).
5051 String queryCountDebugDbRows = new StringBuilder()
5052 .append("SELECT ").append(DebugDbHelper.KEY)
5053 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
5054 .append(" ORDER BY ")
5055 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
5056 .append(" LIMIT 1")
5057 .toString();
5058 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5059 }
5060
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005061 static class PreNDatabaseHelper extends SQLiteOpenHelper {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005062 private final Context mContext;
5063 private final int mUserId;
5064
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005065 public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
5066 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005067 mContext = context;
5068 mUserId = userId;
Fred Quintana60307342009-03-24 22:48:12 -07005069 }
5070
5071 @Override
5072 public void onCreate(SQLiteDatabase db) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005073 // We use PreNDatabaseHelper only if pre-N db exists
5074 throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005075 }
5076
Amith Yamasani67df64b2012-12-14 12:09:36 -08005077 private void createSharedAccountsTable(SQLiteDatabase db) {
5078 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5079 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5080 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5081 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5082 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5083 }
5084
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005085 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
5086 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
5087 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
5088 }
5089
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005090 private void addOldAccountNameColumn(SQLiteDatabase db) {
5091 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
5092 }
5093
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005094 private void addDebugTable(SQLiteDatabase db) {
5095 DebugDbHelper.createDebugTable(db);
5096 }
5097
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005098 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005099 db.execSQL(""
5100 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5101 + " BEGIN"
5102 + " DELETE FROM " + TABLE_AUTHTOKENS
5103 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5104 + " DELETE FROM " + TABLE_EXTRAS
5105 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005106 + " DELETE FROM " + TABLE_GRANTS
5107 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07005108 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07005109 }
5110
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005111 private void createGrantsTable(SQLiteDatabase db) {
5112 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5113 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5114 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5115 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5116 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5117 + "," + GRANTS_GRANTEE_UID + "))");
5118 }
5119
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005120 private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db,
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005121 Map<String, Integer> authTypeAndUIDMap) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005122 for (Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) {
5123 AccountsDbUtils.insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005124 }
5125 }
5126
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005127 /**
5128 * Pre-N database may need an upgrade before splitting
5129 */
Fred Quintana60307342009-03-24 22:48:12 -07005130 @Override
5131 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005132 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07005133
Fred Quintanaa698f422009-04-08 19:14:54 -07005134 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005135 // no longer need to do anything since the work is done
5136 // when upgrading from version 2
5137 oldVersion++;
5138 }
5139
5140 if (oldVersion == 2) {
5141 createGrantsTable(db);
5142 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
5143 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07005144 oldVersion++;
5145 }
Costin Manolache3348f142009-09-29 18:58:36 -07005146
5147 if (oldVersion == 3) {
5148 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
5149 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
5150 oldVersion++;
5151 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08005152
5153 if (oldVersion == 4) {
5154 createSharedAccountsTable(db);
5155 oldVersion++;
5156 }
5157
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005158 if (oldVersion == 5) {
5159 addOldAccountNameColumn(db);
5160 oldVersion++;
5161 }
5162
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005163 if (oldVersion == 6) {
5164 addLastSuccessfullAuthenticatedTimeColumn(db);
5165 oldVersion++;
5166 }
5167
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005168 if (oldVersion == 7) {
5169 addDebugTable(db);
5170 oldVersion++;
5171 }
5172
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005173 if (oldVersion == 8) {
5174 populateMetaTableWithAuthTypeAndUID(
5175 db,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005176 getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005177 oldVersion++;
5178 }
5179
Amith Yamasani67df64b2012-12-14 12:09:36 -08005180 if (oldVersion != newVersion) {
5181 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5182 }
Fred Quintana60307342009-03-24 22:48:12 -07005183 }
5184
5185 @Override
5186 public void onOpen(SQLiteDatabase db) {
5187 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
5188 }
5189 }
5190
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005191 static class DeDatabaseHelper extends SQLiteOpenHelper {
5192
5193 private final int mUserId;
5194 private volatile boolean mCeAttached;
5195
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005196 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
5197 super(context, deDatabaseName, null, DE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005198 mUserId = userId;
5199 }
5200
5201 /**
5202 * This call needs to be made while the mCacheLock is held. The way to
5203 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
5204 * @param db The database.
5205 */
5206 @Override
5207 public void onCreate(SQLiteDatabase db) {
5208 Log.i(TAG, "Creating DE database for user " + mUserId);
5209 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5210 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
5211 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5212 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5213 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
5214 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
5215 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5216
5217 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
5218 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
5219 + META_VALUE + " TEXT)");
5220
5221 createGrantsTable(db);
5222 createSharedAccountsTable(db);
5223 createAccountsDeletionTrigger(db);
5224 DebugDbHelper.createDebugTable(db);
5225 }
5226
5227 private void createSharedAccountsTable(SQLiteDatabase db) {
5228 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5229 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5230 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5231 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5232 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5233 }
5234
5235 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5236 db.execSQL(""
5237 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5238 + " BEGIN"
5239 + " DELETE FROM " + TABLE_GRANTS
5240 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5241 + " END");
5242 }
5243
5244 private void createGrantsTable(SQLiteDatabase db) {
5245 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5246 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5247 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5248 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5249 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5250 + "," + GRANTS_GRANTEE_UID + "))");
5251 }
5252
5253 @Override
5254 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5255 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
5256
5257 if (oldVersion != newVersion) {
5258 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5259 }
5260 }
5261
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005262 public void attachCeDatabase(File ceDbFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005263 SQLiteDatabase db = getWritableDatabase();
5264 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
5265 mCeAttached = true;
5266 }
5267
5268 public boolean isCeDatabaseAttached() {
5269 return mCeAttached;
5270 }
5271
5272
5273 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
5274 if(!mCeAttached) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005275 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
5276 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005277 }
5278 return super.getReadableDatabase();
5279 }
5280
5281 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
5282 if(!mCeAttached) {
5283 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005284 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005285 }
5286 return super.getWritableDatabase();
5287 }
5288
5289 @Override
5290 public void onOpen(SQLiteDatabase db) {
5291 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
5292 }
5293
5294 private void migratePreNDbToDe(File preNDbFile) {
5295 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
5296 SQLiteDatabase db = getWritableDatabase();
5297 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb");
5298 db.beginTransaction();
5299 // Copy accounts fields
5300 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
5301 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5302 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5303 + ") "
5304 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5305 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5306 + " FROM preNDb." + TABLE_ACCOUNTS);
5307 // Copy SHARED_ACCOUNTS
5308 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
5309 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
5310 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
5311 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
5312 // Copy DEBUG_TABLE
5313 db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
5314 + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5315 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5316 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
5317 "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5318 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5319 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
5320 + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
5321 // Copy GRANTS
5322 db.execSQL("INSERT INTO " + TABLE_GRANTS
5323 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5324 + GRANTS_GRANTEE_UID + ") " +
5325 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5326 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
5327 // Copy META
5328 db.execSQL("INSERT INTO " + TABLE_META
5329 + "(" + META_KEY + "," + META_VALUE + ") "
5330 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
5331 db.setTransactionSuccessful();
5332 db.endTransaction();
5333
5334 db.execSQL("DETACH DATABASE preNDb");
5335 }
5336
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005337 static DeDatabaseHelper create(
5338 Context context,
5339 int userId,
5340 File preNDatabaseFile,
5341 File deDatabaseFile) {
5342 boolean newDbExists = deDatabaseFile.exists();
5343 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
5344 deDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005345 // If the db just created, and there is a legacy db, migrate it
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005346 if (!newDbExists && preNDatabaseFile.exists()) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005347 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005348 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
5349 preNDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005350 // Open the database to force upgrade if required
5351 preNDatabaseHelper.getWritableDatabase();
5352 preNDatabaseHelper.close();
5353 // Move data without SPII to DE
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005354 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005355 }
5356 return deDatabaseHelper;
5357 }
5358 }
5359
5360 static class CeDatabaseHelper extends SQLiteOpenHelper {
5361
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005362 public CeDatabaseHelper(Context context, String ceDatabaseName) {
5363 super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005364 }
5365
5366 /**
5367 * This call needs to be made while the mCacheLock is held.
5368 * @param db The database.
5369 */
5370 @Override
5371 public void onCreate(SQLiteDatabase db) {
5372 Log.i(TAG, "Creating CE database " + getDatabaseName());
5373 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5374 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5375 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5376 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5377 + ACCOUNTS_PASSWORD + " TEXT, "
5378 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5379
5380 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
5381 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5382 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5383 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
5384 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
5385 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
5386
5387 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
5388 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5389 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
5390 + EXTRAS_KEY + " TEXT NOT NULL, "
5391 + EXTRAS_VALUE + " TEXT, "
5392 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
5393
5394 createAccountsDeletionTrigger(db);
5395 }
5396
5397 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5398 db.execSQL(""
5399 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5400 + " BEGIN"
5401 + " DELETE FROM " + TABLE_AUTHTOKENS
5402 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5403 + " DELETE FROM " + TABLE_EXTRAS
5404 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5405 + " END");
5406 }
5407
5408 @Override
5409 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5410 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
5411
5412 if (oldVersion == 9) {
5413 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5414 Log.v(TAG, "onUpgrade upgrading to v10");
5415 }
5416 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
5417 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
5418 // Recreate the trigger, since the old one references the table to be removed
5419 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
5420 createAccountsDeletionTrigger(db);
5421 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
5422 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
5423 oldVersion ++;
5424 }
5425
5426 if (oldVersion != newVersion) {
5427 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5428 }
5429 }
5430
5431 @Override
5432 public void onOpen(SQLiteDatabase db) {
5433 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
5434 }
5435
Fyodor Kupolov35f68082016-04-06 12:14:17 -07005436
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005437 /**
5438 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
5439 * it also performs migration to the new CE database.
5440 * @param context
5441 * @param userId id of the user where the database is located
5442 */
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005443 static CeDatabaseHelper create(
5444 Context context,
5445 int userId,
5446 File preNDatabaseFile,
5447 File ceDatabaseFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005448 boolean newDbExists = ceDatabaseFile.exists();
5449 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5450 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005451 + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005452 }
5453 boolean removeOldDb = false;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005454 if (!newDbExists && preNDatabaseFile.exists()) {
5455 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005456 }
5457 // Try to open and upgrade if necessary
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005458 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005459 ceHelper.getWritableDatabase();
5460 ceHelper.close();
5461 if (removeOldDb) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005462 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
5463 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
5464 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
5465 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005466 }
5467 return ceHelper;
5468 }
5469
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005470 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005471 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005472 try {
5473 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
5474 } catch (IOException e) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005475 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005476 // Try to remove potentially damaged file if I/O error occurred
5477 deleteDbFileWarnIfFailed(ceDbFile);
5478 return false;
5479 }
5480 return true;
5481 }
5482 }
5483
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005484 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07005485 return asBinder();
5486 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005487
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005488 /**
5489 * Searches array of arguments for the specified string
5490 * @param args array of argument strings
5491 * @param value value to search for
5492 * @return true if the value is contained in the array
5493 */
5494 private static boolean scanArgs(String[] args, String value) {
5495 if (args != null) {
5496 for (String arg : args) {
5497 if (value.equals(arg)) {
5498 return true;
5499 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005500 }
5501 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005502 return false;
5503 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005504
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005505 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005506 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07005507 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
5508 != PackageManager.PERMISSION_GRANTED) {
5509 fout.println("Permission Denial: can't dump AccountsManager from from pid="
5510 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
5511 + " without permission " + android.Manifest.permission.DUMP);
5512 return;
5513 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005514 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005515 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07005516
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005517 final List<UserInfo> users = getUserManager().getUsers();
5518 for (UserInfo user : users) {
5519 ipw.println("User " + user + ":");
5520 ipw.increaseIndent();
5521 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5522 ipw.println();
5523 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08005524 }
5525 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005526
Amith Yamasani04e0d262012-02-14 11:50:53 -08005527 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5528 String[] args, boolean isCheckinRequest) {
5529 synchronized (userAccounts.cacheLock) {
5530 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005531
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005532 if (isCheckinRequest) {
5533 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005534 AccountsDbUtils.dumpAccountsTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005535 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005536 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07005537 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005538 fout.println("Accounts: " + accounts.length);
5539 for (Account account : accounts) {
5540 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005541 }
Fred Quintana307da1a2010-01-21 14:24:20 -08005542
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005543 // Add debug information.
5544 fout.println();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005545 AccountsDbUtils.dumpDebugTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005546 fout.println();
5547 synchronized (mSessions) {
5548 final long now = SystemClock.elapsedRealtime();
5549 fout.println("Active Sessions: " + mSessions.size());
5550 for (Session session : mSessions.values()) {
5551 fout.println(" " + session.toDebugString(now));
5552 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005553 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005554
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005555 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005556 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005557 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005558 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005559 }
5560
Amith Yamasani04e0d262012-02-14 11:50:53 -08005561 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005562 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005563 long identityToken = clearCallingIdentity();
5564 try {
5565 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5566 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5567 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005568
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005569 if (intent.getComponent() != null &&
5570 GrantCredentialsPermissionActivity.class.getName().equals(
5571 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005572 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005573 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005574 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005575 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07005576 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005577
Fred Quintana33f889a2009-09-14 17:31:26 -07005578 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005579 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005580 Notification n = new Notification.Builder(contextForUser)
5581 .setWhen(0)
5582 .setSmallIcon(android.R.drawable.stat_sys_warning)
5583 .setColor(contextForUser.getColor(
5584 com.android.internal.R.color.system_notification_accent_color))
5585 .setContentTitle(String.format(notificationTitleFormat, account.name))
5586 .setContentText(message)
5587 .setContentIntent(PendingIntent.getActivityAsUser(
5588 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005589 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005590 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005591 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005592 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005593 } finally {
5594 restoreCallingIdentity(identityToken);
5595 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005596 }
5597
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005598 @VisibleForTesting
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005599 protected void installNotification(int notificationId, final Notification notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005600 UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005601 installNotification(notificationId, notification, "android", user.getIdentifier());
5602 }
5603
5604 private void installNotification(int notificationId, final Notification notification,
5605 String packageName, int userId) {
5606 final long token = clearCallingIdentity();
5607 try {
5608 INotificationManager notificationManager = NotificationManager.getService();
5609 try {
5610 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
5611 notificationId, notification, new int[1], userId);
5612 } catch (RemoteException e) {
5613 /* ignore - local call */
5614 }
5615 } finally {
5616 Binder.restoreCallingIdentity(token);
5617 }
Fred Quintana56285a62010-12-02 14:20:51 -08005618 }
5619
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005620 @VisibleForTesting
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005621 protected void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005622 cancelNotification(id, mContext.getPackageName(), user);
5623 }
5624
5625 protected void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005626 long identityToken = clearCallingIdentity();
5627 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005628 INotificationManager service = INotificationManager.Stub.asInterface(
5629 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
5630 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
5631 } catch (RemoteException e) {
5632 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005633 } finally {
5634 restoreCallingIdentity(identityToken);
5635 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005636 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005637
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005638 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
5639 for (String perm : permissions) {
5640 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5641 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5642 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
5643 }
5644 final int opCode = AppOpsManager.permissionToOpCode(perm);
5645 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
5646 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5647 return true;
5648 }
5649 }
5650 }
5651 return false;
5652 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005653
Amith Yamasani67df64b2012-12-14 12:09:36 -08005654 private int handleIncomingUser(int userId) {
5655 try {
5656 return ActivityManagerNative.getDefault().handleIncomingUser(
5657 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5658 } catch (RemoteException re) {
5659 // Shouldn't happen, local.
5660 }
5661 return userId;
5662 }
5663
Christopher Tateccbf84f2013-05-08 15:25:41 -07005664 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005665 final int callingUserId = UserHandle.getUserId(callingUid);
5666
5667 final PackageManager userPackageManager;
5668 try {
5669 userPackageManager = mContext.createPackageContextAsUser(
5670 "android", 0, new UserHandle(callingUserId)).getPackageManager();
5671 } catch (NameNotFoundException e) {
5672 return false;
5673 }
5674
5675 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07005676 for (String name : packages) {
5677 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005678 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08005679 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08005680 && (packageInfo.applicationInfo.privateFlags
5681 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07005682 return true;
5683 }
5684 } catch (PackageManager.NameNotFoundException e) {
5685 return false;
5686 }
5687 }
5688 return false;
5689 }
5690
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005691 private boolean permissionIsGranted(
5692 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005693 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5694 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5695 Log.v(TAG, "Access to " + account + " granted calling uid is system");
5696 }
5697 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005698 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005699
5700 if (isPrivileged(callerUid)) {
5701 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5702 Log.v(TAG, "Access to " + account + " granted calling uid "
5703 + callerUid + " privileged");
5704 }
5705 return true;
5706 }
5707 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5708 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5709 Log.v(TAG, "Access to " + account + " granted calling uid "
5710 + callerUid + " manages the account");
5711 }
5712 return true;
5713 }
5714 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5715 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5716 Log.v(TAG, "Access to " + account + " granted calling uid "
5717 + callerUid + " user granted access");
5718 }
5719 return true;
5720 }
5721
5722 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5723 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5724 }
5725
5726 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005727 }
5728
Svetoslavf3f02ac2015-09-08 14:36:35 -07005729 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5730 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005731 if (accountType == null) {
5732 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005733 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005734 return getTypesVisibleToCaller(callingUid, userId,
5735 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005736 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005737 }
5738
5739 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5740 if (accountType == null) {
5741 return false;
5742 } else {
5743 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5744 }
5745 }
5746
Svetoslavf3f02ac2015-09-08 14:36:35 -07005747 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5748 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005749 boolean isPermitted =
5750 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5751 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005752 return getTypesForCaller(callingUid, userId, isPermitted);
5753 }
5754
5755 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5756 return getTypesForCaller(callingUid, userId, false);
5757 }
5758
5759 private List<String> getTypesForCaller(
5760 int callingUid, int userId, boolean isOtherwisePermitted) {
5761 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005762 long identityToken = Binder.clearCallingIdentity();
5763 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5764 try {
5765 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5766 } finally {
5767 Binder.restoreCallingIdentity(identityToken);
5768 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005769 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005770 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005771 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5772 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5773 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005774 }
5775 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005776 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005777 }
5778
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005779 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5780 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5781 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5782 if (account.name.equals(accountName)) {
5783 return true;
5784 }
5785 }
5786 }
5787 return false;
5788 }
5789
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005790 private static void checkManageUsersPermission(String message) {
5791 if (ActivityManager.checkComponentPermission(
5792 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5793 != PackageManager.PERMISSION_GRANTED) {
5794 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5795 }
5796 }
5797
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005798 private static void checkManageOrCreateUsersPermission(String message) {
5799 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5800 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5801 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5802 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5803 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5804 + message);
5805 }
5806 }
5807
Amith Yamasani04e0d262012-02-14 11:50:53 -08005808 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5809 int callerUid) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005810 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005811 return true;
5812 }
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005813 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005814 synchronized (accounts.cacheLock) {
5815 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005816 final String query;
5817 final String[] args;
5818
5819 if (authTokenType != null) {
5820 query = COUNT_OF_MATCHING_GRANTS;
5821 args = new String[] {String.valueOf(callerUid), authTokenType,
5822 account.name, account.type};
5823 } else {
5824 query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
5825 args = new String[] {String.valueOf(callerUid), account.name,
5826 account.type};
5827 }
5828 final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005829
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005830 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5831 // TODO: Skip this check when running automated tests. Replace this
5832 // with a more general solution.
5833 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005834 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005835 + " but ignoring since device is in test harness.");
5836 return true;
5837 }
5838 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005839 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005840 }
5841
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005842 private boolean isSystemUid(int callingUid) {
5843 String[] packages = null;
5844 long ident = Binder.clearCallingIdentity();
5845 try {
5846 packages = mPackageManager.getPackagesForUid(callingUid);
5847 } finally {
5848 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005849 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005850 if (packages != null) {
5851 for (String name : packages) {
5852 try {
5853 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5854 if (packageInfo != null
5855 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5856 != 0) {
5857 return true;
5858 }
5859 } catch (PackageManager.NameNotFoundException e) {
5860 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5861 }
5862 }
5863 } else {
5864 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005865 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005866 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005867 }
5868
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005869 /** Succeeds if any of the specified permissions are granted. */
5870 private void checkReadAccountsPermitted(
5871 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005872 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005873 int userId,
5874 String opPackageName) {
5875 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005876 String msg = String.format(
5877 "caller uid %s cannot access %s accounts",
5878 callingUid,
5879 accountType);
5880 Log.w(TAG, " " + msg);
5881 throw new SecurityException(msg);
5882 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005883 }
5884
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005885 private boolean canUserModifyAccounts(int userId, int callingUid) {
5886 // the managing app can always modify accounts
5887 if (isProfileOwner(callingUid)) {
5888 return true;
5889 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005890 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5891 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5892 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005893 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005894 return true;
5895 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005896
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005897 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5898 // the managing app can always modify accounts
5899 if (isProfileOwner(callingUid)) {
5900 return true;
5901 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005902 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5903 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005904 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005905 if (typesArray == null) {
5906 return true;
5907 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005908 for (String forbiddenType : typesArray) {
5909 if (forbiddenType.equals(accountType)) {
5910 return false;
5911 }
5912 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005913 return true;
5914 }
5915
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005916 private boolean isProfileOwner(int uid) {
5917 final DevicePolicyManagerInternal dpmi =
5918 LocalServices.getService(DevicePolicyManagerInternal.class);
5919 return (dpmi != null)
5920 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5921 }
5922
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005923 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005924 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5925 throws RemoteException {
5926 final int callingUid = getCallingUid();
5927
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005928 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005929 throw new SecurityException();
5930 }
5931
5932 if (value) {
5933 grantAppPermission(account, authTokenType, uid);
5934 } else {
5935 revokeAppPermission(account, authTokenType, uid);
5936 }
5937 }
5938
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005939 /**
5940 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5941 * <p>
5942 * Although this is public it can only be accessed via the AccountManagerService object
5943 * which is in the system. This means we don't need to protect it with permissions.
5944 * @hide
5945 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005946 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005947 if (account == null || authTokenType == null) {
5948 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005949 return;
5950 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005951 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005952 synchronized (accounts.cacheLock) {
5953 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005954 long accountId = AccountsDbUtils.findAccountId(db, account);
5955 if (accountId >= 0) {
5956 AccountsDbUtils.insertGrant(db, accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005957 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005958 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005959 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005960
5961 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005962 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005963
5964 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5965 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5966 : mAppPermissionChangeListeners) {
5967 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5968 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005969 }
5970
5971 /**
5972 * Don't allow callers with the given uid permission to get credentials for
5973 * account/authTokenType.
5974 * <p>
5975 * Although this is public it can only be accessed via the AccountManagerService object
5976 * which is in the system. This means we don't need to protect it with permissions.
5977 * @hide
5978 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005979 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005980 if (account == null || authTokenType == null) {
5981 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005982 return;
5983 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005984 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005985 synchronized (accounts.cacheLock) {
5986 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005987 db.beginTransaction();
5988 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005989 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005990 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005991 AccountsDbUtils.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5992 db, accountId, authTokenType, uid);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005993 db.setTransactionSuccessful();
5994 }
5995 } finally {
5996 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005997 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005998
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005999 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
6000 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07006001 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07006002
6003 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
6004 for (AccountManagerInternal.OnAppPermissionChangeListener listener
6005 : mAppPermissionChangeListeners) {
6006 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
6007 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07006008 }
Fred Quintana56285a62010-12-02 14:20:51 -08006009
6010 static final private String stringArrayToString(String[] value) {
6011 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
6012 }
6013
Amith Yamasani04e0d262012-02-14 11:50:53 -08006014 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
6015 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006016 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006017 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006018 for (Account curAccount : oldAccountsForType) {
6019 if (!curAccount.equals(account)) {
6020 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006021 }
6022 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006023 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006024 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006025 } else {
6026 Account[] newAccountsForType = new Account[newAccountsList.size()];
6027 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006028 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006029 }
Fred Quintana56285a62010-12-02 14:20:51 -08006030 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08006031 accounts.userDataCache.remove(account);
6032 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07006033 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006034 }
6035
6036 /**
6037 * This assumes that the caller has already checked that the account is not already present.
6038 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08006039 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
6040 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006041 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
6042 Account[] newAccountsForType = new Account[oldLength + 1];
6043 if (accountsForType != null) {
6044 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08006045 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07006046 newAccountsForType[oldLength] = new Account(account, new AccountAccessTracker());
Amith Yamasani04e0d262012-02-14 11:50:53 -08006047 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08006048 }
6049
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006050 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07006051 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006052 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07006053 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006054 return unfiltered;
6055 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07006056 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07006057 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006058 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006059 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006060 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006061 // This might be a temporary way to specify a visible list
6062 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006063 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
6064 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006065 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006066 return unfiltered;
6067 }
6068 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006069 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006070 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
6071 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006072 String requiredAccountType = "";
6073 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07006074 // If there's an explicit callingPackage specified, check if that package
6075 // opted in to see restricted accounts.
6076 if (callingPackage != null) {
6077 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006078 if (pi != null && pi.restrictedAccountType != null) {
6079 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07006080 }
6081 } else {
6082 // Otherwise check if the callingUid has a package that has opted in
6083 for (String packageName : packages) {
6084 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
6085 if (pi != null && pi.restrictedAccountType != null) {
6086 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07006087 break;
6088 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006089 }
6090 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006091 } catch (NameNotFoundException nnfe) {
6092 }
6093 for (Account account : unfiltered) {
6094 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006095 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006096 } else {
6097 boolean found = false;
6098 for (Account shared : sharedAccounts) {
6099 if (shared.equals(account)) {
6100 found = true;
6101 break;
6102 }
6103 }
6104 if (!found) {
6105 allowed.add(account);
6106 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006107 }
6108 }
6109 Account[] filtered = new Account[allowed.size()];
6110 allowed.toArray(filtered);
6111 return filtered;
6112 } else {
6113 return unfiltered;
6114 }
6115 }
6116
Amith Yamasani27db4682013-03-30 17:07:47 -07006117 /*
6118 * packageName can be null. If not null, it should be used to filter out restricted accounts
6119 * that the package is not allowed to access.
6120 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006121 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07006122 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006123 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006124 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006125 if (accounts == null) {
6126 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08006127 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006128 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07006129 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006130 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006131 } else {
6132 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006133 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006134 totalLength += accounts.length;
6135 }
6136 if (totalLength == 0) {
6137 return EMPTY_ACCOUNT_ARRAY;
6138 }
6139 Account[] accounts = new Account[totalLength];
6140 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006141 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006142 System.arraycopy(accountsOfType, 0, accounts, totalLength,
6143 accountsOfType.length);
6144 totalLength += accountsOfType.length;
6145 }
Amith Yamasani27db4682013-03-30 17:07:47 -07006146 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006147 }
6148 }
6149
Amith Yamasani04e0d262012-02-14 11:50:53 -08006150 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6151 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006152 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006153 if (userDataForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006154 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006155 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006156 }
6157 if (value == null) {
6158 userDataForAccount.remove(key);
6159 } else {
6160 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006161 }
6162 }
6163
Carlos Valdivia91979be2015-05-22 14:11:35 -07006164 protected String readCachedTokenInternal(
6165 UserAccounts accounts,
6166 Account account,
6167 String tokenType,
6168 String callingPackage,
6169 byte[] pkgSigDigest) {
6170 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07006171 return accounts.accountTokenCaches.get(
6172 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07006173 }
6174 }
6175
Amith Yamasani04e0d262012-02-14 11:50:53 -08006176 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6177 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006178 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006179 if (authTokensForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006180 authTokensForAccount = AccountsDbUtils.findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006181 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006182 }
6183 if (value == null) {
6184 authTokensForAccount.remove(key);
6185 } else {
6186 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006187 }
6188 }
6189
Amith Yamasani04e0d262012-02-14 11:50:53 -08006190 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
6191 String authTokenType) {
6192 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006193 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006194 if (authTokensForAccount == null) {
6195 // need to populate the cache for this account
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07006196 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006197 authTokensForAccount = AccountsDbUtils
6198 .findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006199 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006200 }
6201 return authTokensForAccount.get(authTokenType);
6202 }
6203 }
6204
Simranjit Kohli858511c2016-03-10 18:36:11 +00006205 protected String readUserDataInternalLocked(
6206 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006207 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006208 if (userDataForAccount == null) {
6209 // need to populate the cache for this account
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07006210 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006211 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006212 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006213 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00006214 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08006215 }
6216
Kenny Guy07ad8dc2014-09-01 20:56:12 +01006217 private Context getContextForUser(UserHandle user) {
6218 try {
6219 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
6220 } catch (NameNotFoundException e) {
6221 // Default to mContext, not finding the package system is running as is unlikely.
6222 return mContext;
6223 }
6224 }
Sandra Kwan78812282015-11-04 11:19:47 -08006225
6226 private void sendResponse(IAccountManagerResponse response, Bundle result) {
6227 try {
6228 response.onResult(result);
6229 } catch (RemoteException e) {
6230 // if the caller is dead then there is no one to care about remote
6231 // exceptions
6232 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6233 Log.v(TAG, "failure while notifying response", e);
6234 }
6235 }
6236 }
6237
6238 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
6239 String errorMessage) {
6240 try {
6241 response.onError(errorCode, errorMessage);
6242 } catch (RemoteException e) {
6243 // if the caller is dead then there is no one to care about remote
6244 // exceptions
6245 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6246 Log.v(TAG, "failure while notifying response", e);
6247 }
6248 }
6249 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006250
6251 static class AccountsDbUtils {
6252
6253 static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
6254 String type) {
6255 Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
6256 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6257 new String[]{name, type}, null, null, null);
6258 try {
6259 if (cursor.moveToNext()) {
6260 return cursor.getString(0);
6261 }
6262 return null;
6263 } finally {
6264 cursor.close();
6265 }
6266 }
6267
6268 static Map<Long, Account> findAllAccounts(SQLiteDatabase db) {
6269 LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
6270 Cursor cursor = db.query(TABLE_ACCOUNTS,
6271 new String[] {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
6272 null, null, null, null, ACCOUNTS_ID);
6273 try {
6274 while (cursor.moveToNext()) {
6275 final long accountId = cursor.getLong(0);
6276 final String accountType = cursor.getString(1);
6277 final String accountName = cursor.getString(2);
6278
6279 final Account account = new Account(accountName, accountType);
6280 map.put(accountId, account);
6281 }
6282 } finally {
6283 cursor.close();
6284 }
6285 return map;
6286 }
6287
6288 static String findAccountPreviousName(SQLiteDatabase db, Account account) {
6289 Cursor cursor = db.query(
6290 TABLE_ACCOUNTS,
6291 new String[]{ ACCOUNTS_PREVIOUS_NAME },
6292 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6293 new String[] { account.name, account.type },
6294 null,
6295 null,
6296 null);
6297 try {
6298 if (cursor.moveToNext()) {
6299 return cursor.getString(0);
6300 }
6301 } finally {
6302 cursor.close();
6303 }
6304 return null;
6305 }
6306
6307 static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
6308 // Select accounts from CE that do not exist in DE
6309 Cursor cursor = db.rawQuery(
6310 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
6311 + " FROM " + CE_TABLE_ACCOUNTS
6312 + " WHERE NOT EXISTS "
6313 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
6314 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6315 + " )", null);
6316 try {
6317 List<Account> accounts = new ArrayList<>(cursor.getCount());
6318 while (cursor.moveToNext()) {
6319 String accountName = cursor.getString(0);
6320 String accountType = cursor.getString(1);
6321 accounts.add(new Account(accountName, accountType));
6322 }
6323 return accounts;
6324 } finally {
6325 cursor.close();
6326 }
6327 }
6328
6329 static boolean deleteAccount(SQLiteDatabase db, long accountId) {
6330 return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6331 }
6332
6333 static boolean deleteCeAccount(SQLiteDatabase db, long accountId) {
6334 return db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6335 }
6336
6337 /**
6338 * Returns information about auth tokens and their account for the specified query parameters.
6339 * Output is in the format:
6340 * <pre><code> | AUTHTOKEN_ID | ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
6341 */
6342 static Cursor findAuthtokenForAllAccounts(SQLiteDatabase db, String accountType,
6343 String authToken) {
6344 return db.rawQuery(
6345 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
6346 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
6347 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
6348 + " FROM " + CE_TABLE_ACCOUNTS
6349 + " JOIN " + CE_TABLE_AUTHTOKENS
6350 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6351 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
6352 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
6353 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
6354 new String[]{authToken, accountType});
6355 }
6356
6357 static boolean deleteAuthtokensByAccountIdAndType(SQLiteDatabase db, long accountId,
6358 String authtokenType) {
6359 return db.delete(CE_TABLE_AUTHTOKENS,
6360 AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
6361 new String[] {String.valueOf(accountId), authtokenType}) > 0;
6362 }
6363
6364 static boolean deleteAuthToken(SQLiteDatabase db, String authTokenId) {
6365 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
6366 new String[] {authTokenId}) > 0;
6367 }
6368
6369 static long insertAuthToken(SQLiteDatabase db, long accountId, String authTokenType,
6370 String authToken) {
6371 ContentValues values = new ContentValues();
6372 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
6373 values.put(AUTHTOKENS_TYPE, authTokenType);
6374 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
6375 return db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
6376 }
6377
6378 static Map<String, String> findAuthTokensByAccount(final SQLiteDatabase db, Account account) {
6379 HashMap<String, String> authTokensForAccount = new HashMap<>();
6380 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
6381 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
6382 SELECTION_AUTHTOKENS_BY_ACCOUNT,
6383 new String[]{account.name, account.type},
6384 null, null, null);
6385 try {
6386 while (cursor.moveToNext()) {
6387 final String type = cursor.getString(0);
6388 final String authToken = cursor.getString(1);
6389 authTokensForAccount.put(type, authToken);
6390 }
6391 } finally {
6392 cursor.close();
6393 }
6394 return authTokensForAccount;
6395 }
6396
6397 static int updateAccountPassword(SQLiteDatabase db, long accountId, String password) {
6398 final ContentValues values = new ContentValues();
6399 values.put(ACCOUNTS_PASSWORD, password);
6400 return db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
6401 new String[] {String.valueOf(accountId)});
6402 }
6403
6404 static boolean deleteAuthTokensByAccountId(SQLiteDatabase db, long accountId) {
6405 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
6406 new String[] {String.valueOf(accountId)}) > 0;
6407 }
6408
6409 static long insertSharedAccount(SQLiteDatabase db, Account account) {
6410 ContentValues values = new ContentValues();
6411 values.put(ACCOUNTS_NAME, account.name);
6412 values.put(ACCOUNTS_TYPE, account.type);
6413 return db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
6414 }
6415
6416 static boolean deleteSharedAccount(SQLiteDatabase db, Account account) {
6417 return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6418 new String[] {account.name, account.type}) > 0;
6419 }
6420
6421 static int renameSharedAccount(SQLiteDatabase db, Account account, String newName) {
6422 final ContentValues values = new ContentValues();
6423 values.put(ACCOUNTS_NAME, newName);
6424 return db.update(TABLE_SHARED_ACCOUNTS,
6425 values,
6426 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6427 new String[] { account.name, account.type });
6428 }
6429
6430 static List<Account> getSharedAccounts(SQLiteDatabase db) {
6431 ArrayList<Account> accountList = new ArrayList<>();
6432 Cursor cursor = null;
6433 try {
6434 cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE},
6435 null, null, null, null, null);
6436 if (cursor != null && cursor.moveToFirst()) {
6437 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
6438 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
6439 do {
6440 accountList.add(new Account(cursor.getString(nameIndex),
6441 cursor.getString(typeIndex)));
6442 } while (cursor.moveToNext());
6443 }
6444 } finally {
6445 if (cursor != null) {
6446 cursor.close();
6447 }
6448 }
6449 return accountList;
6450 }
6451
6452 static long findSharedAccountId(SQLiteDatabase db, Account account) {
6453 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
6454 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6455 try {
6456 if (cursor.moveToNext()) {
6457 return cursor.getLong(0);
6458 }
6459 return -1;
6460 } finally {
6461 cursor.close();
6462 }
6463 }
6464
6465 static long findAccountId(SQLiteDatabase db, Account account) {
6466 Cursor cursor = db.query(
6467 TABLE_ACCOUNTS, new String[] {ACCOUNTS_ID},
6468 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6469 try {
6470 if (cursor.moveToNext()) {
6471 return cursor.getLong(0);
6472 }
6473 return -1;
6474 } finally {
6475 cursor.close();
6476 }
6477 }
6478
6479 static long findExtrasIdByAccountId(SQLiteDatabase db, long accountId, String key) {
6480 Cursor cursor = db.query(
6481 CE_TABLE_EXTRAS, new String[] {EXTRAS_ID},
6482 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
6483 new String[]{key}, null, null, null);
6484 try {
6485 if (cursor.moveToNext()) {
6486 return cursor.getLong(0);
6487 }
6488 return -1;
6489 } finally {
6490 cursor.close();
6491 }
6492 }
6493
6494 static boolean updateExtra(SQLiteDatabase db, long extrasId, String value) {
6495 ContentValues values = new ContentValues();
6496 values.put(EXTRAS_VALUE, value);
6497 int rows = db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=?",
6498 new String[]{String.valueOf(extrasId)});
6499 return rows == 1;
6500 }
6501
6502 static long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
6503 ContentValues values = new ContentValues();
6504 values.put(EXTRAS_KEY, key);
6505 values.put(EXTRAS_ACCOUNTS_ID, accountId);
6506 values.put(EXTRAS_VALUE, value);
6507 return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
6508 }
6509
6510 static Map<String, String> findUserExtrasForAccount(SQLiteDatabase db, Account account) {
6511 Map<String, String> userExtrasForAccount = new HashMap<>();
6512 Cursor cursor = db.query(CE_TABLE_EXTRAS,
6513 COLUMNS_EXTRAS_KEY_AND_VALUE,
6514 SELECTION_USERDATA_BY_ACCOUNT,
6515 new String[] {account.name, account.type},
6516 null, null, null);
6517 try {
6518 while (cursor.moveToNext()) {
6519 final String tmpkey = cursor.getString(0);
6520 final String value = cursor.getString(1);
6521 userExtrasForAccount.put(tmpkey, value);
6522 }
6523 } finally {
6524 cursor.close();
6525 }
6526 return userExtrasForAccount;
6527 }
6528
6529 static long insertGrant(SQLiteDatabase db, long accountId, String authTokenType, int uid) {
6530 ContentValues values = new ContentValues();
6531 values.put(GRANTS_ACCOUNTS_ID, accountId);
6532 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
6533 values.put(GRANTS_GRANTEE_UID, uid);
6534 return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
6535 }
6536
6537 static boolean deleteGrantsByUid(SQLiteDatabase db, int uid) {
6538 return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
6539 new String[] {Integer.toString(uid)}) > 0;
6540 }
6541
6542 static boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(SQLiteDatabase db, long accountId, String authTokenType, long uid) {
6543 return db.delete(TABLE_GRANTS,
6544 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
6545 + GRANTS_GRANTEE_UID + "=?",
6546 new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0;
6547 }
6548
6549 static List<Integer> findAllUidGrants(SQLiteDatabase db) {
6550 List<Integer> result = new ArrayList<>();
6551 final Cursor cursor = db.query(TABLE_GRANTS,
6552 new String[] {GRANTS_GRANTEE_UID},
6553 null, null, GRANTS_GRANTEE_UID, null, null);
6554 try {
6555 while (cursor.moveToNext()) {
6556 final int uid = cursor.getInt(0);
6557 result.add(uid);
6558 }
6559 } finally {
6560 cursor.close();
6561 }
6562 return result;
6563 }
6564
6565 static long findMatchingGrantsCount(SQLiteDatabase db,
6566 int uid, String authTokenType, Account account) {
6567 String[] args = { String.valueOf(uid), authTokenType,
6568 account.name, account.type};
6569 return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
6570 }
6571
6572 static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
6573 ContentValues values = new ContentValues();
6574 values.put(META_KEY,
6575 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6576 values.put(META_VALUE, uid);
6577 return db.insert(TABLE_META, null, values);
6578 }
6579
6580 static long insertOrReplaceMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType,
6581 int uid) {
6582 ContentValues values = new ContentValues();
6583 values.put(META_KEY,
6584 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6585 values.put(META_VALUE, uid);
6586 return db.insertWithOnConflict(TABLE_META, null, values,
6587 SQLiteDatabase.CONFLICT_REPLACE);
6588 }
6589
6590
6591 static Map<String, Integer> findMetaAuthUid(SQLiteDatabase db) {
6592 Cursor metaCursor = db.query(
6593 TABLE_META,
6594 new String[] {META_KEY, META_VALUE},
6595 SELECTION_META_BY_AUTHENTICATOR_TYPE,
6596 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
6597 null /* groupBy */,
6598 null /* having */,
6599 META_KEY);
6600 Map<String, Integer> map = new LinkedHashMap<>();
6601 try {
6602 while (metaCursor.moveToNext()) {
6603 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
6604 String uidStr = metaCursor.getString(1);
6605 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
6606 // Should never happen.
6607 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
6608 + ", uid empty: " + TextUtils.isEmpty(uidStr));
6609 continue;
6610 }
6611 int uid = Integer.parseInt(metaCursor.getString(1));
6612 map.put(type, uid);
6613 }
6614 } finally {
6615 metaCursor.close();
6616 }
6617 return map;
6618 }
6619
6620 static boolean deleteMetaByAuthTypeAndUid(SQLiteDatabase db, String type, int uid) {
6621 return db.delete(
6622 TABLE_META,
6623 META_KEY + "=? AND " + META_VALUE + "=?",
6624 new String[] {
6625 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
6626 String.valueOf(uid)}
6627 ) > 0;
6628 }
6629
6630 static void dumpAccountsTable(SQLiteDatabase db, PrintWriter pw) {
6631 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
6632 null, null, ACCOUNTS_TYPE, null, null);
6633 try {
6634 while (cursor.moveToNext()) {
6635 // print type,count
6636 pw.println(cursor.getString(0) + "," + cursor.getString(1));
6637 }
6638 } finally {
6639 if (cursor != null) {
6640 cursor.close();
6641 }
6642 }
6643 }
6644
6645 static void dumpDebugTable(SQLiteDatabase db, PrintWriter pw) {
6646 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
6647 null, null, null, null, DebugDbHelper.TIMESTAMP);
6648 pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
6649 pw.println("Accounts History");
6650 try {
6651 while (cursor.moveToNext()) {
6652 // print type,count
6653 pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
6654 cursor.getString(2) + "," + cursor.getString(3) + ","
6655 + cursor.getString(4) + "," + cursor.getString(5));
6656 }
6657 } finally {
6658 cursor.close();
6659 }
6660 }
Svet Ganov890a2102016-08-24 00:08:00 -07006661 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006662
Svet Ganovf6d424f12016-09-20 20:18:53 -07006663 private final class AccountAccessTracker extends IAccountAccessTracker.Stub {
6664 @Override
6665 public void onAccountAccessed() throws RemoteException {
6666 final int uid = Binder.getCallingUid();
6667 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
6668 return;
6669 }
6670 final int userId = UserHandle.getCallingUserId();
6671 final long identity = Binder.clearCallingIdentity();
6672 try {
6673 for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
6674 IAccountAccessTracker accountTracker = account.getAccessTracker();
6675 if (accountTracker != null && asBinder() == accountTracker.asBinder()) {
6676 // An app just accessed the account. At this point it knows about
6677 // it and there is not need to hide this account from the app.
6678 if (!hasAccountAccess(account, null, uid)) {
6679 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
6680 uid, true);
6681 }
6682 }
6683 }
6684 } finally {
6685 Binder.restoreCallingIdentity(identity);
6686 }
6687 }
6688 }
6689
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006690 private final class AccountManagerInternalImpl extends AccountManagerInternal {
6691 @Override
6692 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
6693 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
6694 if (account == null) {
6695 Slog.w(TAG, "account cannot be null");
6696 return;
6697 }
6698 if (packageName == null) {
6699 Slog.w(TAG, "packageName cannot be null");
6700 return;
6701 }
6702 if (userId < UserHandle.USER_SYSTEM) {
6703 Slog.w(TAG, "user id must be concrete");
6704 return;
6705 }
6706 if (callback == null) {
6707 Slog.w(TAG, "callback cannot be null");
6708 return;
6709 }
6710
Svet Ganovf6d424f12016-09-20 20:18:53 -07006711 if (AccountManagerService.this.hasAccountAccess(account, packageName,
6712 new UserHandle(userId))) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006713 Bundle result = new Bundle();
6714 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
6715 callback.sendResult(result);
6716 return;
6717 }
6718
6719 final int uid;
6720 try {
6721 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
6722 } catch (NameNotFoundException e) {
6723 Slog.e(TAG, "Unknown package " + packageName);
6724 return;
6725 }
6726
6727 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
Svet Ganovf6d424f12016-09-20 20:18:53 -07006728 final UserAccounts userAccounts;
6729 synchronized (mUsers) {
6730 userAccounts = mUsers.get(userId);
6731 }
6732 doNotification(userAccounts, account, null, intent, packageName, userId);
6733 }
6734
6735 @Override
6736 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
6737 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
6738 mAppPermissionChangeListeners.add(listener);
6739 }
6740
6741 @Override
6742 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
6743 return AccountManagerService.this.hasAccountAccess(account, null, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006744 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006745 }
Fred Quintana60307342009-03-24 22:48:12 -07006746}