Merge "Improve the error handling for armRebootEscrow" am: 246c14d6da am: cd4836d197

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1649651

Change-Id: I6aabf1250d67f6a79711e89fb05c68bf84849656
This commit is contained in:
Tianjie Xu
2021-04-14 06:32:31 +00:00
committed by Automerger Merge Worker
8 changed files with 244 additions and 68 deletions

View File

@@ -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.
*
* <p> 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;

View File

@@ -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();
/**

View File

@@ -3488,7 +3488,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
public boolean armRebootEscrow() {
public @ArmRebootEscrowErrorCode int armRebootEscrow() {
return mRebootEscrowManager.armRebootEscrowIfNeeded();
}

View File

@@ -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<UserInfo> 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;
}

View File

@@ -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<Integer> 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;
}

View File

@@ -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(

View File

@@ -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<byte[]> 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<byte[]> 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));
}
}

View File

@@ -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 */);
}
}