1 /* 2 * Copyright (C) 2012 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 17 package com.android.server; 18 19 import android.app.ActivityManagerNative; 20 import android.app.KeyguardManager; 21 import android.app.Notification; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.app.admin.DevicePolicyManager; 25 import android.app.backup.BackupManager; 26 import android.app.trust.IStrongAuthTracker; 27 import android.app.trust.TrustManager; 28 import android.content.BroadcastReceiver; 29 import android.content.ContentResolver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.PackageManager; 34 import android.content.pm.UserInfo; 35 import android.content.res.Resources; 36 37 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; 38 import static android.content.Context.KEYGUARD_SERVICE; 39 import static android.content.Context.USER_SERVICE; 40 import static android.Manifest.permission.READ_CONTACTS; 41 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; 42 43 import android.database.sqlite.SQLiteDatabase; 44 import android.os.Binder; 45 import android.os.Bundle; 46 import android.os.Handler; 47 import android.os.IBinder; 48 import android.os.IProgressListener; 49 import android.os.Parcel; 50 import android.os.Process; 51 import android.os.RemoteException; 52 import android.os.storage.IMountService; 53 import android.os.storage.StorageManager; 54 import android.os.ServiceManager; 55 import android.os.SystemProperties; 56 import android.os.UserHandle; 57 import android.os.UserManager; 58 import android.provider.Settings; 59 import android.provider.Settings.Secure; 60 import android.provider.Settings.SettingNotFoundException; 61 import android.security.KeyStore; 62 import android.security.keystore.AndroidKeyStoreProvider; 63 import android.security.keystore.KeyProperties; 64 import android.security.keystore.KeyProtection; 65 import android.service.gatekeeper.GateKeeperResponse; 66 import android.service.gatekeeper.IGateKeeperService; 67 import android.text.TextUtils; 68 import android.util.Log; 69 import android.util.Slog; 70 71 import com.android.internal.util.ArrayUtils; 72 import com.android.internal.widget.ICheckCredentialProgressCallback; 73 import com.android.internal.widget.ILockSettings; 74 import com.android.internal.widget.LockPatternUtils; 75 import com.android.internal.widget.VerifyCredentialResponse; 76 import com.android.server.LockSettingsStorage.CredentialHash; 77 78 import libcore.util.HexEncoding; 79 80 import java.io.ByteArrayOutputStream; 81 import java.io.FileNotFoundException; 82 import java.io.IOException; 83 import java.nio.charset.StandardCharsets; 84 import java.security.InvalidAlgorithmParameterException; 85 import java.security.InvalidKeyException; 86 import java.security.KeyStoreException; 87 import java.security.MessageDigest; 88 import java.security.NoSuchAlgorithmException; 89 import java.security.SecureRandom; 90 import java.security.UnrecoverableKeyException; 91 import java.security.cert.CertificateException; 92 import java.util.Arrays; 93 import java.util.List; 94 import java.util.concurrent.CountDownLatch; 95 import java.util.concurrent.TimeUnit; 96 97 import javax.crypto.BadPaddingException; 98 import javax.crypto.Cipher; 99 import javax.crypto.IllegalBlockSizeException; 100 import javax.crypto.KeyGenerator; 101 import javax.crypto.NoSuchPaddingException; 102 import javax.crypto.SecretKey; 103 import javax.crypto.spec.GCMParameterSpec; 104 105 /** 106 * Keeps the lock pattern/password data and related settings for each user. 107 * Used by LockPatternUtils. Needs to be a service because Settings app also needs 108 * to be able to save lockscreen information for secondary users. 109 * @hide 110 */ 111 public class LockSettingsService extends ILockSettings.Stub { 112 private static final String TAG = "LockSettingsService"; 113 private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE; 114 private static final Intent ACTION_NULL; // hack to ensure notification shows the bouncer 115 private static final int FBE_ENCRYPTED_NOTIFICATION = 0; 116 private static final boolean DEBUG = false; 117 118 private static final int PROFILE_KEY_IV_SIZE = 12; 119 private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge"; 120 private final Object mSeparateChallengeLock = new Object(); 121 122 private final Context mContext; 123 private final Handler mHandler; 124 private final LockSettingsStorage mStorage; 125 private final LockSettingsStrongAuth mStrongAuth; 126 private final SynchronizedStrongAuthTracker mStrongAuthTracker; 127 128 private LockPatternUtils mLockPatternUtils; 129 private boolean mFirstCallToVold; 130 private IGateKeeperService mGateKeeperService; 131 private NotificationManager mNotificationManager; 132 private UserManager mUserManager; 133 134 private final KeyStore mKeyStore = KeyStore.getInstance(); 135 136 /** 137 * The UIDs that are used for system credential storage in keystore. 138 */ 139 private static final int[] SYSTEM_CREDENTIAL_UIDS = {Process.WIFI_UID, Process.VPN_UID, 140 Process.ROOT_UID, Process.SYSTEM_UID}; 141 142 static { 143 // Just launch the home screen, which happens anyway 144 ACTION_NULL = new Intent(Intent.ACTION_MAIN); 145 ACTION_NULL.addCategory(Intent.CATEGORY_HOME); 146 } 147 148 private interface CredentialUtil { setCredential(String credential, String savedCredential, int userId)149 void setCredential(String credential, String savedCredential, int userId) 150 throws RemoteException; toHash(String credential, int userId)151 byte[] toHash(String credential, int userId); adjustForKeystore(String credential)152 String adjustForKeystore(String credential); 153 } 154 155 // This class manages life cycle events for encrypted users on File Based Encryption (FBE) 156 // devices. The most basic of these is to show/hide notifications about missing features until 157 // the user unlocks the account and credential-encrypted storage is available. 158 public static final class Lifecycle extends SystemService { 159 private LockSettingsService mLockSettingsService; 160 Lifecycle(Context context)161 public Lifecycle(Context context) { 162 super(context); 163 } 164 165 @Override onStart()166 public void onStart() { 167 AndroidKeyStoreProvider.install(); 168 mLockSettingsService = new LockSettingsService(getContext()); 169 publishBinderService("lock_settings", mLockSettingsService); 170 } 171 172 @Override onBootPhase(int phase)173 public void onBootPhase(int phase) { 174 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 175 mLockSettingsService.maybeShowEncryptionNotifications(); 176 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { 177 // TODO 178 } 179 } 180 181 @Override onUnlockUser(int userHandle)182 public void onUnlockUser(int userHandle) { 183 mLockSettingsService.onUnlockUser(userHandle); 184 } 185 186 @Override onCleanupUser(int userHandle)187 public void onCleanupUser(int userHandle) { 188 mLockSettingsService.onCleanupUser(userHandle); 189 } 190 } 191 192 private class SynchronizedStrongAuthTracker extends LockPatternUtils.StrongAuthTracker { SynchronizedStrongAuthTracker(Context context)193 public SynchronizedStrongAuthTracker(Context context) { 194 super(context); 195 } 196 197 @Override handleStrongAuthRequiredChanged(int strongAuthFlags, int userId)198 protected void handleStrongAuthRequiredChanged(int strongAuthFlags, int userId) { 199 synchronized (this) { 200 super.handleStrongAuthRequiredChanged(strongAuthFlags, userId); 201 } 202 } 203 204 @Override getStrongAuthForUser(int userId)205 public int getStrongAuthForUser(int userId) { 206 synchronized (this) { 207 return super.getStrongAuthForUser(userId); 208 } 209 } 210 register()211 void register() { 212 mStrongAuth.registerStrongAuthTracker(this.mStub); 213 } 214 } 215 216 /** 217 * Tie managed profile to primary profile if it is in unified mode and not tied before. 218 * 219 * @param managedUserId Managed profile user Id 220 * @param managedUserPassword Managed profile original password (when it has separated lock). 221 * NULL when it does not have a separated lock before. 222 */ tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword)223 public void tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword) { 224 if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId); 225 // Only for managed profile 226 if (!UserManager.get(mContext).getUserInfo(managedUserId).isManagedProfile()) { 227 return; 228 } 229 // Do not tie managed profile when work challenge is enabled 230 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) { 231 return; 232 } 233 // Do not tie managed profile to parent when it's done already 234 if (mStorage.hasChildProfileLock(managedUserId)) { 235 return; 236 } 237 // Do not tie it to parent when parent does not have a screen lock 238 final int parentId = mUserManager.getProfileParent(managedUserId).id; 239 if (!mStorage.hasPassword(parentId) && !mStorage.hasPattern(parentId)) { 240 if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock"); 241 return; 242 } 243 if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!"); 244 byte[] randomLockSeed = new byte[] {}; 245 try { 246 randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40); 247 String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed)); 248 setLockPasswordInternal(newPassword, managedUserPassword, managedUserId); 249 // We store a private credential for the managed user that's unlocked by the primary 250 // account holder's credential. As such, the user will never be prompted to enter this 251 // password directly, so we always store a password. 252 setLong(LockPatternUtils.PASSWORD_TYPE_KEY, 253 DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, managedUserId); 254 tieProfileLockToParent(managedUserId, newPassword); 255 } catch (NoSuchAlgorithmException | RemoteException e) { 256 Slog.e(TAG, "Fail to tie managed profile", e); 257 // Nothing client can do to fix this issue, so we do not throw exception out 258 } 259 } 260 LockSettingsService(Context context)261 public LockSettingsService(Context context) { 262 mContext = context; 263 mHandler = new Handler(); 264 mStrongAuth = new LockSettingsStrongAuth(context); 265 // Open the database 266 267 mLockPatternUtils = new LockPatternUtils(context); 268 mFirstCallToVold = true; 269 270 IntentFilter filter = new IntentFilter(); 271 filter.addAction(Intent.ACTION_USER_ADDED); 272 filter.addAction(Intent.ACTION_USER_STARTING); 273 filter.addAction(Intent.ACTION_USER_REMOVED); 274 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); 275 276 mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() { 277 @Override 278 public void initialize(SQLiteDatabase db) { 279 // Get the lockscreen default from a system property, if available 280 boolean lockScreenDisable = SystemProperties.getBoolean( 281 "ro.lockscreen.disable.default", false); 282 if (lockScreenDisable) { 283 mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0); 284 } 285 } 286 }); 287 mNotificationManager = (NotificationManager) 288 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 289 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 290 mStrongAuthTracker = new SynchronizedStrongAuthTracker(mContext); 291 mStrongAuthTracker.register(); 292 293 } 294 295 /** 296 * If the account is credential-encrypted, show notification requesting the user to unlock 297 * the device. 298 */ maybeShowEncryptionNotifications()299 private void maybeShowEncryptionNotifications() { 300 final List<UserInfo> users = mUserManager.getUsers(); 301 for (int i = 0; i < users.size(); i++) { 302 UserInfo user = users.get(i); 303 UserHandle userHandle = user.getUserHandle(); 304 final boolean isSecure = mStorage.hasPassword(user.id) || mStorage.hasPattern(user.id); 305 if (isSecure && !mUserManager.isUserUnlockingOrUnlocked(userHandle)) { 306 if (!user.isManagedProfile()) { 307 // When the user is locked, we communicate it loud-and-clear 308 // on the lockscreen; we only show a notification below for 309 // locked managed profiles. 310 } else { 311 UserInfo parent = mUserManager.getProfileParent(user.id); 312 if (parent != null && 313 mUserManager.isUserUnlockingOrUnlocked(parent.getUserHandle()) && 314 !mUserManager.isQuietModeEnabled(userHandle)) { 315 // Only show notifications for managed profiles once their parent 316 // user is unlocked. 317 showEncryptionNotificationForProfile(userHandle); 318 } 319 } 320 } 321 } 322 } 323 showEncryptionNotificationForProfile(UserHandle user)324 private void showEncryptionNotificationForProfile(UserHandle user) { 325 Resources r = mContext.getResources(); 326 CharSequence title = r.getText( 327 com.android.internal.R.string.user_encrypted_title); 328 CharSequence message = r.getText( 329 com.android.internal.R.string.profile_encrypted_message); 330 CharSequence detail = r.getText( 331 com.android.internal.R.string.profile_encrypted_detail); 332 333 final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE); 334 final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier()); 335 if (unlockIntent == null) { 336 return; 337 } 338 unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 339 PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent, 340 PendingIntent.FLAG_UPDATE_CURRENT); 341 342 showEncryptionNotification(user, title, message, detail, intent); 343 } 344 showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message, CharSequence detail, PendingIntent intent)345 private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message, 346 CharSequence detail, PendingIntent intent) { 347 if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier()); 348 349 // Suppress all notifications on non-FBE devices for now 350 if (!StorageManager.isFileEncryptedNativeOrEmulated()) return; 351 352 Notification notification = new Notification.Builder(mContext) 353 .setSmallIcon(com.android.internal.R.drawable.ic_user_secure) 354 .setWhen(0) 355 .setOngoing(true) 356 .setTicker(title) 357 .setDefaults(0) // please be quiet 358 .setPriority(Notification.PRIORITY_MAX) 359 .setColor(mContext.getColor( 360 com.android.internal.R.color.system_notification_accent_color)) 361 .setContentTitle(title) 362 .setContentText(message) 363 .setSubText(detail) 364 .setVisibility(Notification.VISIBILITY_PUBLIC) 365 .setContentIntent(intent) 366 .build(); 367 mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user); 368 } 369 hideEncryptionNotification(UserHandle userHandle)370 public void hideEncryptionNotification(UserHandle userHandle) { 371 if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier()); 372 mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle); 373 } 374 onCleanupUser(int userId)375 public void onCleanupUser(int userId) { 376 hideEncryptionNotification(new UserHandle(userId)); 377 } 378 onUnlockUser(final int userId)379 public void onUnlockUser(final int userId) { 380 // Hide notification first, as tie managed profile lock takes time 381 hideEncryptionNotification(new UserHandle(userId)); 382 383 if (mUserManager.getUserInfo(userId).isManagedProfile()) { 384 // As tieManagedProfileLockIfNecessary() may try to unlock user, we should not do it 385 // in onUnlockUser() synchronously, otherwise it may cause a deadlock 386 mHandler.post(new Runnable() { 387 @Override 388 public void run() { 389 tieManagedProfileLockIfNecessary(userId, null); 390 } 391 }); 392 } 393 394 // Now we have unlocked the parent user we should show notifications 395 // about any profiles that exist. 396 List<UserInfo> profiles = mUserManager.getProfiles(userId); 397 for (int i = 0; i < profiles.size(); i++) { 398 UserInfo profile = profiles.get(i); 399 final boolean isSecure = 400 mStorage.hasPassword(profile.id) || mStorage.hasPattern(profile.id); 401 if (isSecure && profile.isManagedProfile()) { 402 UserHandle userHandle = profile.getUserHandle(); 403 if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) && 404 !mUserManager.isQuietModeEnabled(userHandle)) { 405 showEncryptionNotificationForProfile(userHandle); 406 } 407 } 408 } 409 } 410 411 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 412 @Override 413 public void onReceive(Context context, Intent intent) { 414 if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) { 415 // Notify keystore that a new user was added. 416 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 417 if (userHandle > UserHandle.USER_SYSTEM) { 418 removeUser(userHandle, /* unknownUser= */ true); 419 } 420 final KeyStore ks = KeyStore.getInstance(); 421 final UserInfo parentInfo = mUserManager.getProfileParent(userHandle); 422 final int parentHandle = parentInfo != null ? parentInfo.id : -1; 423 ks.onUserAdded(userHandle, parentHandle); 424 } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) { 425 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 426 mStorage.prefetchUser(userHandle); 427 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { 428 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 429 if (userHandle > 0) { 430 removeUser(userHandle, /* unknownUser= */ false); 431 } 432 } 433 } 434 }; 435 436 @Override // binder interface systemReady()437 public void systemReady() { 438 migrateOldData(); 439 try { 440 getGateKeeperService(); 441 } catch (RemoteException e) { 442 Slog.e(TAG, "Failure retrieving IGateKeeperService", e); 443 } 444 // TODO: maybe skip this for split system user mode. 445 mStorage.prefetchUser(UserHandle.USER_SYSTEM); 446 } 447 migrateOldData()448 private void migrateOldData() { 449 try { 450 // These Settings moved before multi-user was enabled, so we only have to do it for the 451 // root user. 452 if (getString("migrated", null, 0) == null) { 453 final ContentResolver cr = mContext.getContentResolver(); 454 for (String validSetting : VALID_SETTINGS) { 455 String value = Settings.Secure.getString(cr, validSetting); 456 if (value != null) { 457 setString(validSetting, value, 0); 458 } 459 } 460 // No need to move the password / pattern files. They're already in the right place. 461 setString("migrated", "true", 0); 462 Slog.i(TAG, "Migrated lock settings to new location"); 463 } 464 465 // These Settings changed after multi-user was enabled, hence need to be moved per user. 466 if (getString("migrated_user_specific", null, 0) == null) { 467 final ContentResolver cr = mContext.getContentResolver(); 468 List<UserInfo> users = mUserManager.getUsers(); 469 for (int user = 0; user < users.size(); user++) { 470 // Migrate owner info 471 final int userId = users.get(user).id; 472 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO; 473 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId); 474 if (!TextUtils.isEmpty(ownerInfo)) { 475 setString(OWNER_INFO, ownerInfo, userId); 476 Settings.Secure.putStringForUser(cr, OWNER_INFO, "", userId); 477 } 478 479 // Migrate owner info enabled. Note there was a bug where older platforms only 480 // stored this value if the checkbox was toggled at least once. The code detects 481 // this case by handling the exception. 482 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; 483 boolean enabled; 484 try { 485 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId); 486 enabled = ivalue != 0; 487 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId); 488 } catch (SettingNotFoundException e) { 489 // Setting was never stored. Store it if the string is not empty. 490 if (!TextUtils.isEmpty(ownerInfo)) { 491 setLong(OWNER_INFO_ENABLED, 1, userId); 492 } 493 } 494 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId); 495 } 496 // No need to move the password / pattern files. They're already in the right place. 497 setString("migrated_user_specific", "true", 0); 498 Slog.i(TAG, "Migrated per-user lock settings to new location"); 499 } 500 501 // Migrates biometric weak such that the fallback mechanism becomes the primary. 502 if (getString("migrated_biometric_weak", null, 0) == null) { 503 List<UserInfo> users = mUserManager.getUsers(); 504 for (int i = 0; i < users.size(); i++) { 505 int userId = users.get(i).id; 506 long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY, 507 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 508 userId); 509 long alternateType = getLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 510 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 511 userId); 512 if (type == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { 513 setLong(LockPatternUtils.PASSWORD_TYPE_KEY, 514 alternateType, 515 userId); 516 } 517 setLong(LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 518 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 519 userId); 520 } 521 setString("migrated_biometric_weak", "true", 0); 522 Slog.i(TAG, "Migrated biometric weak to use the fallback instead"); 523 } 524 525 // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one 526 // user was present on the system, so if we're upgrading to M and there is more than one 527 // user we disable the flag to remain consistent. 528 if (getString("migrated_lockscreen_disabled", null, 0) == null) { 529 final List<UserInfo> users = mUserManager.getUsers(); 530 final int userCount = users.size(); 531 int switchableUsers = 0; 532 for (int i = 0; i < userCount; i++) { 533 if (users.get(i).supportsSwitchTo()) { 534 switchableUsers++; 535 } 536 } 537 538 if (switchableUsers > 1) { 539 for (int i = 0; i < userCount; i++) { 540 int id = users.get(i).id; 541 542 if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) { 543 setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id); 544 } 545 } 546 } 547 548 setString("migrated_lockscreen_disabled", "true", 0); 549 Slog.i(TAG, "Migrated lockscreen disabled flag"); 550 } 551 552 final List<UserInfo> users = mUserManager.getUsers(); 553 for (int i = 0; i < users.size(); i++) { 554 final UserInfo userInfo = users.get(i); 555 if (userInfo.isManagedProfile() && mStorage.hasChildProfileLock(userInfo.id)) { 556 // When managed profile has a unified lock, the password quality stored has 2 557 // possibilities only. 558 // 1). PASSWORD_QUALITY_UNSPECIFIED, which is upgraded from dp2, and we are 559 // going to set it back to PASSWORD_QUALITY_ALPHANUMERIC. 560 // 2). PASSWORD_QUALITY_ALPHANUMERIC, which is the actual password quality for 561 // unified lock. 562 final long quality = getLong(LockPatternUtils.PASSWORD_TYPE_KEY, 563 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userInfo.id); 564 if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { 565 // Only possible when it's upgraded from nyc dp3 566 Slog.i(TAG, "Migrated tied profile lock type"); 567 setLong(LockPatternUtils.PASSWORD_TYPE_KEY, 568 DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, userInfo.id); 569 } else if (quality != DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC) { 570 // It should not happen 571 Slog.e(TAG, "Invalid tied profile lock type: " + quality); 572 } 573 } 574 try { 575 final String alias = LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userInfo.id; 576 java.security.KeyStore keyStore = 577 java.security.KeyStore.getInstance("AndroidKeyStore"); 578 keyStore.load(null); 579 if (keyStore.containsAlias(alias)) { 580 keyStore.deleteEntry(alias); 581 } 582 } catch (KeyStoreException | NoSuchAlgorithmException | 583 CertificateException | IOException e) { 584 Slog.e(TAG, "Unable to remove tied profile key", e); 585 } 586 } 587 } catch (RemoteException re) { 588 Slog.e(TAG, "Unable to migrate old data", re); 589 } 590 } 591 checkWritePermission(int userId)592 private final void checkWritePermission(int userId) { 593 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite"); 594 } 595 checkPasswordReadPermission(int userId)596 private final void checkPasswordReadPermission(int userId) { 597 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead"); 598 } 599 checkReadPermission(String requestedKey, int userId)600 private final void checkReadPermission(String requestedKey, int userId) { 601 final int callingUid = Binder.getCallingUid(); 602 603 for (int i = 0; i < READ_CONTACTS_PROTECTED_SETTINGS.length; i++) { 604 String key = READ_CONTACTS_PROTECTED_SETTINGS[i]; 605 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_CONTACTS) 606 != PackageManager.PERMISSION_GRANTED) { 607 throw new SecurityException("uid=" + callingUid 608 + " needs permission " + READ_CONTACTS + " to read " 609 + requestedKey + " for user " + userId); 610 } 611 } 612 613 for (int i = 0; i < READ_PASSWORD_PROTECTED_SETTINGS.length; i++) { 614 String key = READ_PASSWORD_PROTECTED_SETTINGS[i]; 615 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(PERMISSION) 616 != PackageManager.PERMISSION_GRANTED) { 617 throw new SecurityException("uid=" + callingUid 618 + " needs permission " + PERMISSION + " to read " 619 + requestedKey + " for user " + userId); 620 } 621 } 622 } 623 624 @Override getSeparateProfileChallengeEnabled(int userId)625 public boolean getSeparateProfileChallengeEnabled(int userId) throws RemoteException { 626 checkReadPermission(SEPARATE_PROFILE_CHALLENGE_KEY, userId); 627 synchronized (mSeparateChallengeLock) { 628 return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId); 629 } 630 } 631 632 @Override setSeparateProfileChallengeEnabled(int userId, boolean enabled, String managedUserPassword)633 public void setSeparateProfileChallengeEnabled(int userId, boolean enabled, 634 String managedUserPassword) throws RemoteException { 635 checkWritePermission(userId); 636 synchronized (mSeparateChallengeLock) { 637 setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId); 638 if (enabled) { 639 mStorage.removeChildProfileLock(userId); 640 removeKeystoreProfileKey(userId); 641 } else { 642 tieManagedProfileLockIfNecessary(userId, managedUserPassword); 643 } 644 } 645 } 646 647 @Override setBoolean(String key, boolean value, int userId)648 public void setBoolean(String key, boolean value, int userId) throws RemoteException { 649 checkWritePermission(userId); 650 setStringUnchecked(key, userId, value ? "1" : "0"); 651 } 652 653 @Override setLong(String key, long value, int userId)654 public void setLong(String key, long value, int userId) throws RemoteException { 655 checkWritePermission(userId); 656 setStringUnchecked(key, userId, Long.toString(value)); 657 } 658 659 @Override setString(String key, String value, int userId)660 public void setString(String key, String value, int userId) throws RemoteException { 661 checkWritePermission(userId); 662 setStringUnchecked(key, userId, value); 663 } 664 setStringUnchecked(String key, int userId, String value)665 private void setStringUnchecked(String key, int userId, String value) { 666 mStorage.writeKeyValue(key, value, userId); 667 if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) { 668 BackupManager.dataChanged("com.android.providers.settings"); 669 } 670 } 671 672 @Override getBoolean(String key, boolean defaultValue, int userId)673 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { 674 checkReadPermission(key, userId); 675 String value = getStringUnchecked(key, null, userId); 676 return TextUtils.isEmpty(value) ? 677 defaultValue : (value.equals("1") || value.equals("true")); 678 } 679 680 @Override getLong(String key, long defaultValue, int userId)681 public long getLong(String key, long defaultValue, int userId) throws RemoteException { 682 checkReadPermission(key, userId); 683 String value = getStringUnchecked(key, null, userId); 684 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); 685 } 686 687 @Override getString(String key, String defaultValue, int userId)688 public String getString(String key, String defaultValue, int userId) throws RemoteException { 689 checkReadPermission(key, userId); 690 return getStringUnchecked(key, defaultValue, userId); 691 } 692 getStringUnchecked(String key, String defaultValue, int userId)693 public String getStringUnchecked(String key, String defaultValue, int userId) { 694 if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) { 695 long ident = Binder.clearCallingIdentity(); 696 try { 697 return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0"; 698 } finally { 699 Binder.restoreCallingIdentity(ident); 700 } 701 } 702 703 if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) { 704 key = Settings.Secure.LOCK_PATTERN_ENABLED; 705 } 706 707 return mStorage.readKeyValue(key, defaultValue, userId); 708 } 709 710 @Override havePassword(int userId)711 public boolean havePassword(int userId) throws RemoteException { 712 // Do we need a permissions check here? 713 return mStorage.hasPassword(userId); 714 } 715 716 @Override havePattern(int userId)717 public boolean havePattern(int userId) throws RemoteException { 718 // Do we need a permissions check here? 719 return mStorage.hasPattern(userId); 720 } 721 setKeystorePassword(String password, int userHandle)722 private void setKeystorePassword(String password, int userHandle) { 723 final KeyStore ks = KeyStore.getInstance(); 724 ks.onUserPasswordChanged(userHandle, password); 725 } 726 unlockKeystore(String password, int userHandle)727 private void unlockKeystore(String password, int userHandle) { 728 if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle); 729 final KeyStore ks = KeyStore.getInstance(); 730 ks.unlock(userHandle, password); 731 } 732 getDecryptedPasswordForTiedProfile(int userId)733 private String getDecryptedPasswordForTiedProfile(int userId) 734 throws KeyStoreException, UnrecoverableKeyException, 735 NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, 736 InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, 737 CertificateException, IOException { 738 if (DEBUG) Slog.v(TAG, "Get child profile decrytped key"); 739 byte[] storedData = mStorage.readChildProfileLock(userId); 740 if (storedData == null) { 741 throw new FileNotFoundException("Child profile lock file not found"); 742 } 743 byte[] iv = Arrays.copyOfRange(storedData, 0, PROFILE_KEY_IV_SIZE); 744 byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE, 745 storedData.length); 746 byte[] decryptionResult; 747 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore"); 748 keyStore.load(null); 749 SecretKey decryptionKey = (SecretKey) keyStore.getKey( 750 LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, null); 751 752 Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" 753 + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); 754 755 cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv)); 756 decryptionResult = cipher.doFinal(encryptedPassword); 757 return new String(decryptionResult, StandardCharsets.UTF_8); 758 } 759 unlockChildProfile(int profileHandle)760 private void unlockChildProfile(int profileHandle) throws RemoteException { 761 try { 762 doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false, 763 0 /* no challenge */, profileHandle, null /* progressCallback */); 764 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException 765 | NoSuchAlgorithmException | NoSuchPaddingException 766 | InvalidAlgorithmParameterException | IllegalBlockSizeException 767 | BadPaddingException | CertificateException | IOException e) { 768 if (e instanceof FileNotFoundException) { 769 Slog.i(TAG, "Child profile key not found"); 770 } else { 771 Slog.e(TAG, "Failed to decrypt child profile key", e); 772 } 773 } 774 } 775 unlockUser(int userId, byte[] token, byte[] secret)776 private void unlockUser(int userId, byte[] token, byte[] secret) { 777 // TODO: make this method fully async so we can update UI with progress strings 778 final CountDownLatch latch = new CountDownLatch(1); 779 final IProgressListener listener = new IProgressListener.Stub() { 780 @Override 781 public void onStarted(int id, Bundle extras) throws RemoteException { 782 Log.d(TAG, "unlockUser started"); 783 } 784 785 @Override 786 public void onProgress(int id, int progress, Bundle extras) throws RemoteException { 787 Log.d(TAG, "unlockUser progress " + progress); 788 } 789 790 @Override 791 public void onFinished(int id, Bundle extras) throws RemoteException { 792 Log.d(TAG, "unlockUser finished"); 793 latch.countDown(); 794 } 795 }; 796 797 try { 798 ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener); 799 } catch (RemoteException e) { 800 throw e.rethrowAsRuntimeException(); 801 } 802 803 try { 804 latch.await(15, TimeUnit.SECONDS); 805 } catch (InterruptedException e) { 806 Thread.currentThread().interrupt(); 807 } 808 try { 809 if (!mUserManager.getUserInfo(userId).isManagedProfile()) { 810 final List<UserInfo> profiles = mUserManager.getProfiles(userId); 811 for (UserInfo pi : profiles) { 812 // Unlock managed profile with unified lock 813 if (pi.isManagedProfile() 814 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id) 815 && mStorage.hasChildProfileLock(pi.id)) { 816 unlockChildProfile(pi.id); 817 } 818 } 819 } 820 } catch (RemoteException e) { 821 Log.d(TAG, "Failed to unlock child profile", e); 822 } 823 } 824 getCurrentHandle(int userId)825 private byte[] getCurrentHandle(int userId) { 826 CredentialHash credential; 827 byte[] currentHandle; 828 829 int currentHandleType = mStorage.getStoredCredentialType(userId); 830 switch (currentHandleType) { 831 case CredentialHash.TYPE_PATTERN: 832 credential = mStorage.readPatternHash(userId); 833 currentHandle = credential != null 834 ? credential.hash 835 : null; 836 break; 837 case CredentialHash.TYPE_PASSWORD: 838 credential = mStorage.readPasswordHash(userId); 839 currentHandle = credential != null 840 ? credential.hash 841 : null; 842 break; 843 case CredentialHash.TYPE_NONE: 844 default: 845 currentHandle = null; 846 break; 847 } 848 849 // sanity check 850 if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) { 851 Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available"); 852 } 853 854 return currentHandle; 855 } 856 onUserLockChanged(int userId)857 private void onUserLockChanged(int userId) throws RemoteException { 858 if (mUserManager.getUserInfo(userId).isManagedProfile()) { 859 return; 860 } 861 final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId); 862 final List<UserInfo> profiles = mUserManager.getProfiles(userId); 863 final int size = profiles.size(); 864 for (int i = 0; i < size; i++) { 865 final UserInfo profile = profiles.get(i); 866 if (profile.isManagedProfile()) { 867 final int managedUserId = profile.id; 868 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) { 869 continue; 870 } 871 if (isSecure) { 872 tieManagedProfileLockIfNecessary(managedUserId, null); 873 } else { 874 clearUserKeyProtection(managedUserId); 875 getGateKeeperService().clearSecureUserId(managedUserId); 876 mStorage.writePatternHash(null, managedUserId); 877 setKeystorePassword(null, managedUserId); 878 fixateNewestUserKeyAuth(managedUserId); 879 mStorage.removeChildProfileLock(managedUserId); 880 removeKeystoreProfileKey(managedUserId); 881 } 882 } 883 } 884 } 885 isManagedProfileWithUnifiedLock(int userId)886 private boolean isManagedProfileWithUnifiedLock(int userId) { 887 return mUserManager.getUserInfo(userId).isManagedProfile() 888 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId); 889 } 890 isManagedProfileWithSeparatedLock(int userId)891 private boolean isManagedProfileWithSeparatedLock(int userId) { 892 return mUserManager.getUserInfo(userId).isManagedProfile() 893 && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId); 894 } 895 896 // This method should be called by LockPatternUtil only, all internal methods in this class 897 // should call setLockPatternInternal. 898 @Override setLockPattern(String pattern, String savedCredential, int userId)899 public void setLockPattern(String pattern, String savedCredential, int userId) 900 throws RemoteException { 901 checkWritePermission(userId); 902 synchronized (mSeparateChallengeLock) { 903 setLockPatternInternal(pattern, savedCredential, userId); 904 setSeparateProfileChallengeEnabled(userId, true, null); 905 } 906 } 907 setLockPatternInternal(String pattern, String savedCredential, int userId)908 private void setLockPatternInternal(String pattern, String savedCredential, int userId) 909 throws RemoteException { 910 byte[] currentHandle = getCurrentHandle(userId); 911 912 if (pattern == null) { 913 clearUserKeyProtection(userId); 914 getGateKeeperService().clearSecureUserId(userId); 915 mStorage.writePatternHash(null, userId); 916 setKeystorePassword(null, userId); 917 fixateNewestUserKeyAuth(userId); 918 onUserLockChanged(userId); 919 return; 920 } 921 922 if (isManagedProfileWithUnifiedLock(userId)) { 923 // get credential from keystore when managed profile has unified lock 924 try { 925 savedCredential = getDecryptedPasswordForTiedProfile(userId); 926 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException 927 | NoSuchAlgorithmException | NoSuchPaddingException 928 | InvalidAlgorithmParameterException | IllegalBlockSizeException 929 | BadPaddingException | CertificateException | IOException e) { 930 if (e instanceof FileNotFoundException) { 931 Slog.i(TAG, "Child profile key not found"); 932 } else { 933 Slog.e(TAG, "Failed to decrypt child profile key", e); 934 } 935 } 936 } else { 937 if (currentHandle == null) { 938 if (savedCredential != null) { 939 Slog.w(TAG, "Saved credential provided, but none stored"); 940 } 941 savedCredential = null; 942 } 943 } 944 945 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId); 946 if (enrolledHandle != null) { 947 CredentialHash willStore 948 = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER); 949 setUserKeyProtection(userId, pattern, 950 doVerifyPattern(pattern, willStore, true, 0, userId, null /* progressCallback */)); 951 mStorage.writePatternHash(enrolledHandle, userId); 952 fixateNewestUserKeyAuth(userId); 953 onUserLockChanged(userId); 954 } else { 955 throw new RemoteException("Failed to enroll pattern"); 956 } 957 } 958 959 // This method should be called by LockPatternUtil only, all internal methods in this class 960 // should call setLockPasswordInternal. 961 @Override setLockPassword(String password, String savedCredential, int userId)962 public void setLockPassword(String password, String savedCredential, int userId) 963 throws RemoteException { 964 checkWritePermission(userId); 965 synchronized (mSeparateChallengeLock) { 966 setLockPasswordInternal(password, savedCredential, userId); 967 setSeparateProfileChallengeEnabled(userId, true, null); 968 } 969 } 970 setLockPasswordInternal(String password, String savedCredential, int userId)971 private void setLockPasswordInternal(String password, String savedCredential, int userId) 972 throws RemoteException { 973 byte[] currentHandle = getCurrentHandle(userId); 974 if (password == null) { 975 clearUserKeyProtection(userId); 976 getGateKeeperService().clearSecureUserId(userId); 977 mStorage.writePasswordHash(null, userId); 978 setKeystorePassword(null, userId); 979 fixateNewestUserKeyAuth(userId); 980 onUserLockChanged(userId); 981 return; 982 } 983 984 if (isManagedProfileWithUnifiedLock(userId)) { 985 // get credential from keystore when managed profile has unified lock 986 try { 987 savedCredential = getDecryptedPasswordForTiedProfile(userId); 988 } catch (FileNotFoundException e) { 989 Slog.i(TAG, "Child profile key not found"); 990 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException 991 | NoSuchAlgorithmException | NoSuchPaddingException 992 | InvalidAlgorithmParameterException | IllegalBlockSizeException 993 | BadPaddingException | CertificateException | IOException e) { 994 Slog.e(TAG, "Failed to decrypt child profile key", e); 995 } 996 } else { 997 if (currentHandle == null) { 998 if (savedCredential != null) { 999 Slog.w(TAG, "Saved credential provided, but none stored"); 1000 } 1001 savedCredential = null; 1002 } 1003 } 1004 1005 byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId); 1006 if (enrolledHandle != null) { 1007 CredentialHash willStore 1008 = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER); 1009 setUserKeyProtection(userId, password, 1010 doVerifyPassword(password, willStore, true, 0, userId, 1011 null /* progressCallback */)); 1012 mStorage.writePasswordHash(enrolledHandle, userId); 1013 fixateNewestUserKeyAuth(userId); 1014 onUserLockChanged(userId); 1015 } else { 1016 throw new RemoteException("Failed to enroll password"); 1017 } 1018 } 1019 tieProfileLockToParent(int userId, String password)1020 private void tieProfileLockToParent(int userId, String password) { 1021 if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId); 1022 byte[] randomLockSeed = password.getBytes(StandardCharsets.UTF_8); 1023 byte[] encryptionResult; 1024 byte[] iv; 1025 try { 1026 KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); 1027 keyGenerator.init(new SecureRandom()); 1028 SecretKey secretKey = keyGenerator.generateKey(); 1029 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore"); 1030 keyStore.load(null); 1031 try { 1032 keyStore.setEntry( 1033 LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, 1034 new java.security.KeyStore.SecretKeyEntry(secretKey), 1035 new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT) 1036 .setBlockModes(KeyProperties.BLOCK_MODE_GCM) 1037 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 1038 .build()); 1039 keyStore.setEntry( 1040 LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, 1041 new java.security.KeyStore.SecretKeyEntry(secretKey), 1042 new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) 1043 .setBlockModes(KeyProperties.BLOCK_MODE_GCM) 1044 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 1045 .setUserAuthenticationRequired(true) 1046 .setUserAuthenticationValidityDurationSeconds(30) 1047 .build()); 1048 // Key imported, obtain a reference to it. 1049 SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey( 1050 LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, null); 1051 Cipher cipher = Cipher.getInstance( 1052 KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" 1053 + KeyProperties.ENCRYPTION_PADDING_NONE); 1054 cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey); 1055 encryptionResult = cipher.doFinal(randomLockSeed); 1056 iv = cipher.getIV(); 1057 } finally { 1058 // The original key can now be discarded. 1059 keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId); 1060 } 1061 } catch (CertificateException | UnrecoverableKeyException 1062 | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException 1063 | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { 1064 throw new RuntimeException("Failed to encrypt key", e); 1065 } 1066 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 1067 try { 1068 if (iv.length != PROFILE_KEY_IV_SIZE) { 1069 throw new RuntimeException("Invalid iv length: " + iv.length); 1070 } 1071 outputStream.write(iv); 1072 outputStream.write(encryptionResult); 1073 } catch (IOException e) { 1074 throw new RuntimeException("Failed to concatenate byte arrays", e); 1075 } 1076 mStorage.writeChildProfileLock(userId, outputStream.toByteArray()); 1077 } 1078 enrollCredential(byte[] enrolledHandle, String enrolledCredential, String toEnroll, int userId)1079 private byte[] enrollCredential(byte[] enrolledHandle, 1080 String enrolledCredential, String toEnroll, int userId) 1081 throws RemoteException { 1082 checkWritePermission(userId); 1083 byte[] enrolledCredentialBytes = enrolledCredential == null 1084 ? null 1085 : enrolledCredential.getBytes(); 1086 byte[] toEnrollBytes = toEnroll == null 1087 ? null 1088 : toEnroll.getBytes(); 1089 GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle, 1090 enrolledCredentialBytes, toEnrollBytes); 1091 1092 if (response == null) { 1093 return null; 1094 } 1095 1096 byte[] hash = response.getPayload(); 1097 if (hash != null) { 1098 setKeystorePassword(toEnroll, userId); 1099 } else { 1100 // Should not happen 1101 Slog.e(TAG, "Throttled while enrolling a password"); 1102 } 1103 return hash; 1104 } 1105 setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)1106 private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr) 1107 throws RemoteException { 1108 if (vcr == null) { 1109 throw new RemoteException("Null response verifying a credential we just set"); 1110 } 1111 if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { 1112 throw new RemoteException("Non-OK response verifying a credential we just set: " 1113 + vcr.getResponseCode()); 1114 } 1115 byte[] token = vcr.getPayload(); 1116 if (token == null) { 1117 throw new RemoteException("Empty payload verifying a credential we just set"); 1118 } 1119 addUserKeyAuth(userId, token, secretFromCredential(credential)); 1120 } 1121 clearUserKeyProtection(int userId)1122 private void clearUserKeyProtection(int userId) throws RemoteException { 1123 addUserKeyAuth(userId, null, null); 1124 } 1125 secretFromCredential(String credential)1126 private static byte[] secretFromCredential(String credential) throws RemoteException { 1127 try { 1128 MessageDigest digest = MessageDigest.getInstance("SHA-512"); 1129 // Personalize the hash 1130 byte[] personalization = "Android FBE credential hash" 1131 .getBytes(StandardCharsets.UTF_8); 1132 // Pad it to the block size of the hash function 1133 personalization = Arrays.copyOf(personalization, 128); 1134 digest.update(personalization); 1135 digest.update(credential.getBytes(StandardCharsets.UTF_8)); 1136 return digest.digest(); 1137 } catch (NoSuchAlgorithmException e) { 1138 throw new RuntimeException("NoSuchAlgorithmException for SHA-512"); 1139 } 1140 } 1141 addUserKeyAuth(int userId, byte[] token, byte[] secret)1142 private void addUserKeyAuth(int userId, byte[] token, byte[] secret) 1143 throws RemoteException { 1144 final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId); 1145 final IMountService mountService = getMountService(); 1146 final long callingId = Binder.clearCallingIdentity(); 1147 try { 1148 mountService.addUserKeyAuth(userId, userInfo.serialNumber, token, secret); 1149 } finally { 1150 Binder.restoreCallingIdentity(callingId); 1151 } 1152 } 1153 fixateNewestUserKeyAuth(int userId)1154 private void fixateNewestUserKeyAuth(int userId) 1155 throws RemoteException { 1156 final IMountService mountService = getMountService(); 1157 final long callingId = Binder.clearCallingIdentity(); 1158 try { 1159 mountService.fixateNewestUserKeyAuth(userId); 1160 } finally { 1161 Binder.restoreCallingIdentity(callingId); 1162 } 1163 } 1164 1165 @Override resetKeyStore(int userId)1166 public void resetKeyStore(int userId) throws RemoteException { 1167 checkWritePermission(userId); 1168 if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId); 1169 int managedUserId = -1; 1170 String managedUserDecryptedPassword = null; 1171 final List<UserInfo> profiles = mUserManager.getProfiles(userId); 1172 for (UserInfo pi : profiles) { 1173 // Unlock managed profile with unified lock 1174 if (pi.isManagedProfile() 1175 && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id) 1176 && mStorage.hasChildProfileLock(pi.id)) { 1177 try { 1178 if (managedUserId == -1) { 1179 managedUserDecryptedPassword = getDecryptedPasswordForTiedProfile(pi.id); 1180 managedUserId = pi.id; 1181 } else { 1182 // Should not happen 1183 Slog.e(TAG, "More than one managed profile, uid1:" + managedUserId 1184 + ", uid2:" + pi.id); 1185 } 1186 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException 1187 | NoSuchAlgorithmException | NoSuchPaddingException 1188 | InvalidAlgorithmParameterException | IllegalBlockSizeException 1189 | BadPaddingException | CertificateException | IOException e) { 1190 Slog.e(TAG, "Failed to decrypt child profile key", e); 1191 } 1192 } 1193 } 1194 try { 1195 // Clear all the users credentials could have been installed in for this user. 1196 for (int profileId : mUserManager.getProfileIdsWithDisabled(userId)) { 1197 for (int uid : SYSTEM_CREDENTIAL_UIDS) { 1198 mKeyStore.clearUid(UserHandle.getUid(profileId, uid)); 1199 } 1200 } 1201 } finally { 1202 if (managedUserId != -1 && managedUserDecryptedPassword != null) { 1203 if (DEBUG) Slog.v(TAG, "Restore tied profile lock"); 1204 tieProfileLockToParent(managedUserId, managedUserDecryptedPassword); 1205 } 1206 } 1207 } 1208 1209 @Override checkPattern(String pattern, int userId, ICheckCredentialProgressCallback progressCallback)1210 public VerifyCredentialResponse checkPattern(String pattern, int userId, 1211 ICheckCredentialProgressCallback progressCallback) throws RemoteException { 1212 return doVerifyPattern(pattern, false, 0, userId, progressCallback); 1213 } 1214 1215 @Override verifyPattern(String pattern, long challenge, int userId)1216 public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId) 1217 throws RemoteException { 1218 return doVerifyPattern(pattern, true, challenge, userId, null /* progressCallback */); 1219 } 1220 doVerifyPattern(String pattern, boolean hasChallenge, long challenge, int userId, ICheckCredentialProgressCallback progressCallback)1221 private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge, 1222 long challenge, int userId, ICheckCredentialProgressCallback progressCallback) 1223 throws RemoteException { 1224 checkPasswordReadPermission(userId); 1225 if (TextUtils.isEmpty(pattern)) { 1226 throw new IllegalArgumentException("Pattern can't be null or empty"); 1227 } 1228 CredentialHash storedHash = mStorage.readPatternHash(userId); 1229 return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId, 1230 progressCallback); 1231 } 1232 doVerifyPattern(String pattern, CredentialHash storedHash, boolean hasChallenge, long challenge, int userId, ICheckCredentialProgressCallback progressCallback)1233 private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash, 1234 boolean hasChallenge, long challenge, int userId, 1235 ICheckCredentialProgressCallback progressCallback) throws RemoteException { 1236 1237 if (TextUtils.isEmpty(pattern)) { 1238 throw new IllegalArgumentException("Pattern can't be null or empty"); 1239 } 1240 boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern; 1241 1242 String patternToVerify; 1243 if (shouldReEnrollBaseZero) { 1244 patternToVerify = LockPatternUtils.patternStringToBaseZero(pattern); 1245 } else { 1246 patternToVerify = pattern; 1247 } 1248 1249 VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify, 1250 hasChallenge, challenge, 1251 new CredentialUtil() { 1252 @Override 1253 public void setCredential(String pattern, String oldPattern, int userId) 1254 throws RemoteException { 1255 setLockPatternInternal(pattern, oldPattern, userId); 1256 } 1257 1258 @Override 1259 public byte[] toHash(String pattern, int userId) { 1260 return LockPatternUtils.patternToHash( 1261 LockPatternUtils.stringToPattern(pattern)); 1262 } 1263 1264 @Override 1265 public String adjustForKeystore(String pattern) { 1266 return LockPatternUtils.patternStringToBaseZero(pattern); 1267 } 1268 }, 1269 progressCallback 1270 ); 1271 1272 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK 1273 && shouldReEnrollBaseZero) { 1274 setLockPatternInternal(pattern, patternToVerify, userId); 1275 } 1276 1277 return response; 1278 } 1279 1280 @Override checkPassword(String password, int userId, ICheckCredentialProgressCallback progressCallback)1281 public VerifyCredentialResponse checkPassword(String password, int userId, 1282 ICheckCredentialProgressCallback progressCallback) throws RemoteException { 1283 return doVerifyPassword(password, false, 0, userId, progressCallback); 1284 } 1285 1286 @Override verifyPassword(String password, long challenge, int userId)1287 public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId) 1288 throws RemoteException { 1289 return doVerifyPassword(password, true, challenge, userId, null /* progressCallback */); 1290 } 1291 1292 @Override verifyTiedProfileChallenge(String password, boolean isPattern, long challenge, int userId)1293 public VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern, 1294 long challenge, int userId) throws RemoteException { 1295 checkPasswordReadPermission(userId); 1296 if (!isManagedProfileWithUnifiedLock(userId)) { 1297 throw new RemoteException("User id must be managed profile with unified lock"); 1298 } 1299 final int parentProfileId = mUserManager.getProfileParent(userId).id; 1300 // Unlock parent by using parent's challenge 1301 final VerifyCredentialResponse parentResponse = isPattern 1302 ? doVerifyPattern(password, true, challenge, parentProfileId, 1303 null /* progressCallback */) 1304 : doVerifyPassword(password, true, challenge, parentProfileId, 1305 null /* progressCallback */); 1306 if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { 1307 // Failed, just return parent's response 1308 return parentResponse; 1309 } 1310 1311 try { 1312 // Unlock work profile, and work profile with unified lock must use password only 1313 return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true, 1314 challenge, 1315 userId, null /* progressCallback */); 1316 } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException 1317 | NoSuchAlgorithmException | NoSuchPaddingException 1318 | InvalidAlgorithmParameterException | IllegalBlockSizeException 1319 | BadPaddingException | CertificateException | IOException e) { 1320 Slog.e(TAG, "Failed to decrypt child profile key", e); 1321 throw new RemoteException("Unable to get tied profile token"); 1322 } 1323 } 1324 doVerifyPassword(String password, boolean hasChallenge, long challenge, int userId, ICheckCredentialProgressCallback progressCallback)1325 private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge, 1326 long challenge, int userId, ICheckCredentialProgressCallback progressCallback) 1327 throws RemoteException { 1328 checkPasswordReadPermission(userId); 1329 if (TextUtils.isEmpty(password)) { 1330 throw new IllegalArgumentException("Password can't be null or empty"); 1331 } 1332 CredentialHash storedHash = mStorage.readPasswordHash(userId); 1333 return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId, 1334 progressCallback); 1335 } 1336 doVerifyPassword(String password, CredentialHash storedHash, boolean hasChallenge, long challenge, int userId, ICheckCredentialProgressCallback progressCallback)1337 private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash, 1338 boolean hasChallenge, long challenge, int userId, 1339 ICheckCredentialProgressCallback progressCallback) throws RemoteException { 1340 if (TextUtils.isEmpty(password)) { 1341 throw new IllegalArgumentException("Password can't be null or empty"); 1342 } 1343 return verifyCredential(userId, storedHash, password, hasChallenge, challenge, 1344 new CredentialUtil() { 1345 @Override 1346 public void setCredential(String password, String oldPassword, int userId) 1347 throws RemoteException { 1348 setLockPasswordInternal(password, oldPassword, userId); 1349 } 1350 1351 @Override 1352 public byte[] toHash(String password, int userId) { 1353 return mLockPatternUtils.passwordToHash(password, userId); 1354 } 1355 1356 @Override 1357 public String adjustForKeystore(String password) { 1358 return password; 1359 } 1360 }, progressCallback); 1361 } 1362 1363 private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash, 1364 String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil, 1365 ICheckCredentialProgressCallback progressCallback) 1366 throws RemoteException { 1367 if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) { 1368 // don't need to pass empty credentials to GateKeeper 1369 return VerifyCredentialResponse.OK; 1370 } 1371 1372 if (TextUtils.isEmpty(credential)) { 1373 return VerifyCredentialResponse.ERROR; 1374 } 1375 1376 if (storedHash.version == CredentialHash.VERSION_LEGACY) { 1377 byte[] hash = credentialUtil.toHash(credential, userId); 1378 if (Arrays.equals(hash, storedHash.hash)) { 1379 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId); 1380 1381 // Users with legacy credentials don't have credential-backed 1382 // FBE keys, so just pass through a fake token/secret 1383 Slog.i(TAG, "Unlocking user with fake token: " + userId); 1384 final byte[] fakeToken = String.valueOf(userId).getBytes(); 1385 unlockUser(userId, fakeToken, fakeToken); 1386 1387 // migrate credential to GateKeeper 1388 credentialUtil.setCredential(credential, null, userId); 1389 if (!hasChallenge) { 1390 return VerifyCredentialResponse.OK; 1391 } 1392 // Fall through to get the auth token. Technically this should never happen, 1393 // as a user that had a legacy credential would have to unlock their device 1394 // before getting to a flow with a challenge, but supporting for consistency. 1395 } else { 1396 return VerifyCredentialResponse.ERROR; 1397 } 1398 } 1399 1400 VerifyCredentialResponse response; 1401 boolean shouldReEnroll = false; 1402 GateKeeperResponse gateKeeperResponse = getGateKeeperService() 1403 .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes()); 1404 int responseCode = gateKeeperResponse.getResponseCode(); 1405 if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { 1406 response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout()); 1407 } else if (responseCode == GateKeeperResponse.RESPONSE_OK) { 1408 byte[] token = gateKeeperResponse.getPayload(); 1409 if (token == null) { 1410 // something's wrong if there's no payload with a challenge 1411 Slog.e(TAG, "verifyChallenge response had no associated payload"); 1412 response = VerifyCredentialResponse.ERROR; 1413 } else { 1414 shouldReEnroll = gateKeeperResponse.getShouldReEnroll(); 1415 response = new VerifyCredentialResponse(token); 1416 } 1417 } else { 1418 response = VerifyCredentialResponse.ERROR; 1419 } 1420 1421 if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { 1422 1423 1424 // credential has matched 1425 1426 if (progressCallback != null) { 1427 progressCallback.onCredentialVerified(); 1428 } 1429 unlockKeystore(credential, userId); 1430 1431 Slog.i(TAG, "Unlocking user " + userId + 1432 " with token length " + response.getPayload().length); 1433 unlockUser(userId, response.getPayload(), secretFromCredential(credential)); 1434 1435 if (isManagedProfileWithSeparatedLock(userId)) { 1436 TrustManager trustManager = 1437 (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); 1438 trustManager.setDeviceLockedForUser(userId, false); 1439 } 1440 if (shouldReEnroll) { 1441 credentialUtil.setCredential(credential, credential, userId); 1442 } 1443 } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { 1444 if (response.getTimeout() > 0) { 1445 requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId); 1446 } 1447 } 1448 1449 return response; 1450 } 1451 1452 @Override 1453 public boolean checkVoldPassword(int userId) throws RemoteException { 1454 if (!mFirstCallToVold) { 1455 return false; 1456 } 1457 mFirstCallToVold = false; 1458 1459 checkPasswordReadPermission(userId); 1460 1461 // There's no guarantee that this will safely connect, but if it fails 1462 // we will simply show the lock screen when we shouldn't, so relatively 1463 // benign. There is an outside chance something nasty would happen if 1464 // this service restarted before vold stales out the password in this 1465 // case. The nastiness is limited to not showing the lock screen when 1466 // we should, within the first minute of decrypting the phone if this 1467 // service can't connect to vold, it restarts, and then the new instance 1468 // does successfully connect. 1469 final IMountService service = getMountService(); 1470 String password; 1471 long identity = Binder.clearCallingIdentity(); 1472 try { 1473 password = service.getPassword(); 1474 service.clearPassword(); 1475 } finally { 1476 Binder.restoreCallingIdentity(identity); 1477 } 1478 if (password == null) { 1479 return false; 1480 } 1481 1482 try { 1483 if (mLockPatternUtils.isLockPatternEnabled(userId)) { 1484 if (checkPattern(password, userId, null /* progressCallback */).getResponseCode() 1485 == GateKeeperResponse.RESPONSE_OK) { 1486 return true; 1487 } 1488 } 1489 } catch (Exception e) { 1490 } 1491 1492 try { 1493 if (mLockPatternUtils.isLockPasswordEnabled(userId)) { 1494 if (checkPassword(password, userId, null /* progressCallback */).getResponseCode() 1495 == GateKeeperResponse.RESPONSE_OK) { 1496 return true; 1497 } 1498 } 1499 } catch (Exception e) { 1500 } 1501 1502 return false; 1503 } 1504 1505 private void removeUser(int userId, boolean unknownUser) { 1506 mStorage.removeUser(userId); 1507 mStrongAuth.removeUser(userId); 1508 1509 final KeyStore ks = KeyStore.getInstance(); 1510 ks.onUserRemoved(userId); 1511 1512 try { 1513 final IGateKeeperService gk = getGateKeeperService(); 1514 if (gk != null) { 1515 gk.clearSecureUserId(userId); 1516 } 1517 } catch (RemoteException ex) { 1518 Slog.w(TAG, "unable to clear GK secure user id"); 1519 } 1520 if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) { 1521 removeKeystoreProfileKey(userId); 1522 } 1523 } 1524 1525 private void removeKeystoreProfileKey(int targetUserId) { 1526 if (DEBUG) Slog.v(TAG, "Remove keystore profile key for user: " + targetUserId); 1527 try { 1528 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore"); 1529 keyStore.load(null); 1530 keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + targetUserId); 1531 keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + targetUserId); 1532 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException 1533 | IOException e) { 1534 // We have tried our best to remove all keys 1535 Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e); 1536 } 1537 } 1538 1539 @Override 1540 public void registerStrongAuthTracker(IStrongAuthTracker tracker) { 1541 checkPasswordReadPermission(UserHandle.USER_ALL); 1542 mStrongAuth.registerStrongAuthTracker(tracker); 1543 } 1544 1545 @Override 1546 public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) { 1547 checkPasswordReadPermission(UserHandle.USER_ALL); 1548 mStrongAuth.unregisterStrongAuthTracker(tracker); 1549 } 1550 1551 @Override 1552 public void requireStrongAuth(int strongAuthReason, int userId) { 1553 checkWritePermission(userId); 1554 mStrongAuth.requireStrongAuth(strongAuthReason, userId); 1555 } 1556 1557 @Override 1558 public void userPresent(int userId) { 1559 checkWritePermission(userId); 1560 mStrongAuth.reportUnlock(userId); 1561 } 1562 1563 @Override 1564 public int getStrongAuthForUser(int userId) { 1565 checkPasswordReadPermission(userId); 1566 return mStrongAuthTracker.getStrongAuthForUser(userId); 1567 } 1568 1569 private static final String[] VALID_SETTINGS = new String[] { 1570 LockPatternUtils.LOCKOUT_PERMANENT_KEY, 1571 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, 1572 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, 1573 LockPatternUtils.PASSWORD_TYPE_KEY, 1574 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, 1575 LockPatternUtils.LOCK_PASSWORD_SALT_KEY, 1576 LockPatternUtils.DISABLE_LOCKSCREEN_KEY, 1577 LockPatternUtils.LOCKSCREEN_OPTIONS, 1578 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, 1579 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY, 1580 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, 1581 LockPatternUtils.PASSWORD_HISTORY_KEY, 1582 Secure.LOCK_PATTERN_ENABLED, 1583 Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 1584 Secure.LOCK_PATTERN_VISIBLE, 1585 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED 1586 }; 1587 1588 // Reading these settings needs the contacts permission 1589 private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] { 1590 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1591 Secure.LOCK_SCREEN_OWNER_INFO 1592 }; 1593 1594 // Reading these settings needs the same permission as checking the password 1595 private static final String[] READ_PASSWORD_PROTECTED_SETTINGS = new String[] { 1596 LockPatternUtils.LOCK_PASSWORD_SALT_KEY, 1597 LockPatternUtils.PASSWORD_HISTORY_KEY, 1598 LockPatternUtils.PASSWORD_TYPE_KEY, 1599 SEPARATE_PROFILE_CHALLENGE_KEY 1600 }; 1601 1602 private static final String[] SETTINGS_TO_BACKUP = new String[] { 1603 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1604 Secure.LOCK_SCREEN_OWNER_INFO 1605 }; 1606 1607 private IMountService getMountService() { 1608 final IBinder service = ServiceManager.getService("mount"); 1609 if (service != null) { 1610 return IMountService.Stub.asInterface(service); 1611 } 1612 return null; 1613 } 1614 1615 private class GateKeeperDiedRecipient implements IBinder.DeathRecipient { 1616 @Override 1617 public void binderDied() { 1618 mGateKeeperService.asBinder().unlinkToDeath(this, 0); 1619 mGateKeeperService = null; 1620 } 1621 } 1622 1623 private synchronized IGateKeeperService getGateKeeperService() 1624 throws RemoteException { 1625 if (mGateKeeperService != null) { 1626 return mGateKeeperService; 1627 } 1628 1629 final IBinder service = 1630 ServiceManager.getService(Context.GATEKEEPER_SERVICE); 1631 if (service != null) { 1632 service.linkToDeath(new GateKeeperDiedRecipient(), 0); 1633 mGateKeeperService = IGateKeeperService.Stub.asInterface(service); 1634 return mGateKeeperService; 1635 } 1636 1637 Slog.e(TAG, "Unable to acquire GateKeeperService"); 1638 return null; 1639 } 1640 } 1641