1 /* 2 * Copyright (C) 2020 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.locksettings; 18 19 import static android.os.UserHandle.USER_SYSTEM; 20 21 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY; 22 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE; 23 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE; 24 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY; 25 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER; 26 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH; 27 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_STORE_ESCROW_KEY; 28 import static com.android.internal.widget.LockSettingsInternal.ArmRebootEscrowErrorCode; 29 30 import android.annotation.IntDef; 31 import android.annotation.NonNull; 32 import android.annotation.UserIdInt; 33 import android.content.Context; 34 import android.content.pm.PackageManager; 35 import android.content.pm.UserInfo; 36 import android.net.ConnectivityManager; 37 import android.net.Network; 38 import android.net.NetworkCapabilities; 39 import android.net.NetworkRequest; 40 import android.os.Handler; 41 import android.os.PowerManager; 42 import android.os.SystemClock; 43 import android.os.SystemProperties; 44 import android.os.UserManager; 45 import android.provider.DeviceConfig; 46 import android.provider.Settings; 47 import android.util.Slog; 48 49 import com.android.internal.annotations.GuardedBy; 50 import com.android.internal.annotations.VisibleForTesting; 51 import com.android.internal.util.FrameworkStatsLog; 52 import com.android.internal.util.IndentingPrintWriter; 53 import com.android.internal.widget.RebootEscrowListener; 54 55 import java.io.IOException; 56 import java.lang.annotation.Retention; 57 import java.lang.annotation.RetentionPolicy; 58 import java.text.SimpleDateFormat; 59 import java.util.ArrayList; 60 import java.util.Date; 61 import java.util.List; 62 import java.util.Locale; 63 import java.util.Objects; 64 65 import javax.crypto.SecretKey; 66 67 /** 68 * This class aims to persists the synthetic password(SP) across reboot in a secure way. In 69 * particular, it manages the encryption of the sp before reboot, and decryption of the sp after 70 * reboot. Here are the meaning of some terms. 71 * SP: synthetic password 72 * K_s: The RebootEscrowKey, i.e. AES-GCM key stored in memory 73 * K_k: AES-GCM key in android keystore 74 * RebootEscrowData: The synthetic password and its encrypted blob. We encrypt SP with K_s first, 75 * then with K_k, i.e. E(K_k, E(K_s, SP)) 76 */ 77 class RebootEscrowManager { 78 private static final String TAG = "RebootEscrowManager"; 79 80 /** 81 * Used in the database storage to indicate the boot count at which the reboot escrow was 82 * previously armed. 83 */ 84 @VisibleForTesting 85 public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count"; 86 87 static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp"; 88 static final String REBOOT_ESCROW_KEY_PROVIDER = "reboot_escrow_key_provider"; 89 90 /** 91 * The verified boot 2.0 vbmeta digest of the current slot, the property value is always 92 * available after boot. 93 */ 94 static final String VBMETA_DIGEST_PROP_NAME = "ro.boot.vbmeta.digest"; 95 /** 96 * The system prop contains vbmeta digest of the inactive slot. The build property is set after 97 * an OTA update. RebootEscrowManager will store it in disk before the OTA reboot, so the value 98 * is available for vbmeta digest verification after the device reboots. 99 */ 100 static final String OTHER_VBMETA_DIGEST_PROP_NAME = "ota.other.vbmeta_digest"; 101 static final String REBOOT_ESCROW_KEY_VBMETA_DIGEST = "reboot_escrow_key_vbmeta_digest"; 102 static final String REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST = 103 "reboot_escrow_key_other_vbmeta_digest"; 104 105 /** 106 * Number of boots until we consider the escrow data to be stale for the purposes of metrics. 107 * 108 * <p>If the delta between the current boot number and the boot number stored when the mechanism 109 * was armed is under this number and the escrow mechanism fails, we report it as a failure of 110 * the mechanism. 111 * 112 * <p>If the delta over this number and escrow fails, we will not report the metric as failed 113 * since there most likely was some other issue if the device rebooted several times before 114 * getting to the escrow restore code. 115 */ 116 private static final int BOOT_COUNT_TOLERANCE = 5; 117 118 /** 119 * The default retry specs for loading reboot escrow data. We will attempt to retry loading 120 * escrow data on temporarily errors, e.g. unavailable network. 121 */ 122 private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3; 123 private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30; 124 125 // 3 minutes. It's enough for the default 3 retries with 30 seconds interval 126 private static final int DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS = 180_000; 127 // 5 seconds. An extension of the overall RoR timeout to account for overhead. 128 private static final int DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS = 5000; 129 130 @IntDef(prefix = {"ERROR_"}, value = { 131 ERROR_NONE, 132 ERROR_UNKNOWN, 133 ERROR_NO_PROVIDER, 134 ERROR_LOAD_ESCROW_KEY, 135 ERROR_RETRY_COUNT_EXHAUSTED, 136 ERROR_UNLOCK_ALL_USERS, 137 ERROR_PROVIDER_MISMATCH, 138 ERROR_KEYSTORE_FAILURE, 139 ERROR_NO_NETWORK, 140 ERROR_TIMEOUT_EXHAUSTED, 141 }) 142 @Retention(RetentionPolicy.SOURCE) 143 @interface RebootEscrowErrorCode { 144 } 145 146 static final int ERROR_NONE = 0; 147 static final int ERROR_UNKNOWN = 1; 148 static final int ERROR_NO_PROVIDER = 2; 149 static final int ERROR_LOAD_ESCROW_KEY = 3; 150 static final int ERROR_RETRY_COUNT_EXHAUSTED = 4; 151 static final int ERROR_UNLOCK_ALL_USERS = 5; 152 static final int ERROR_PROVIDER_MISMATCH = 6; 153 static final int ERROR_KEYSTORE_FAILURE = 7; 154 static final int ERROR_NO_NETWORK = 8; 155 static final int ERROR_TIMEOUT_EXHAUSTED = 9; 156 157 private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; 158 159 /** 160 * Logs events for later debugging in bugreports. 161 */ 162 private final RebootEscrowEventLog mEventLog; 163 164 /** 165 * Used to track when the reboot escrow is wanted. Should stay true once escrow is requested 166 * unless clearRebootEscrow is called. This will allow all the active users to be unlocked 167 * after reboot. 168 */ 169 private boolean mRebootEscrowWanted; 170 171 /** Used to track when reboot escrow is ready. */ 172 private boolean mRebootEscrowReady; 173 174 /** Notified when mRebootEscrowReady changes. */ 175 private RebootEscrowListener mRebootEscrowListener; 176 177 /** Set when unlocking reboot escrow times out. */ 178 private boolean mRebootEscrowTimedOut = false; 179 180 /** 181 * Set when {@link #loadRebootEscrowDataWithRetry} is called to ensure the function is only 182 * called once. 183 */ 184 private boolean mLoadEscrowDataWithRetry = false; 185 186 /** 187 * Hold this lock when checking or generating the reboot escrow key. 188 */ 189 private final Object mKeyGenerationLock = new Object(); 190 191 /** 192 * Stores the reboot escrow data between when it's supplied and when 193 * {@link #armRebootEscrowIfNeeded()} is called. 194 */ 195 @GuardedBy("mKeyGenerationLock") 196 private RebootEscrowKey mPendingRebootEscrowKey; 197 198 private final UserManager mUserManager; 199 200 private final Injector mInjector; 201 202 private final LockSettingsStorage mStorage; 203 204 private final Callbacks mCallbacks; 205 206 private final RebootEscrowKeyStoreManager mKeyStoreManager; 207 208 private final Handler mHandler; 209 210 PowerManager.WakeLock mWakeLock; 211 212 private ConnectivityManager.NetworkCallback mNetworkCallback; 213 214 interface Callbacks { isUserSecure(int userId)215 boolean isUserSecure(int userId); 216 onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId)217 void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId); 218 } 219 220 static class Injector { 221 protected Context mContext; 222 private final RebootEscrowKeyStoreManager mKeyStoreManager; 223 private final LockSettingsStorage mStorage; 224 private RebootEscrowProviderInterface mRebootEscrowProvider; 225 Injector(Context context, LockSettingsStorage storage)226 Injector(Context context, LockSettingsStorage storage) { 227 mContext = context; 228 mStorage = storage; 229 mKeyStoreManager = new RebootEscrowKeyStoreManager(); 230 } 231 createRebootEscrowProvider()232 private RebootEscrowProviderInterface createRebootEscrowProvider() { 233 RebootEscrowProviderInterface rebootEscrowProvider; 234 if (serverBasedResumeOnReboot()) { 235 Slog.i(TAG, "Using server based resume on reboot"); 236 rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage); 237 } else { 238 Slog.i(TAG, "Using HAL based resume on reboot"); 239 rebootEscrowProvider = new RebootEscrowProviderHalImpl(); 240 } 241 242 if (rebootEscrowProvider.hasRebootEscrowSupport()) { 243 return rebootEscrowProvider; 244 } 245 return null; 246 } 247 post(Handler handler, Runnable runnable)248 void post(Handler handler, Runnable runnable) { 249 handler.post(runnable); 250 } 251 postDelayed(Handler handler, Runnable runnable, long delayMillis)252 void postDelayed(Handler handler, Runnable runnable, long delayMillis) { 253 handler.postDelayed(runnable, delayMillis); 254 } 255 serverBasedResumeOnReboot()256 public boolean serverBasedResumeOnReboot() { 257 // Always use the server based RoR if the HAL isn't installed on device. 258 if (!mContext.getPackageManager().hasSystemFeature( 259 PackageManager.FEATURE_REBOOT_ESCROW)) { 260 return true; 261 } 262 263 return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA, 264 "server_based_ror_enabled", false); 265 } 266 waitForInternet()267 public boolean waitForInternet() { 268 return DeviceConfig.getBoolean( 269 DeviceConfig.NAMESPACE_OTA, "wait_for_internet_ror", false); 270 } 271 isNetworkConnected()272 public boolean isNetworkConnected() { 273 final ConnectivityManager connectivityManager = 274 mContext.getSystemService(ConnectivityManager.class); 275 if (connectivityManager == null) { 276 return false; 277 } 278 279 Network activeNetwork = connectivityManager.getActiveNetwork(); 280 NetworkCapabilities networkCapabilities = 281 connectivityManager.getNetworkCapabilities(activeNetwork); 282 return networkCapabilities != null 283 && networkCapabilities.hasCapability( 284 NetworkCapabilities.NET_CAPABILITY_INTERNET) 285 && networkCapabilities.hasCapability( 286 NetworkCapabilities.NET_CAPABILITY_VALIDATED); 287 } 288 289 /** 290 * Request network with internet connectivity with timeout. 291 * 292 * @param networkCallback callback to be executed if connectivity manager exists. 293 * @return true if success 294 */ requestNetworkWithInternet( ConnectivityManager.NetworkCallback networkCallback)295 public boolean requestNetworkWithInternet( 296 ConnectivityManager.NetworkCallback networkCallback) { 297 final ConnectivityManager connectivityManager = 298 mContext.getSystemService(ConnectivityManager.class); 299 if (connectivityManager == null) { 300 return false; 301 } 302 NetworkRequest request = 303 new NetworkRequest.Builder() 304 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 305 .build(); 306 307 connectivityManager.requestNetwork( 308 request, networkCallback, getLoadEscrowTimeoutMillis()); 309 return true; 310 } 311 stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback)312 public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) { 313 final ConnectivityManager connectivityManager = 314 mContext.getSystemService(ConnectivityManager.class); 315 if (connectivityManager == null) { 316 return; 317 } 318 connectivityManager.unregisterNetworkCallback(networkCallback); 319 } 320 getContext()321 public Context getContext() { 322 return mContext; 323 } 324 getUserManager()325 public UserManager getUserManager() { 326 return (UserManager) mContext.getSystemService(Context.USER_SERVICE); 327 } 328 getKeyStoreManager()329 public RebootEscrowKeyStoreManager getKeyStoreManager() { 330 return mKeyStoreManager; 331 } 332 createRebootEscrowProviderIfNeeded()333 public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() { 334 // Initialize for the provider lazily. Because the device_config and service 335 // implementation apps may change when system server is running. 336 if (mRebootEscrowProvider == null) { 337 mRebootEscrowProvider = createRebootEscrowProvider(); 338 } 339 340 return mRebootEscrowProvider; 341 } 342 getWakeLock()343 PowerManager.WakeLock getWakeLock() { 344 final PowerManager pm = mContext.getSystemService(PowerManager.class); 345 return pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "RebootEscrowManager"); 346 } 347 getRebootEscrowProvider()348 public RebootEscrowProviderInterface getRebootEscrowProvider() { 349 return mRebootEscrowProvider; 350 } 351 clearRebootEscrowProvider()352 public void clearRebootEscrowProvider() { 353 mRebootEscrowProvider = null; 354 } 355 getBootCount()356 public int getBootCount() { 357 return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 358 0); 359 } 360 getCurrentTimeMillis()361 public long getCurrentTimeMillis() { 362 return System.currentTimeMillis(); 363 } 364 getLoadEscrowDataRetryLimit()365 public int getLoadEscrowDataRetryLimit() { 366 return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA, 367 "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT); 368 } 369 getLoadEscrowDataRetryIntervalSeconds()370 public int getLoadEscrowDataRetryIntervalSeconds() { 371 return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA, 372 "load_escrow_data_retry_interval_seconds", 373 DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS); 374 } 375 376 @VisibleForTesting getLoadEscrowTimeoutMillis()377 public int getLoadEscrowTimeoutMillis() { 378 return DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS; 379 } 380 381 @VisibleForTesting getWakeLockTimeoutMillis()382 public int getWakeLockTimeoutMillis() { 383 return getLoadEscrowTimeoutMillis() + DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS; 384 } 385 reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootCompleteInSeconds)386 public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, 387 int escrowDurationInSeconds, int vbmetaDigestStatus, 388 int durationSinceBootCompleteInSeconds) { 389 FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success, 390 errorCode, serviceType, attemptCount, escrowDurationInSeconds, 391 vbmetaDigestStatus, durationSinceBootCompleteInSeconds); 392 } 393 getEventLog()394 public RebootEscrowEventLog getEventLog() { 395 return new RebootEscrowEventLog(); 396 } 397 getVbmetaDigest(boolean other)398 public String getVbmetaDigest(boolean other) { 399 return other ? SystemProperties.get(OTHER_VBMETA_DIGEST_PROP_NAME) 400 : SystemProperties.get(VBMETA_DIGEST_PROP_NAME); 401 } 402 } 403 RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage, Handler handler)404 RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage, 405 Handler handler) { 406 this(new Injector(context, storage), callbacks, storage, handler); 407 } 408 409 @VisibleForTesting RebootEscrowManager(Injector injector, Callbacks callbacks, LockSettingsStorage storage, Handler handler)410 RebootEscrowManager(Injector injector, Callbacks callbacks, 411 LockSettingsStorage storage, Handler handler) { 412 mInjector = injector; 413 mCallbacks = callbacks; 414 mStorage = storage; 415 mUserManager = injector.getUserManager(); 416 mEventLog = injector.getEventLog(); 417 mKeyStoreManager = injector.getKeyStoreManager(); 418 mHandler = handler; 419 } 420 421 /** Wrapper function to set error code serialized through handler, */ setLoadEscrowDataErrorCode(@ebootEscrowErrorCode int value, Handler handler)422 private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) { 423 if (mInjector.waitForInternet()) { 424 mInjector.post( 425 handler, 426 () -> { 427 mLoadEscrowDataErrorCode = value; 428 }); 429 } else { 430 mLoadEscrowDataErrorCode = value; 431 } 432 } 433 434 /** Wrapper function to compare and set error code serialized through handler. */ compareAndSetLoadEscrowDataErrorCode( @ebootEscrowErrorCode int expectedValue, @RebootEscrowErrorCode int newValue, Handler handler)435 private void compareAndSetLoadEscrowDataErrorCode( 436 @RebootEscrowErrorCode int expectedValue, 437 @RebootEscrowErrorCode int newValue, 438 Handler handler) { 439 if (expectedValue == mLoadEscrowDataErrorCode) { 440 setLoadEscrowDataErrorCode(newValue, handler); 441 } 442 } 443 onGetRebootEscrowKeyFailed( List<UserInfo> users, int attemptCount, Handler retryHandler)444 private void onGetRebootEscrowKeyFailed( 445 List<UserInfo> users, int attemptCount, Handler retryHandler) { 446 Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage."); 447 for (UserInfo user : users) { 448 mStorage.removeRebootEscrow(user.id); 449 } 450 451 onEscrowRestoreComplete(false, attemptCount, retryHandler); 452 } 453 loadRebootEscrowDataIfAvailable(Handler retryHandler)454 void loadRebootEscrowDataIfAvailable(Handler retryHandler) { 455 List<UserInfo> users = mUserManager.getUsers(); 456 List<UserInfo> rebootEscrowUsers = new ArrayList<>(); 457 for (UserInfo user : users) { 458 if (mCallbacks.isUserSecure(user.id) && mStorage.hasRebootEscrow(user.id)) { 459 rebootEscrowUsers.add(user); 460 } 461 } 462 463 if (rebootEscrowUsers.isEmpty()) { 464 Slog.i(TAG, "No reboot escrow data found for users," 465 + " skipping loading escrow data"); 466 clearMetricsStorage(); 467 return; 468 } 469 470 // Acquire the wake lock to make sure our scheduled task will run. 471 mWakeLock = mInjector.getWakeLock(); 472 if (mWakeLock != null) { 473 mWakeLock.setReferenceCounted(false); 474 mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis()); 475 } 476 477 if (mInjector.waitForInternet()) { 478 // Timeout to stop retrying same as the wake lock timeout. 479 mInjector.postDelayed( 480 retryHandler, 481 () -> { 482 mRebootEscrowTimedOut = true; 483 }, 484 mInjector.getLoadEscrowTimeoutMillis()); 485 486 mInjector.post( 487 retryHandler, 488 () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers)); 489 return; 490 } 491 492 mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry( 493 retryHandler, 0, users, rebootEscrowUsers)); 494 } 495 scheduleLoadRebootEscrowDataOrFail( Handler retryHandler, int attemptNumber, List<UserInfo> users, List<UserInfo> rebootEscrowUsers)496 void scheduleLoadRebootEscrowDataOrFail( 497 Handler retryHandler, 498 int attemptNumber, 499 List<UserInfo> users, 500 List<UserInfo> rebootEscrowUsers) { 501 Objects.requireNonNull(retryHandler); 502 503 final int retryLimit = mInjector.getLoadEscrowDataRetryLimit(); 504 final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds(); 505 506 if (attemptNumber < retryLimit && !mRebootEscrowTimedOut) { 507 Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber); 508 mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry( 509 retryHandler, attemptNumber, users, rebootEscrowUsers), 510 retryIntervalInSeconds * 1000); 511 return; 512 } 513 514 if (mInjector.waitForInternet()) { 515 if (mRebootEscrowTimedOut) { 516 Slog.w(TAG, "Failed to load reboot escrow data within timeout"); 517 compareAndSetLoadEscrowDataErrorCode( 518 ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler); 519 } else { 520 Slog.w( 521 TAG, 522 "Failed to load reboot escrow data after " + attemptNumber + " attempts"); 523 compareAndSetLoadEscrowDataErrorCode( 524 ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler); 525 } 526 onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); 527 return; 528 } 529 530 Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); 531 if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) { 532 mLoadEscrowDataErrorCode = ERROR_NO_NETWORK; 533 } else { 534 mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; 535 } 536 onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); 537 } 538 loadRebootEscrowDataOnInternet( Handler retryHandler, List<UserInfo> users, List<UserInfo> rebootEscrowUsers)539 void loadRebootEscrowDataOnInternet( 540 Handler retryHandler, List<UserInfo> users, List<UserInfo> rebootEscrowUsers) { 541 542 // HAL-Based RoR does not require network connectivity. 543 if (!mInjector.serverBasedResumeOnReboot()) { 544 loadRebootEscrowDataWithRetry( 545 retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers); 546 return; 547 } 548 549 mNetworkCallback = 550 new ConnectivityManager.NetworkCallback() { 551 @Override 552 public void onAvailable(Network network) { 553 compareAndSetLoadEscrowDataErrorCode( 554 ERROR_NO_NETWORK, ERROR_NONE, retryHandler); 555 556 if (!mLoadEscrowDataWithRetry) { 557 mLoadEscrowDataWithRetry = true; 558 // Only kickoff retry mechanism on first onAvailable call. 559 loadRebootEscrowDataWithRetry( 560 retryHandler, 561 /* attemptNumber = */ 0, 562 users, 563 rebootEscrowUsers); 564 } 565 } 566 567 @Override 568 public void onUnavailable() { 569 Slog.w(TAG, "Failed to connect to network within timeout"); 570 compareAndSetLoadEscrowDataErrorCode( 571 ERROR_NONE, ERROR_NO_NETWORK, retryHandler); 572 onGetRebootEscrowKeyFailed(users, /* attemptCount= */ 0, retryHandler); 573 } 574 575 @Override 576 public void onLost(Network lostNetwork) { 577 // TODO(b/231660348): If network is lost, wait for network to become 578 // available again. 579 Slog.w(TAG, "Network lost, still attempting to load escrow key."); 580 compareAndSetLoadEscrowDataErrorCode( 581 ERROR_NONE, ERROR_NO_NETWORK, retryHandler); 582 } 583 }; 584 585 // Fallback to retrying without waiting for internet on failure. 586 boolean success = mInjector.requestNetworkWithInternet(mNetworkCallback); 587 if (!success) { 588 loadRebootEscrowDataWithRetry( 589 retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers); 590 } 591 } 592 loadRebootEscrowDataWithRetry( Handler retryHandler, int attemptNumber, List<UserInfo> users, List<UserInfo> rebootEscrowUsers)593 void loadRebootEscrowDataWithRetry( 594 Handler retryHandler, 595 int attemptNumber, 596 List<UserInfo> users, 597 List<UserInfo> rebootEscrowUsers) { 598 // Fetch the key from keystore to decrypt the escrow data & escrow key; this key is 599 // generated before reboot. Note that we will clear the escrow key even if the keystore key 600 // is null. 601 SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey(); 602 if (kk == null) { 603 Slog.i(TAG, "Failed to load the key for resume on reboot from key store."); 604 } 605 606 RebootEscrowKey escrowKey; 607 try { 608 escrowKey = getAndClearRebootEscrowKey(kk, retryHandler); 609 } catch (IOException e) { 610 Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e); 611 scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users, 612 rebootEscrowUsers); 613 return; 614 } 615 616 if (escrowKey == null) { 617 if (mLoadEscrowDataErrorCode == ERROR_NONE) { 618 // Specifically check if the RoR provider has changed after reboot. 619 int providerType = mInjector.serverBasedResumeOnReboot() 620 ? RebootEscrowProviderInterface.TYPE_SERVER_BASED 621 : RebootEscrowProviderInterface.TYPE_HAL; 622 if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) { 623 setLoadEscrowDataErrorCode(ERROR_PROVIDER_MISMATCH, retryHandler); 624 } else { 625 setLoadEscrowDataErrorCode(ERROR_LOAD_ESCROW_KEY, retryHandler); 626 } 627 } 628 onGetRebootEscrowKeyFailed(users, attemptNumber + 1, retryHandler); 629 return; 630 } 631 632 mEventLog.addEntry(RebootEscrowEvent.FOUND_ESCROW_DATA); 633 634 boolean allUsersUnlocked = true; 635 for (UserInfo user : rebootEscrowUsers) { 636 allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk); 637 } 638 639 if (!allUsersUnlocked) { 640 compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNLOCK_ALL_USERS, retryHandler); 641 } 642 onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1, retryHandler); 643 } 644 clearMetricsStorage()645 private void clearMetricsStorage() { 646 mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); 647 mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM); 648 mStorage.removeKey(REBOOT_ESCROW_KEY_VBMETA_DIGEST, USER_SYSTEM); 649 mStorage.removeKey(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, USER_SYSTEM); 650 mStorage.removeKey(REBOOT_ESCROW_KEY_PROVIDER, USER_SYSTEM); 651 } 652 getVbmetaDigestStatusOnRestoreComplete()653 private int getVbmetaDigestStatusOnRestoreComplete() { 654 String currentVbmetaDigest = mInjector.getVbmetaDigest(false); 655 String vbmetaDigestStored = mStorage.getString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, 656 "", USER_SYSTEM); 657 String vbmetaDigestOtherStored = mStorage.getString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, 658 "", USER_SYSTEM); 659 660 // The other vbmeta digest is never set, assume no slot switch is attempted. 661 if (vbmetaDigestOtherStored.isEmpty()) { 662 if (currentVbmetaDigest.equals(vbmetaDigestStored)) { 663 return FrameworkStatsLog 664 .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; 665 } 666 return FrameworkStatsLog 667 .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; 668 } 669 670 // The other vbmeta digest is set, we expect to boot into the new slot. 671 if (currentVbmetaDigest.equals(vbmetaDigestOtherStored)) { 672 return FrameworkStatsLog 673 .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; 674 } else if (currentVbmetaDigest.equals(vbmetaDigestStored)) { 675 return FrameworkStatsLog 676 .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_FALLBACK_SLOT; 677 } 678 return FrameworkStatsLog 679 .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; 680 } 681 reportMetricOnRestoreComplete( boolean success, int attemptCount, Handler retryHandler)682 private void reportMetricOnRestoreComplete( 683 boolean success, int attemptCount, Handler retryHandler) { 684 int serviceType = mInjector.serverBasedResumeOnReboot() 685 ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED 686 : FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL; 687 688 long armedTimestamp = mStorage.getLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, -1, 689 USER_SYSTEM); 690 int escrowDurationInSeconds = -1; 691 long currentTimeStamp = mInjector.getCurrentTimeMillis(); 692 if (armedTimestamp != -1 && currentTimeStamp > armedTimestamp) { 693 escrowDurationInSeconds = (int) (currentTimeStamp - armedTimestamp) / 1000; 694 } 695 696 int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete(); 697 if (!success) { 698 compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNKNOWN, retryHandler); 699 } 700 701 Slog.i( 702 TAG, 703 "Reporting RoR recovery metrics, success: " 704 + success 705 + ", service type: " 706 + serviceType 707 + ", error code: " 708 + mLoadEscrowDataErrorCode); 709 // TODO(179105110) report the duration since boot complete. 710 mInjector.reportMetric( 711 success, 712 mLoadEscrowDataErrorCode, 713 serviceType, 714 attemptCount, 715 escrowDurationInSeconds, 716 vbmetaDigestStatus, 717 -1); 718 719 setLoadEscrowDataErrorCode(ERROR_NONE, retryHandler); 720 } 721 onEscrowRestoreComplete(boolean success, int attemptCount, Handler retryHandler)722 private void onEscrowRestoreComplete(boolean success, int attemptCount, Handler retryHandler) { 723 int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM); 724 725 int bootCountDelta = mInjector.getBootCount() - previousBootCount; 726 if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) { 727 reportMetricOnRestoreComplete(success, attemptCount, retryHandler); 728 } 729 // Clear the old key in keystore. A new key will be generated by new RoR requests. 730 mKeyStoreManager.clearKeyStoreEncryptionKey(); 731 // Clear the saved reboot escrow provider 732 mInjector.clearRebootEscrowProvider(); 733 clearMetricsStorage(); 734 735 if (mNetworkCallback != null) { 736 mInjector.stopRequestingNetwork(mNetworkCallback); 737 } 738 739 if (mWakeLock != null) { 740 mWakeLock.release(); 741 } 742 } 743 getAndClearRebootEscrowKey(SecretKey kk, Handler retryHandler)744 private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk, Handler retryHandler) 745 throws IOException { 746 RebootEscrowProviderInterface rebootEscrowProvider = 747 mInjector.createRebootEscrowProviderIfNeeded(); 748 if (rebootEscrowProvider == null) { 749 Slog.w( 750 TAG, 751 "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); 752 setLoadEscrowDataErrorCode(ERROR_NO_PROVIDER, retryHandler); 753 return null; 754 } 755 756 // Server based RoR always need the decryption key from keystore. 757 if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED 758 && kk == null) { 759 setLoadEscrowDataErrorCode(ERROR_KEYSTORE_FAILURE, retryHandler); 760 return null; 761 } 762 763 // The K_s blob maybe encrypted by K_k as well. 764 RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(kk); 765 if (key != null) { 766 mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_STORED_KEK); 767 } 768 return key; 769 } 770 restoreRebootEscrowForUser(@serIdInt int userId, RebootEscrowKey ks, SecretKey kk)771 private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey ks, 772 SecretKey kk) { 773 if (!mStorage.hasRebootEscrow(userId)) { 774 return false; 775 } 776 777 try { 778 byte[] blob = mStorage.readRebootEscrow(userId); 779 mStorage.removeRebootEscrow(userId); 780 781 RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(ks, blob, kk); 782 783 mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(), 784 escrowData.getSyntheticPassword(), userId); 785 Slog.i(TAG, "Restored reboot escrow data for user " + userId); 786 mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_LSKF_FOR_USER, userId); 787 return true; 788 } catch (IOException e) { 789 Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e); 790 return false; 791 } 792 } 793 callToRebootEscrowIfNeeded(@serIdInt int userId, byte spVersion, byte[] syntheticPassword)794 void callToRebootEscrowIfNeeded(@UserIdInt int userId, byte spVersion, 795 byte[] syntheticPassword) { 796 if (!mRebootEscrowWanted) { 797 return; 798 } 799 800 if (mInjector.createRebootEscrowProviderIfNeeded() == null) { 801 Slog.w(TAG, "Not storing escrow data, RebootEscrowProvider is unavailable"); 802 return; 803 } 804 805 RebootEscrowKey escrowKey = generateEscrowKeyIfNeeded(); 806 if (escrowKey == null) { 807 Slog.e(TAG, "Could not generate escrow key"); 808 return; 809 } 810 811 SecretKey kk = mKeyStoreManager.generateKeyStoreEncryptionKeyIfNeeded(); 812 if (kk == null) { 813 Slog.e(TAG, "Failed to generate encryption key from keystore."); 814 return; 815 } 816 817 final RebootEscrowData escrowData; 818 try { 819 escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion, 820 syntheticPassword, kk); 821 } catch (IOException e) { 822 setRebootEscrowReady(false); 823 Slog.w(TAG, "Could not escrow reboot data", e); 824 return; 825 } 826 827 mStorage.writeRebootEscrow(userId, escrowData.getBlob()); 828 mEventLog.addEntry(RebootEscrowEvent.STORED_LSKF_FOR_USER, userId); 829 830 setRebootEscrowReady(true); 831 } 832 generateEscrowKeyIfNeeded()833 private RebootEscrowKey generateEscrowKeyIfNeeded() { 834 synchronized (mKeyGenerationLock) { 835 if (mPendingRebootEscrowKey != null) { 836 return mPendingRebootEscrowKey; 837 } 838 839 RebootEscrowKey key; 840 try { 841 key = RebootEscrowKey.generate(); 842 } catch (IOException e) { 843 Slog.w(TAG, "Could not generate reboot escrow key"); 844 return null; 845 } 846 847 mPendingRebootEscrowKey = key; 848 return key; 849 } 850 } 851 clearRebootEscrowIfNeeded()852 private void clearRebootEscrowIfNeeded() { 853 mRebootEscrowWanted = false; 854 setRebootEscrowReady(false); 855 856 // We want to clear the internal data inside the provider, so always try to create the 857 // provider. 858 RebootEscrowProviderInterface rebootEscrowProvider = 859 mInjector.createRebootEscrowProviderIfNeeded(); 860 if (rebootEscrowProvider == null) { 861 Slog.w(TAG, "RebootEscrowProvider is unavailable for clear request"); 862 } else { 863 rebootEscrowProvider.clearRebootEscrowKey(); 864 } 865 866 mInjector.clearRebootEscrowProvider(); 867 clearMetricsStorage(); 868 869 List<UserInfo> users = mUserManager.getUsers(); 870 for (UserInfo user : users) { 871 mStorage.removeRebootEscrow(user.id); 872 } 873 874 mEventLog.addEntry(RebootEscrowEvent.CLEARED_LSKF_REQUEST); 875 } 876 armRebootEscrowIfNeeded()877 @ArmRebootEscrowErrorCode int armRebootEscrowIfNeeded() { 878 if (!mRebootEscrowReady) { 879 return ARM_REBOOT_ERROR_ESCROW_NOT_READY; 880 } 881 882 RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider(); 883 if (rebootEscrowProvider == null) { 884 Slog.w(TAG, "Not storing escrow key, RebootEscrowProvider is unavailable"); 885 clearRebootEscrowIfNeeded(); 886 return ARM_REBOOT_ERROR_NO_PROVIDER; 887 } 888 889 int expectedProviderType = mInjector.serverBasedResumeOnReboot() 890 ? RebootEscrowProviderInterface.TYPE_SERVER_BASED 891 : RebootEscrowProviderInterface.TYPE_HAL; 892 int actualProviderType = rebootEscrowProvider.getType(); 893 if (expectedProviderType != actualProviderType) { 894 Slog.w(TAG, "Expect reboot escrow provider " + expectedProviderType 895 + ", but the RoR is prepared with " + actualProviderType 896 + ". Please prepare the RoR again."); 897 clearRebootEscrowIfNeeded(); 898 return ARM_REBOOT_ERROR_PROVIDER_MISMATCH; 899 } 900 901 RebootEscrowKey escrowKey; 902 synchronized (mKeyGenerationLock) { 903 escrowKey = mPendingRebootEscrowKey; 904 } 905 906 if (escrowKey == null) { 907 Slog.e(TAG, "Escrow key is null, but escrow was marked as ready"); 908 clearRebootEscrowIfNeeded(); 909 return ARM_REBOOT_ERROR_NO_ESCROW_KEY; 910 } 911 912 // We will use the same key from keystore to encrypt the escrow key and escrow data blob. 913 SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey(); 914 if (kk == null) { 915 Slog.e(TAG, "Failed to get encryption key from keystore."); 916 clearRebootEscrowIfNeeded(); 917 return ARM_REBOOT_ERROR_KEYSTORE_FAILURE; 918 } 919 920 // TODO(b/183140900) design detailed errors for store escrow key errors. 921 // We don't clear rebootEscrow here, because some errors may be recoverable, e.g. network 922 // unavailable for server based provider. 923 boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk); 924 if (!armedRebootEscrow) { 925 return ARM_REBOOT_ERROR_STORE_ESCROW_KEY; 926 } 927 928 mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM); 929 mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(), 930 USER_SYSTEM); 931 // Store the vbmeta digest of both slots. 932 mStorage.setString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, mInjector.getVbmetaDigest(false), 933 USER_SYSTEM); 934 mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, 935 mInjector.getVbmetaDigest(true), USER_SYSTEM); 936 mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM); 937 mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); 938 939 return ARM_REBOOT_ERROR_NONE; 940 } 941 setRebootEscrowReady(boolean ready)942 private void setRebootEscrowReady(boolean ready) { 943 if (mRebootEscrowReady != ready) { 944 mHandler.post(() -> mRebootEscrowListener.onPreparedForReboot(ready)); 945 } 946 mRebootEscrowReady = ready; 947 } 948 prepareRebootEscrow()949 boolean prepareRebootEscrow() { 950 clearRebootEscrowIfNeeded(); 951 if (mInjector.createRebootEscrowProviderIfNeeded() == null) { 952 Slog.w(TAG, "No reboot escrow provider, skipping resume on reboot preparation."); 953 return false; 954 } 955 956 mRebootEscrowWanted = true; 957 mEventLog.addEntry(RebootEscrowEvent.REQUESTED_LSKF); 958 return true; 959 } 960 clearRebootEscrow()961 boolean clearRebootEscrow() { 962 clearRebootEscrowIfNeeded(); 963 return true; 964 } 965 setRebootEscrowListener(RebootEscrowListener listener)966 void setRebootEscrowListener(RebootEscrowListener listener) { 967 mRebootEscrowListener = listener; 968 } 969 970 @VisibleForTesting 971 public static class RebootEscrowEvent { 972 static final int FOUND_ESCROW_DATA = 1; 973 static final int SET_ARMED_STATUS = 2; 974 static final int CLEARED_LSKF_REQUEST = 3; 975 static final int RETRIEVED_STORED_KEK = 4; 976 static final int REQUESTED_LSKF = 5; 977 static final int STORED_LSKF_FOR_USER = 6; 978 static final int RETRIEVED_LSKF_FOR_USER = 7; 979 980 final int mEventId; 981 final Integer mUserId; 982 final long mWallTime; 983 final long mTimestamp; 984 RebootEscrowEvent(int eventId)985 RebootEscrowEvent(int eventId) { 986 this(eventId, null); 987 } 988 RebootEscrowEvent(int eventId, Integer userId)989 RebootEscrowEvent(int eventId, Integer userId) { 990 mEventId = eventId; 991 mUserId = userId; 992 mTimestamp = SystemClock.uptimeMillis(); 993 mWallTime = System.currentTimeMillis(); 994 } 995 getEventDescription()996 String getEventDescription() { 997 switch (mEventId) { 998 case FOUND_ESCROW_DATA: 999 return "Found escrow data"; 1000 case SET_ARMED_STATUS: 1001 return "Set armed status"; 1002 case CLEARED_LSKF_REQUEST: 1003 return "Cleared request for LSKF"; 1004 case RETRIEVED_STORED_KEK: 1005 return "Retrieved stored KEK"; 1006 case REQUESTED_LSKF: 1007 return "Requested LSKF"; 1008 case STORED_LSKF_FOR_USER: 1009 return "Stored LSKF for user"; 1010 case RETRIEVED_LSKF_FOR_USER: 1011 return "Retrieved LSKF for user"; 1012 default: 1013 return "Unknown event ID " + mEventId; 1014 } 1015 } 1016 } 1017 1018 @VisibleForTesting 1019 public static class RebootEscrowEventLog { 1020 private RebootEscrowEvent[] mEntries = new RebootEscrowEvent[16]; 1021 private int mNextIndex = 0; 1022 addEntry(int eventId)1023 void addEntry(int eventId) { 1024 addEntryInternal(new RebootEscrowEvent(eventId)); 1025 } 1026 addEntry(int eventId, int userId)1027 void addEntry(int eventId, int userId) { 1028 addEntryInternal(new RebootEscrowEvent(eventId, userId)); 1029 } 1030 addEntryInternal(RebootEscrowEvent event)1031 private void addEntryInternal(RebootEscrowEvent event) { 1032 final int index = mNextIndex; 1033 mEntries[index] = event; 1034 mNextIndex = (mNextIndex + 1) % mEntries.length; 1035 } 1036 dump(@onNull IndentingPrintWriter pw)1037 void dump(@NonNull IndentingPrintWriter pw) { 1038 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); 1039 1040 for (int i = 0; i < mEntries.length; ++i) { 1041 RebootEscrowEvent event = mEntries[(i + mNextIndex) % mEntries.length]; 1042 if (event == null) { 1043 continue; 1044 } 1045 1046 pw.print("Event #"); 1047 pw.println(i); 1048 1049 pw.println(" time=" + sdf.format(new Date(event.mWallTime)) 1050 + " (timestamp=" + event.mTimestamp + ")"); 1051 1052 pw.print(" event="); 1053 pw.println(event.getEventDescription()); 1054 1055 if (event.mUserId != null) { 1056 pw.print(" user="); 1057 pw.println(event.mUserId); 1058 } 1059 } 1060 } 1061 } 1062 dump(@onNull IndentingPrintWriter pw)1063 void dump(@NonNull IndentingPrintWriter pw) { 1064 pw.print("mRebootEscrowWanted="); 1065 pw.println(mRebootEscrowWanted); 1066 1067 pw.print("mRebootEscrowReady="); 1068 pw.println(mRebootEscrowReady); 1069 1070 pw.print("mRebootEscrowListener="); 1071 pw.println(mRebootEscrowListener); 1072 1073 pw.print("mLoadEscrowDataErrorCode="); 1074 pw.println(mLoadEscrowDataErrorCode); 1075 1076 boolean keySet; 1077 synchronized (mKeyGenerationLock) { 1078 keySet = mPendingRebootEscrowKey != null; 1079 } 1080 1081 pw.print("mPendingRebootEscrowKey is "); 1082 pw.println(keySet ? "set" : "not set"); 1083 1084 RebootEscrowProviderInterface provider = mInjector.getRebootEscrowProvider(); 1085 String providerType = provider == null ? "null" : String.valueOf(provider.getType()); 1086 pw.print("RebootEscrowProvider type is " + providerType); 1087 1088 pw.println(); 1089 pw.println("Event log:"); 1090 pw.increaseIndent(); 1091 mEventLog.dump(pw); 1092 pw.println(); 1093 pw.decreaseIndent(); 1094 } 1095 } 1096