diff options
Diffstat (limited to 'services')
5 files changed, 288 insertions, 16 deletions
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 6e99cba6ea91..76ecc1acc7ac 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -15,14 +15,17 @@ */ package com.android.server.locksettings; + import static android.os.UserHandle.USER_SYSTEM; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.UserInfo; import android.os.Handler; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; @@ -35,6 +38,8 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.widget.RebootEscrowListener; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -65,6 +70,22 @@ class RebootEscrowManager { public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count"; static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp"; + static final String REBOOT_ESCROW_KEY_PROVIDER = "reboot_escrow_key_provider"; + + /** + * The verified boot 2.0 vbmeta digest of the current slot, the property value is always + * available after boot. + */ + static final String VBMETA_DIGEST_PROP_NAME = "ro.boot.vbmeta.digest"; + /** + * The system prop contains vbmeta digest of the inactive slot. The build property is set after + * an OTA update. RebootEscrowManager will store it in disk before the OTA reboot, so the value + * is available for vbmeta digest verification after the device reboots. + */ + static final String OTHER_VBMETA_DIGEST_PROP_NAME = "ota.other.vbmeta_digest"; + static final String REBOOT_ESCROW_KEY_VBMETA_DIGEST = "reboot_escrow_key_vbmeta_digest"; + static final String REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST = + "reboot_escrow_key_other_vbmeta_digest"; /** * Number of boots until we consider the escrow data to be stale for the purposes of metrics. @@ -86,6 +107,31 @@ class RebootEscrowManager { private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3; private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30; + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_NONE, + ERROR_UNKNOWN, + ERROR_NO_PROVIDER, + ERROR_LOAD_ESCROW_KEY, + ERROR_RETRY_COUNT_EXHAUSTED, + ERROR_UNLOCK_ALL_USERS, + ERROR_PROVIDER_MISMATCH, + ERROR_KEYSTORE_FAILURE, + }) + @Retention(RetentionPolicy.SOURCE) + @interface RebootEscrowErrorCode { + } + + static final int ERROR_NONE = 0; + static final int ERROR_UNKNOWN = 1; + static final int ERROR_NO_PROVIDER = 2; + static final int ERROR_LOAD_ESCROW_KEY = 3; + static final int ERROR_RETRY_COUNT_EXHAUSTED = 4; + static final int ERROR_UNLOCK_ALL_USERS = 5; + static final int ERROR_PROVIDER_MISMATCH = 6; + static final int ERROR_KEYSTORE_FAILURE = 7; + + private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; + /** * Logs events for later debugging in bugreports. */ @@ -199,6 +245,10 @@ class RebootEscrowManager { 0); } + public long getCurrentTimeMillis() { + return System.currentTimeMillis(); + } + public int getLoadEscrowDataRetryLimit() { return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA, "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT); @@ -221,6 +271,11 @@ class RebootEscrowManager { public RebootEscrowEventLog getEventLog() { return new RebootEscrowEventLog(); } + + public String getVbmetaDigest(boolean other) { + return other ? SystemProperties.get(OTHER_VBMETA_DIGEST_PROP_NAME) + : SystemProperties.get(VBMETA_DIGEST_PROP_NAME); + } } RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) { @@ -261,6 +316,7 @@ class RebootEscrowManager { if (rebootEscrowUsers.isEmpty()) { Slog.i(TAG, "No reboot escrow data found for users," + " skipping loading escrow data"); + clearMetricsStorage(); return; } @@ -284,6 +340,7 @@ class RebootEscrowManager { } Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); + mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; onGetRebootEscrowKeyFailed(users, attemptNumber); } @@ -307,6 +364,17 @@ class RebootEscrowManager { } if (escrowKey == null) { + if (mLoadEscrowDataErrorCode == ERROR_NONE) { + // Specifically check if the RoR provider has changed after reboot. + int providerType = mInjector.serverBasedResumeOnReboot() + ? RebootEscrowProviderInterface.TYPE_SERVER_BASED + : RebootEscrowProviderInterface.TYPE_HAL; + if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) { + mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH; + } else { + mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY; + } + } onGetRebootEscrowKeyFailed(users, attemptNumber + 1); return; } @@ -321,9 +389,49 @@ class RebootEscrowManager { // Clear the old key in keystore. A new key will be generated by new RoR requests. mKeyStoreManager.clearKeyStoreEncryptionKey(); + if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) { + mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS; + } onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1); } + private void clearMetricsStorage() { + mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_VBMETA_DIGEST, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_PROVIDER, USER_SYSTEM); + } + + private int getVbmetaDigestStatusOnRestoreComplete() { + String currentVbmetaDigest = mInjector.getVbmetaDigest(false); + String vbmetaDigestStored = mStorage.getString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, + "", USER_SYSTEM); + String vbmetaDigestOtherStored = mStorage.getString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, + "", USER_SYSTEM); + + // The other vbmeta digest is never set, assume no slot switch is attempted. + if (vbmetaDigestOtherStored.isEmpty()) { + if (currentVbmetaDigest.equals(vbmetaDigestStored)) { + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; + } + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; + } + + // The other vbmeta digest is set, we expect to boot into the new slot. + if (currentVbmetaDigest.equals(vbmetaDigestOtherStored)) { + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; + } else if (currentVbmetaDigest.equals(vbmetaDigestStored)) { + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_FALLBACK_SLOT; + } + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; + } + private void reportMetricOnRestoreComplete(boolean success, int attemptCount) { int serviceType = mInjector.serverBasedResumeOnReboot() ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED @@ -331,26 +439,32 @@ class RebootEscrowManager { long armedTimestamp = mStorage.getLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, -1, USER_SYSTEM); - mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM); - int escrowDurationInSeconds = armedTimestamp != -1 - ? (int) (System.currentTimeMillis() - armedTimestamp) / 1000 : -1; + int escrowDurationInSeconds = -1; + long currentTimeStamp = mInjector.getCurrentTimeMillis(); + if (armedTimestamp != -1 && currentTimeStamp > armedTimestamp) { + escrowDurationInSeconds = (int) (currentTimeStamp - armedTimestamp) / 1000; + } - // TODO(b/179105110) design error code; and report the true value for other fields. - int vbmetaDigestStatus = FrameworkStatsLog - .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; + int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete(); + if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) { + mLoadEscrowDataErrorCode = ERROR_UNKNOWN; + } - mInjector.reportMetric(success, 0 /* error code */, serviceType, attemptCount, + // TODO(179105110) report the duration since boot complete. + mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, -1); + + mLoadEscrowDataErrorCode = ERROR_NONE; } private void onEscrowRestoreComplete(boolean success, int attemptCount) { int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM); - mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); int bootCountDelta = mInjector.getBootCount() - previousBootCount; if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) { reportMetricOnRestoreComplete(success, attemptCount); } + clearMetricsStorage(); } private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException { @@ -358,6 +472,14 @@ class RebootEscrowManager { if (rebootEscrowProvider == null) { Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); + mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER; + return null; + } + + // Server based RoR always need the decryption key from keystore. + if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED + && kk == null) { + mLoadEscrowDataErrorCode = ERROR_KEYSTORE_FAILURE; return null; } @@ -463,7 +585,7 @@ class RebootEscrowManager { return; } - mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); + clearMetricsStorage(); rebootEscrowProvider.clearRebootEscrowKey(); List<UserInfo> users = mUserManager.getUsers(); @@ -486,6 +608,9 @@ class RebootEscrowManager { return false; } + int actualProviderType = rebootEscrowProvider.getType(); + // TODO(b/183140900) Fail the reboot if provider type mismatches. + RebootEscrowKey escrowKey; synchronized (mKeyGenerationLock) { escrowKey = mPendingRebootEscrowKey; @@ -505,8 +630,14 @@ class RebootEscrowManager { 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, System.currentTimeMillis(), + 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); } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java index 4b00772088f2..e8f6f4abd030 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java @@ -60,6 +60,11 @@ class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface { } @Override + public int getType() { + return TYPE_HAL; + } + + @Override public boolean hasRebootEscrowSupport() { return mInjector.getRebootEscrow() != null; } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java index af6faad3c76e..e106d817c533 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java @@ -16,7 +16,11 @@ package com.android.server.locksettings; +import android.annotation.IntDef; + import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import javax.crypto.SecretKey; @@ -28,6 +32,21 @@ import javax.crypto.SecretKey; * @hide */ public interface RebootEscrowProviderInterface { + @IntDef(prefix = {"TYPE_"}, value = { + TYPE_HAL, + TYPE_SERVER_BASED, + }) + @Retention(RetentionPolicy.SOURCE) + @interface RebootEscrowProviderType { + } + int TYPE_HAL = 0; + int TYPE_SERVER_BASED = 1; + + /** + * Returns the reboot escrow provider type. + */ + @RebootEscrowProviderType int getType(); + /** * Returns true if the secure store/discard of reboot escrow key is supported. */ diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java index 697bf08a232e..28669875f1cd 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java @@ -95,6 +95,11 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa } @Override + public int getType() { + return TYPE_SERVER_BASED; + } + + @Override public boolean hasRebootEscrowSupport() { return mInjector.getServiceConnection() != null; } 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 91342ce925f6..8c08226201a8 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,7 @@ 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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -110,6 +111,10 @@ public class RebootEscrowManagerTests { public interface MockableRebootEscrowInjected { int getBootCount(); + long getCurrentTimeMillis(); + + boolean forceServerBased(); + void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete); } @@ -174,6 +179,9 @@ public class RebootEscrowManagerTests { @Override public boolean serverBasedResumeOnReboot() { + if (mInjected.forceServerBased()) { + return true; + } return mServerBased; } @@ -205,9 +213,20 @@ public class RebootEscrowManagerTests { } @Override + public String getVbmetaDigest(boolean other) { + return other ? "" : "fake digest"; + } + + @Override + public long getCurrentTimeMillis() { + return mInjected.getCurrentTimeMillis(); + } + + @Override public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete) { + mInjected.reportMetric(success, errorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete); } @@ -430,16 +449,21 @@ public class RebootEscrowManagerTests { // pretend reboot happens here when(mInjected.getBootCount()).thenReturn(1); + when(mInjected.getCurrentTimeMillis()).thenReturn(30000L); + mStorage.setLong(RebootEscrowManager.REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, 10000L, + USER_SYSTEM); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), eq(0) /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */, - anyInt(), anyInt(), anyInt()); + eq(20), eq(0) /* vbmeta status */, anyInt()); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue()); mService.loadRebootEscrowDataIfAvailable(null); verify(mRebootEscrow).retrieveKey(); assertTrue(metricsSuccessCaptor.getValue()); verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertEquals(mStorage.getLong(RebootEscrowManager.REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, + -1, USER_SYSTEM), -1); } @Test @@ -468,7 +492,7 @@ public class RebootEscrowManagerTests { ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */, - anyInt(), anyInt(), anyInt()); + anyInt(), eq(0) /* vbmeta status */, anyInt()); when(mServiceConnection.unwrap(any(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); @@ -479,6 +503,84 @@ public class RebootEscrowManagerTests { } @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedRemoteException_Failure() + throws Exception { + setServerBasedRebootEscrowProvider(); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertTrue(mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, + eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class); + mService.loadRebootEscrowDataIfAvailable(null); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); + } + + @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() throws Exception { + setServerBasedRebootEscrowProvider(); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertTrue(mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, + eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); + + HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); + thread.start(); + mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper())); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED), + metricsErrorCodeCaptor.getValue()); + } + + @Test public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception { setServerBasedRebootEscrowProvider(); @@ -607,9 +709,14 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(10); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue()); + // Trigger a vbmeta digest mismatch + mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, + "non sense value", USER_SYSTEM); mService.loadRebootEscrowDataIfAvailable(null); verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */, - eq(1) /* attempt count */, anyInt(), anyInt(), anyInt()); + eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt()); + assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, + "", USER_SYSTEM), ""); } @Test @@ -636,12 +743,17 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + // Return a null escrow key doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - anyInt() /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */, - anyInt(), anyInt(), anyInt()); - when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]); + metricsErrorCodeCaptor.capture(), eq(1) /* HAL based */, + eq(1) /* attempt count */, anyInt(), anyInt(), anyInt()); + + when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null); mService.loadRebootEscrowDataIfAvailable(null); verify(mRebootEscrow).retrieveKey(); assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); } } |