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
This commit is contained in:
Tianjie
2021-02-17 21:06:47 -08:00
parent b079fe666f
commit aec4f406df
2 changed files with 142 additions and 14 deletions

View File

@@ -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;
@@ -66,6 +71,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>
@@ -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;
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;
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);
}

View File

@@ -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);
}
@@ -204,6 +207,16 @@ public class RebootEscrowManagerTests {
return 1;
}
@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,
@@ -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