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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user