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