diff options
author | Tianjie <xunchang@google.com> | 2021-02-17 21:06:47 -0800 |
---|---|---|
committer | Tianjie <xunchang@google.com> | 2021-03-22 15:57:32 -0700 |
commit | aec4f406dfe00f1c456db4fc3bba55628fda0b26 (patch) | |
tree | 0458e401be7b446de2205b2f1642630b1d227a49 /services | |
parent | b079fe666f2f8a222acad3952856a0fda6983247 (diff) |
Report the true value of more RoR metrics
Design ErrorCode for errors of loadRebootEscrowData. Also report
the status of vbmeta digest, as we plan to associate k_k with
vbmeta in the future.
Bug: 179105110
Test: atest FrameworksServicesTests:RebootEscrowManagerTests
Change-Id: I07168a7bc4fd20d41df9b87136e1e5158129d7f6
Diffstat (limited to 'services')
-rw-r--r-- | services/core/java/com/android/server/locksettings/RebootEscrowManager.java | 127 | ||||
-rw-r--r-- | services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java | 29 |
2 files changed, 142 insertions, 14 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..62156a89392e 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; @@ -67,6 +72,21 @@ class RebootEscrowManager { static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp"; /** + * 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. * <p> * If the delta between the current boot number and the boot number stored when the mechanism @@ -86,6 +106,27 @@ 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, + }) + @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; + + private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; + /** * Logs events for later debugging in bugreports. */ @@ -199,6 +240,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 +266,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 +311,7 @@ class RebootEscrowManager { if (rebootEscrowUsers.isEmpty()) { Slog.i(TAG, "No reboot escrow data found for users," + " skipping loading escrow data"); + clearMetricsStorage(); return; } @@ -284,6 +335,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 +359,9 @@ class RebootEscrowManager { } if (escrowKey == null) { + if (mLoadEscrowDataErrorCode == ERROR_NONE) { + mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY; + } onGetRebootEscrowKeyFailed(users, attemptNumber + 1); return; } @@ -321,9 +376,48 @@ 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); + } + + 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 +425,31 @@ 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; - - // 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 escrowDurationInSeconds = -1; + long currentTimeStamp = mInjector.getCurrentTimeMillis(); + if (armedTimestamp != -1 && currentTimeStamp > armedTimestamp) { + escrowDurationInSeconds = (int) (currentTimeStamp - armedTimestamp) / 1000; + } - mInjector.reportMetric(success, 0 /* error code */, serviceType, attemptCount, + int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete(); + if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) { + mLoadEscrowDataErrorCode = ERROR_UNKNOWN; + } + // 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 +457,7 @@ class RebootEscrowManager { if (rebootEscrowProvider == null) { Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); + mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER; return null; } @@ -463,7 +563,7 @@ class RebootEscrowManager { return; } - mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); + clearMetricsStorage(); rebootEscrowProvider.clearRebootEscrowKey(); List<UserInfo> users = mUserManager.getUsers(); @@ -505,8 +605,13 @@ 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); mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); } 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..502b5e4c6633 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,8 @@ public class RebootEscrowManagerTests { public interface MockableRebootEscrowInjected { int getBootCount(); + long getCurrentTimeMillis(); + void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete); } @@ -205,6 +208,16 @@ 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) { @@ -430,16 +443,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 +486,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)); @@ -607,9 +625,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 |