diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index a42448c261d48..b474d7c959968 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -169,8 +169,9 @@ public class RecoverySystem {
public @interface ResumeOnRebootRebootErrorCode {}
/**
- * The preparation of resume on reboot succeeds. Don't expose it because a successful reboot
- * should just reboot the device.
+ * The preparation of resume on reboot succeeds.
+ *
+ *
Don't expose it because a successful reboot should just reboot the device.
* @hide
*/
public static final int RESUME_ON_REBOOT_REBOOT_ERROR_NONE = 0;
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index f5df3abad658f..940979d7cd1f8 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -16,15 +16,41 @@
package com.android.internal.widget;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.admin.PasswordMetrics;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* LockSettingsService local system service interface.
*
* @hide Only for use within the system server.
*/
public abstract class LockSettingsInternal {
+ /** ErrorCode for armRebootEscrow failures. **/
+ @IntDef(prefix = {"ARM_REBOOT_ERROR_"}, value = {
+ ARM_REBOOT_ERROR_NONE,
+ ARM_REBOOT_ERROR_UNSPECIFIED,
+ ARM_REBOOT_ERROR_ESCROW_NOT_READY,
+ ARM_REBOOT_ERROR_NO_PROVIDER,
+ ARM_REBOOT_ERROR_PROVIDER_MISMATCH,
+ ARM_REBOOT_ERROR_NO_ESCROW_KEY,
+ ARM_REBOOT_ERROR_KEYSTORE_FAILURE,
+ ARM_REBOOT_ERROR_STORE_ESCROW_KEY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ArmRebootEscrowErrorCode {}
+
+ public static final int ARM_REBOOT_ERROR_NONE = 0;
+ public static final int ARM_REBOOT_ERROR_UNSPECIFIED = 1;
+ public static final int ARM_REBOOT_ERROR_ESCROW_NOT_READY = 2;
+ public static final int ARM_REBOOT_ERROR_NO_PROVIDER = 3;
+ public static final int ARM_REBOOT_ERROR_PROVIDER_MISMATCH = 4;
+ public static final int ARM_REBOOT_ERROR_NO_ESCROW_KEY = 5;
+ public static final int ARM_REBOOT_ERROR_KEYSTORE_FAILURE = 6;
+ public static final int ARM_REBOOT_ERROR_STORE_ESCROW_KEY = 7;
+ // TODO(b/183140900) split store escrow key errors into detailed ones.
/**
* Create an escrow token for the current user, which can later be used to unlock FBE
@@ -104,9 +130,9 @@ public abstract class LockSettingsInternal {
* Should be called immediately before rebooting for an update. This depends on {@link
* #prepareRebootEscrow()} having been called and the escrow completing.
*
- * @return true if the arming worked
+ * @return ARM_ERROR_NONE if the arming worked
*/
- public abstract boolean armRebootEscrow();
+ public abstract @ArmRebootEscrowErrorCode int armRebootEscrow();
/**
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index f96fa0969de9c..14d9d857d8746 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -3488,7 +3488,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public boolean armRebootEscrow() {
+ public @ArmRebootEscrowErrorCode int armRebootEscrow() {
return mRebootEscrowManager.armRebootEscrowIfNeeded();
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 76ecc1acc7ac5..c01523a4bc404 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -18,6 +18,15 @@ package com.android.server.locksettings;
import static android.os.UserHandle.USER_SYSTEM;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_STORE_ESCROW_KEY;
+import static com.android.internal.widget.LockSettingsInternal.ArmRebootEscrowErrorCode;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -577,16 +586,14 @@ class RebootEscrowManager {
mRebootEscrowWanted = false;
setRebootEscrowReady(false);
-
RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
if (rebootEscrowProvider == null) {
- Slog.w(TAG,
- "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
- return;
+ Slog.w(TAG, "RebootEscrowProvider is unavailable for clear request");
+ } else {
+ rebootEscrowProvider.clearRebootEscrowKey();
}
clearMetricsStorage();
- rebootEscrowProvider.clearRebootEscrowKey();
List users = mUserManager.getUsers();
for (UserInfo user : users) {
@@ -596,20 +603,30 @@ class RebootEscrowManager {
mEventLog.addEntry(RebootEscrowEvent.CLEARED_LSKF_REQUEST);
}
- boolean armRebootEscrowIfNeeded() {
+ @ArmRebootEscrowErrorCode int armRebootEscrowIfNeeded() {
if (!mRebootEscrowReady) {
- return false;
+ return ARM_REBOOT_ERROR_ESCROW_NOT_READY;
}
RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
if (rebootEscrowProvider == null) {
Slog.w(TAG,
"Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
- return false;
+ clearRebootEscrowIfNeeded();
+ return ARM_REBOOT_ERROR_NO_PROVIDER;
}
+ int expectedProviderType = mInjector.serverBasedResumeOnReboot()
+ ? RebootEscrowProviderInterface.TYPE_SERVER_BASED
+ : RebootEscrowProviderInterface.TYPE_HAL;
int actualProviderType = rebootEscrowProvider.getType();
- // TODO(b/183140900) Fail the reboot if provider type mismatches.
+ if (expectedProviderType != actualProviderType) {
+ Slog.w(TAG, "Expect reboot escrow provider " + expectedProviderType
+ + ", but the RoR is prepared with " + actualProviderType
+ + ". Please prepare the RoR again.");
+ clearRebootEscrowIfNeeded();
+ return ARM_REBOOT_ERROR_PROVIDER_MISMATCH;
+ }
RebootEscrowKey escrowKey;
synchronized (mKeyGenerationLock) {
@@ -618,30 +635,38 @@ class RebootEscrowManager {
if (escrowKey == null) {
Slog.e(TAG, "Escrow key is null, but escrow was marked as ready");
- return false;
+ clearRebootEscrowIfNeeded();
+ return ARM_REBOOT_ERROR_NO_ESCROW_KEY;
}
// We will use the same key from keystore to encrypt the escrow key and escrow data blob.
SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
if (kk == null) {
Slog.e(TAG, "Failed to get encryption key from keystore.");
- return false;
- }
- boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk);
- if (armedRebootEscrow) {
- mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
- mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(),
- USER_SYSTEM);
- // Store the vbmeta digest of both slots.
- mStorage.setString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, mInjector.getVbmetaDigest(false),
- USER_SYSTEM);
- mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST,
- mInjector.getVbmetaDigest(true), USER_SYSTEM);
- mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM);
- mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
+ clearRebootEscrowIfNeeded();
+ return ARM_REBOOT_ERROR_KEYSTORE_FAILURE;
}
- return armedRebootEscrow;
+ // TODO(b/183140900) design detailed errors for store escrow key errors.
+ // We don't clear rebootEscrow here, because some errors may be recoverable, e.g. network
+ // unavailable for server based provider.
+ boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk);
+ if (!armedRebootEscrow) {
+ return ARM_REBOOT_ERROR_STORE_ESCROW_KEY;
+ }
+
+ mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
+ mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(),
+ USER_SYSTEM);
+ // Store the vbmeta digest of both slots.
+ mStorage.setString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, mInjector.getVbmetaDigest(false),
+ USER_SYSTEM);
+ mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST,
+ mInjector.getVbmetaDigest(true), USER_SYSTEM);
+ mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM);
+ mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
+
+ return ARM_REBOOT_ERROR_NONE;
}
private void setRebootEscrowReady(boolean ready) {
@@ -663,10 +688,6 @@ class RebootEscrowManager {
}
boolean clearRebootEscrow() {
- if (mInjector.getRebootEscrowProvider() == null) {
- return false;
- }
-
clearRebootEscrowIfNeeded();
return true;
}
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 6ea030f56d4d3..81a51e2906641 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -25,6 +25,8 @@ import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIE
import static android.os.RecoverySystem.ResumeOnRebootRebootErrorCode;
import static android.os.UserHandle.USER_SYSTEM;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE;
+
import android.annotation.IntDef;
import android.content.Context;
import android.content.IntentSender;
@@ -47,6 +49,7 @@ import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.FastImmutableArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -143,7 +146,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
*/
@IntDef({ ROR_NEED_PREPARATION,
ROR_SKIP_PREPARATION_AND_NOTIFY,
- ROR_SKIP_PREPARATION_NOT_NOTIFY})
+ ROR_SKIP_PREPARATION_NOT_NOTIFY })
private @interface ResumeOnRebootActionsOnRequest {}
/**
@@ -151,9 +154,42 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
*/
@IntDef({ ROR_NOT_REQUESTED,
ROR_REQUESTED_NEED_CLEAR,
- ROR_REQUESTED_SKIP_CLEAR})
+ ROR_REQUESTED_SKIP_CLEAR })
private @interface ResumeOnRebootActionsOnClear {}
+ /**
+ * Fatal arm escrow errors from lock settings that means the RoR is in a bad state. So clients
+ * need to prepare RoR again.
+ */
+ static final FastImmutableArraySet FATAL_ARM_ESCROW_ERRORS =
+ new FastImmutableArraySet<>(new Integer[]{
+ LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY,
+ LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER,
+ LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH,
+ LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY,
+ LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE,
+ });
+
+ /**
+ * The error details for ArmRebootEscrow. It contains error codes from RecoverySystemService
+ * and LockSettingsService.
+ */
+ static class RebootPreparationError {
+ final @ResumeOnRebootRebootErrorCode int mRebootErrorCode;
+ final int mProviderErrorCode; // The supplemental error code from lock settings
+
+ RebootPreparationError(int rebootErrorCode, int providerErrorCode) {
+ mRebootErrorCode = rebootErrorCode;
+ mProviderErrorCode = providerErrorCode;
+ }
+
+ int getErrorCodeForMetrics() {
+ // The ResumeOnRebootRebootErrorCode are aligned with 1000; so it's safe to add them
+ // for metrics purpose.
+ return mRebootErrorCode + mProviderErrorCode;
+ }
+ }
+
/**
* Manages shared preference, i.e. the storage used for metrics reporting.
*/
@@ -709,34 +745,40 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return true;
}
- private @ResumeOnRebootRebootErrorCode int armRebootEscrow(String packageName,
+ private RebootPreparationError armRebootEscrow(String packageName,
boolean slotSwitch) {
if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
- return RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
+ return new RebootPreparationError(
+ RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME, ARM_REBOOT_ERROR_NONE);
}
if (!isLskfCaptured(packageName)) {
- return RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
+ return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
+ ARM_REBOOT_ERROR_NONE);
}
if (!verifySlotForNextBoot(slotSwitch)) {
- return RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
+ return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
+ ARM_REBOOT_ERROR_NONE);
}
final long origId = Binder.clearCallingIdentity();
- boolean result;
+ int providerErrorCode;
try {
- result = mInjector.getLockSettingsService().armRebootEscrow();
+ providerErrorCode = mInjector.getLockSettingsService().armRebootEscrow();
} finally {
Binder.restoreCallingIdentity(origId);
}
- if (!result) {
- Slog.w(TAG, "Failure to escrow key for reboot");
- return RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
+ if (providerErrorCode != ARM_REBOOT_ERROR_NONE) {
+ Slog.w(TAG, "Failure to escrow key for reboot, providerErrorCode: "
+ + providerErrorCode);
+ return new RebootPreparationError(
+ RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE, providerErrorCode);
}
- return RESUME_ON_REBOOT_REBOOT_ERROR_NONE;
+ return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_NONE,
+ ARM_REBOOT_ERROR_NONE);
}
private boolean useServerBasedRoR() {
@@ -750,7 +792,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
}
private void reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch,
- @ResumeOnRebootRebootErrorCode int errorCode) {
+ RebootPreparationError escrowError) {
int uid = mInjector.getUidFromPackageName(packageName);
boolean serverBased = useServerBasedRoR();
int preparedClientCount;
@@ -773,15 +815,31 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
+ " request count %d, lskf captured count %d, duration since lskf captured"
+ " %d seconds.", packageName, preparedClientCount, requestCount,
lskfCapturedCount, durationSeconds));
- mInjector.reportRebootEscrowRebootMetrics(errorCode, uid, preparedClientCount,
- requestCount, slotSwitch, serverBased, durationSeconds, lskfCapturedCount);
+ mInjector.reportRebootEscrowRebootMetrics(escrowError.getErrorCodeForMetrics(), uid,
+ preparedClientCount, requestCount, slotSwitch, serverBased, durationSeconds,
+ lskfCapturedCount);
+ }
+
+ private void clearRoRPreparationStateOnRebootFailure(RebootPreparationError escrowError) {
+ if (!FATAL_ARM_ESCROW_ERRORS.contains(escrowError.mProviderErrorCode)) {
+ return;
+ }
+
+ Slog.w(TAG, "Clearing resume on reboot states for all clients on arm escrow error: "
+ + escrowError.mProviderErrorCode);
+ synchronized (this) {
+ mCallerPendingRequest.clear();
+ mCallerPreparedForReboot.clear();
+ }
}
private @ResumeOnRebootRebootErrorCode int rebootWithLskfImpl(String packageName, String reason,
boolean slotSwitch) {
- @ResumeOnRebootRebootErrorCode int errorCode = armRebootEscrow(packageName, slotSwitch);
- reportMetricsOnRebootWithLskf(packageName, slotSwitch, errorCode);
+ RebootPreparationError escrowError = armRebootEscrow(packageName, slotSwitch);
+ reportMetricsOnRebootWithLskf(packageName, slotSwitch, escrowError);
+ clearRoRPreparationStateOnRebootFailure(escrowError);
+ @ResumeOnRebootRebootErrorCode int errorCode = escrowError.mRebootErrorCode;
if (errorCode != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
return errorCode;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 691d174f55f81..f2bb1d662ed9e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -86,6 +86,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath();
}
+ @Override
+ String getRebootEscrowFile(int userId) {
+ return makeDirs(mStorageDir, super.getRebootEscrowFile(userId)).getAbsolutePath();
+ }
+
@Override
protected File getSyntheticPasswordDirectoryForUser(int userId) {
return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 8c08226201a81..49a54ec1354b1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -21,6 +21,11 @@ import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_PROFILE;
import static android.os.UserHandle.USER_SYSTEM;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH;
+import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_STORE_ESCROW_KEY;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -327,7 +332,7 @@ public class RebootEscrowManagerTests {
assertNull(
mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
assertNotNull(
mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
verify(mRebootEscrow).storeKey(any());
@@ -351,7 +356,7 @@ public class RebootEscrowManagerTests {
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
@@ -373,7 +378,7 @@ public class RebootEscrowManagerTests {
assertNull(
mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any());
- assertFalse(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_STORE_ESCROW_KEY, mService.armRebootEscrowIfNeeded());
verify(mRebootEscrow).storeKey(any());
}
@@ -396,7 +401,7 @@ public class RebootEscrowManagerTests {
assertNull(
mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
assertNotNull(
mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
verify(mRebootEscrow, times(1)).storeKey(any());
@@ -408,15 +413,24 @@ public class RebootEscrowManagerTests {
@Test
public void armService_NoInitialization_Failure() throws Exception {
- assertFalse(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_ESCROW_NOT_READY, mService.armRebootEscrowIfNeeded());
verifyNoMoreInteractions(mRebootEscrow);
}
@Test
public void armService_RebootEscrowServiceException_Failure() throws Exception {
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mRebootEscrow, never()).storeKey(any());
+
doThrow(RemoteException.class).when(mRebootEscrow).storeKey(any());
- assertFalse(mService.armRebootEscrowIfNeeded());
- verifyNoMoreInteractions(mRebootEscrow);
+ assertEquals(ARM_REBOOT_ERROR_STORE_ESCROW_KEY, mService.armRebootEscrowIfNeeded());
+ verify(mRebootEscrow).storeKey(any());
}
@Test
@@ -439,7 +453,7 @@ public class RebootEscrowManagerTests {
verify(mRebootEscrow, never()).storeKey(any());
ArgumentCaptor keyByteCaptor = ArgumentCaptor.forClass(byte[].class);
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mRebootEscrow).storeKey(keyByteCaptor.capture());
verify(mKeyStoreManager).getKeyStoreEncryptionKey();
@@ -483,7 +497,7 @@ public class RebootEscrowManagerTests {
// Use x -> x for both wrap & unwrap functions.
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
assertTrue(mStorage.hasRebootEscrowServerBlob());
@@ -520,7 +534,7 @@ public class RebootEscrowManagerTests {
// Use x -> x for both wrap & unwrap functions.
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
assertTrue(mStorage.hasRebootEscrowServerBlob());
@@ -557,7 +571,7 @@ public class RebootEscrowManagerTests {
// Use x -> x for both wrap & unwrap functions.
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
assertTrue(mStorage.hasRebootEscrowServerBlob());
@@ -597,7 +611,7 @@ public class RebootEscrowManagerTests {
// Use x -> x for both wrap & unwrap functions.
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
assertTrue(mStorage.hasRebootEscrowServerBlob());
@@ -635,7 +649,7 @@ public class RebootEscrowManagerTests {
verify(mRebootEscrow, never()).storeKey(any());
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mRebootEscrow).storeKey(any());
assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
@@ -695,7 +709,7 @@ public class RebootEscrowManagerTests {
verify(mRebootEscrow, never()).storeKey(any());
ArgumentCaptor keyByteCaptor = ArgumentCaptor.forClass(byte[].class);
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mRebootEscrow).storeKey(keyByteCaptor.capture());
assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
@@ -732,8 +746,7 @@ public class RebootEscrowManagerTests {
verify(mockListener).onPreparedForReboot(eq(true));
verify(mRebootEscrow, never()).storeKey(any());
-
- assertTrue(mService.armRebootEscrowIfNeeded());
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
verify(mRebootEscrow).storeKey(any());
assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
@@ -756,4 +769,28 @@ public class RebootEscrowManagerTests {
assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
metricsErrorCodeCaptor.getValue());
}
+
+ @Test
+ public void armServiceProviderMismatch_Failure() throws Exception {
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ verify(mRebootEscrow, never()).storeKey(any());
+
+ assertNull(
+ mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
+ // Change the provider to server based, expect the reboot to fail
+ when(mInjected.forceServerBased()).thenReturn(true);
+ assertEquals(ARM_REBOOT_ERROR_PROVIDER_MISMATCH, mService.armRebootEscrowIfNeeded());
+ assertNull(
+ mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
+ // Verify that the escrow key & data have been cleared.
+ verify(mRebootEscrow).storeKey(eq(new byte[32]));
+ assertFalse(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index 2b358ea5e1058..b64810b665421 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -18,11 +18,14 @@ package com.android.server.recoverysystem;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -31,6 +34,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -91,7 +95,8 @@ public class RecoverySystemServiceTest {
mUncryptUpdateFileWriter = mock(FileWriter.class);
mLockSettingsInternal = mock(LockSettingsInternal.class);
- when(mLockSettingsInternal.armRebootEscrow()).thenReturn(true);
+ doReturn(LockSettingsInternal.ARM_REBOOT_ERROR_NONE).when(mLockSettingsInternal)
+ .armRebootEscrow();
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
mIPowerManager = mock(IPowerManager.class);
@@ -489,4 +494,27 @@ public class RecoverySystemServiceTest {
}
// TODO(xunchang) add more multi client tests
+
+ @Test
+ public void rebootWithLskf_armEscrowDataFatalError_Failure() throws Exception {
+ doReturn(LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH)
+ .when(mLockSettingsInternal).armRebootEscrow();
+
+ assertTrue(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null));
+ mRecoverySystemService.onPreparedForReboot(true);
+ assertTrue(mRecoverySystemService.isLskfCaptured(FAKE_OTA_PACKAGE_NAME));
+
+ when(mSharedPreferences.getInt(eq(FAKE_OTA_PACKAGE_NAME
+ + RecoverySystemService.REQUEST_LSKF_COUNT_PREF_SUFFIX), anyInt())).thenReturn(1);
+ when(mSharedPreferences.getInt(eq(RecoverySystemService.LSKF_CAPTURED_COUNT_PREF),
+ anyInt())).thenReturn(1);
+ assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE,
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true));
+ // Verify that the RoR preparation state has been cleared.
+ assertFalse(mRecoverySystemService.isLskfCaptured(FAKE_OTA_PACKAGE_NAME));
+ verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(5004 /* provider mismatch */),
+ eq(1000), eq(1) /* client count */, eq(1) /* request count */,
+ eq(true) /* slot switch */, anyBoolean(), anyInt(),
+ eq(1) /* lskf capture count */);
+ }
}