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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -3488,7 +3488,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean armRebootEscrow() {
|
||||
public @ArmRebootEscrowErrorCode int armRebootEscrow() {
|
||||
return mRebootEscrowManager.armRebootEscrowIfNeeded();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user