Merge "Report RoR metrics in recovery system"

This commit is contained in:
Tianjie Xu
2021-03-19 06:00:46 +00:00
committed by Gerrit Code Review
3 changed files with 204 additions and 11 deletions

View File

@@ -16,6 +16,8 @@
package com.android.server.recoverysystem; package com.android.server.recoverysystem;
import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.content.Context; import android.content.Context;
import android.content.IntentSender; import android.content.IntentSender;
@@ -33,12 +35,14 @@ import android.os.RemoteException;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.os.ShellCallback; import android.os.ShellCallback;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockSettingsInternal; import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.RebootEscrowListener; import com.android.internal.widget.RebootEscrowListener;
import com.android.server.LocalServices; import com.android.server.LocalServices;
@@ -52,6 +56,8 @@ import java.io.FileDescriptor;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/** /**
* The recovery system service is responsible for coordinating recovery related * The recovery system service is responsible for coordinating recovery related
@@ -127,10 +133,28 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
/** /**
* The action to perform upon resume on reboot clear request for a given client. * The action to perform upon resume on reboot clear request for a given client.
*/ */
@IntDef({ROR_NOT_REQUESTED, @IntDef({ ROR_NOT_REQUESTED,
ROR_REQUESTED_NEED_CLEAR, ROR_REQUESTED_NEED_CLEAR,
ROR_REQUESTED_SKIP_CLEAR}) ROR_REQUESTED_SKIP_CLEAR})
private @interface ResumeOnRebootActionsOnClear{} private @interface ResumeOnRebootActionsOnClear {}
/**
* The error code for reboots initiated by resume on reboot clients.
*/
private static final int REBOOT_ERROR_NONE = 0;
private static final int REBOOT_ERROR_UNKNOWN = 1;
private static final int REBOOT_ERROR_INVALID_PACKAGE_NAME = 2;
private static final int REBOOT_ERROR_LSKF_NOT_CAPTURED = 3;
private static final int REBOOT_ERROR_SLOT_MISMATCH = 4;
private static final int REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE = 5;
@IntDef({ REBOOT_ERROR_NONE,
REBOOT_ERROR_UNKNOWN,
REBOOT_ERROR_INVALID_PACKAGE_NAME,
REBOOT_ERROR_LSKF_NOT_CAPTURED,
REBOOT_ERROR_SLOT_MISMATCH,
REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE})
private @interface ResumeOnRebootRebootErrorCode {}
static class Injector { static class Injector {
protected final Context mContext; protected final Context mContext;
@@ -202,6 +226,35 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
public void threadSleep(long millis) throws InterruptedException { public void threadSleep(long millis) throws InterruptedException {
Thread.sleep(millis); Thread.sleep(millis);
} }
public int getUidFromPackageName(String packageName) {
try {
return mContext.getPackageManager().getPackageUidAsUser(packageName, USER_SYSTEM);
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Failed to find uid for " + packageName);
}
return -1;
}
public void reportRebootEscrowPreparationMetrics(int uid,
@ResumeOnRebootActionsOnRequest int requestResult, int requestedClientCount) {
FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_PREPARATION_REPORTED, uid,
requestResult, requestedClientCount);
}
public void reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount,
int requestedToLskfCapturedDurationInSeconds) {
FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_LSKF_CAPTURE_REPORTED, uid,
requestedClientCount, requestedToLskfCapturedDurationInSeconds);
}
public void reportRebootEscrowRebootMetrics(int errorCode, int uid,
int preparedClientCount, int requestCount, boolean slotSwitch, boolean serverBased,
int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts) {
FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_REBOOT_REPORTED, errorCode,
uid, preparedClientCount, requestCount, slotSwitch, serverBased,
lskfCapturedToRebootDurationInSeconds, lskfCapturedCounts);
}
} }
/** /**
@@ -367,6 +420,16 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
} }
} }
private void reportMetricsOnRequestLskf(String packageName, int requestResult) {
int uid = mInjector.getUidFromPackageName(packageName);
int pendingRequestCount;
synchronized (this) {
pendingRequestCount = mCallerPendingRequest.size();
}
mInjector.reportRebootEscrowPreparationMetrics(uid, requestResult, pendingRequestCount);
}
@Override // Binder call @Override // Binder call
public boolean requestLskf(String packageName, IntentSender intentSender) { public boolean requestLskf(String packageName, IntentSender intentSender) {
enforcePermissionForResumeOnReboot(); enforcePermissionForResumeOnReboot();
@@ -378,6 +441,8 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
@ResumeOnRebootActionsOnRequest int action = updateRoRPreparationStateOnNewRequest( @ResumeOnRebootActionsOnRequest int action = updateRoRPreparationStateOnNewRequest(
packageName, intentSender); packageName, intentSender);
reportMetricsOnRequestLskf(packageName, action);
switch (action) { switch (action) {
case ROR_SKIP_PREPARATION_AND_NOTIFY: case ROR_SKIP_PREPARATION_AND_NOTIFY:
// We consider the preparation done if someone else has prepared. // We consider the preparation done if someone else has prepared.
@@ -420,12 +485,26 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return needPreparation ? ROR_NEED_PREPARATION : ROR_SKIP_PREPARATION_NOT_NOTIFY; return needPreparation ? ROR_NEED_PREPARATION : ROR_SKIP_PREPARATION_NOT_NOTIFY;
} }
private void reportMetricsOnPreparedForReboot() {
List<String> preparedClients;
synchronized (this) {
preparedClients = new ArrayList<>(mCallerPreparedForReboot);
}
for (String packageName : preparedClients) {
int uid = mInjector.getUidFromPackageName(packageName);
mInjector.reportRebootEscrowLskfCapturedMetrics(uid, preparedClients.size(),
-1 /* duration */);
}
}
@Override @Override
public void onPreparedForReboot(boolean ready) { public void onPreparedForReboot(boolean ready) {
if (!ready) { if (!ready) {
return; return;
} }
updateRoRPreparationStateOnPreparedForReboot(); updateRoRPreparationStateOnPreparedForReboot();
reportMetricsOnPreparedForReboot();
} }
private synchronized void updateRoRPreparationStateOnPreparedForReboot() { private synchronized void updateRoRPreparationStateOnPreparedForReboot() {
@@ -548,22 +627,49 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return true; return true;
} }
private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) { private @ResumeOnRebootRebootErrorCode int armRebootEscrow(String packageName,
boolean slotSwitch) {
if (packageName == null) { if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf."); Slog.w(TAG, "Missing packageName when rebooting with lskf.");
return false; return REBOOT_ERROR_INVALID_PACKAGE_NAME;
} }
if (!isLskfCaptured(packageName)) { if (!isLskfCaptured(packageName)) {
return false; return REBOOT_ERROR_LSKF_NOT_CAPTURED;
} }
if (!verifySlotForNextBoot(slotSwitch)) { if (!verifySlotForNextBoot(slotSwitch)) {
return false; return REBOOT_ERROR_SLOT_MISMATCH;
} }
// TODO(xunchang) write the vbmeta digest along with the escrowKey before reboot.
if (!mInjector.getLockSettingsService().armRebootEscrow()) { if (!mInjector.getLockSettingsService().armRebootEscrow()) {
Slog.w(TAG, "Failure to escrow key for reboot"); Slog.w(TAG, "Failure to escrow key for reboot");
return REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE;
}
return REBOOT_ERROR_NONE;
}
private void reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch,
@ResumeOnRebootRebootErrorCode int errorCode) {
int uid = mInjector.getUidFromPackageName(packageName);
boolean serverBased = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false);
int preparedClientCount;
synchronized (this) {
preparedClientCount = mCallerPreparedForReboot.size();
}
// TODO(b/179105110) report the true value of duration and counts
mInjector.reportRebootEscrowRebootMetrics(errorCode, uid, preparedClientCount,
1 /* request count */, slotSwitch, serverBased,
-1 /* duration */, 1 /* lskf capture count */);
}
private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
@ResumeOnRebootRebootErrorCode int errorCode = armRebootEscrow(packageName, slotSwitch);
reportMetricsOnRebootWithLskf(packageName, slotSwitch, errorCode);
if (errorCode != REBOOT_ERROR_NONE) {
return false; return false;
} }

View File

@@ -18,6 +18,7 @@ package com.android.server.recoverysystem;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
@@ -70,6 +71,7 @@ public class RecoverySystemServiceTest {
private FileWriter mUncryptUpdateFileWriter; private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal; private LockSettingsInternal mLockSettingsInternal;
private IBootControl mIBootControl; private IBootControl mIBootControl;
private RecoverySystemServiceTestable.IMetricsReporter mMetricsReporter;
private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package"; private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package"; private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
@@ -94,9 +96,11 @@ public class RecoverySystemServiceTest {
when(mIBootControl.getCurrentSlot()).thenReturn(0); when(mIBootControl.getCurrentSlot()).thenReturn(0);
when(mIBootControl.getActiveBootSlot()).thenReturn(1); when(mIBootControl.getActiveBootSlot()).thenReturn(1);
mMetricsReporter = mock(RecoverySystemServiceTestable.IMetricsReporter.class);
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties, mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal, powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal,
mIBootControl); mIBootControl, mMetricsReporter);
} }
@Test @Test
@@ -226,6 +230,16 @@ public class RecoverySystemServiceTest {
mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null); mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null);
} }
@Test
public void requestLskf_reportMetrics() throws Exception {
IntentSender intentSender = mock(IntentSender.class);
assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
is(true));
verify(mMetricsReporter).reportRebootEscrowPreparationMetrics(
eq(1000), eq(0) /* need preparation */, eq(1) /* client count */);
}
@Test @Test
public void requestLskf_success() throws Exception { public void requestLskf_success() throws Exception {
IntentSender intentSender = mock(IntentSender.class); IntentSender intentSender = mock(IntentSender.class);
@@ -233,6 +247,8 @@ public class RecoverySystemServiceTest {
is(true)); is(true));
mRecoverySystemService.onPreparedForReboot(true); mRecoverySystemService.onPreparedForReboot(true);
verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any()); verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
verify(mMetricsReporter).reportRebootEscrowLskfCapturedMetrics(
eq(1000), eq(1) /* client count */, anyInt() /* duration */);
} }
@Test @Test
@@ -255,6 +271,8 @@ public class RecoverySystemServiceTest {
assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender), assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
is(true)); is(true));
verify(intentSender, never()).sendIntent(any(), anyInt(), any(), any(), any()); verify(intentSender, never()).sendIntent(any(), anyInt(), any(), any(), any());
verify(mMetricsReporter, never()).reportRebootEscrowLskfCapturedMetrics(
anyInt(), anyInt(), anyInt());
} }
@Test @Test
@@ -337,6 +355,9 @@ public class RecoverySystemServiceTest {
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true), assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
is(true)); is(true));
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean()); verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
eq(1) /* client count */, eq(1) /* request count */, eq(true) /* slot switch */,
anyBoolean(), anyInt(), eq(1) /* lskf capture count */);
} }
@@ -373,6 +394,20 @@ public class RecoverySystemServiceTest {
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean()); verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
} }
@Test
public void rebootWithLskf_multiClient_success_reportMetrics() throws Exception {
assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
mRecoverySystemService.onPreparedForReboot(true);
// Client B's clear won't affect client A's preparation.
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
is(true));
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
eq(2) /* client count */, eq(1) /* request count */, eq(true) /* slot switch */,
anyBoolean(), anyInt(), eq(1) /* lskf capture count */);
}
@Test @Test
public void rebootWithLskf_multiClient_ClientBSuccess() throws Exception { public void rebootWithLskf_multiClient_ClientBSuccess() throws Exception {
@@ -384,12 +419,18 @@ public class RecoverySystemServiceTest {
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true), assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
is(false)); is(false));
verifyNoMoreInteractions(mIPowerManager); verifyNoMoreInteractions(mIPowerManager);
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(not(eq(0)), eq(1000),
eq(1) /* client count */, eq(1) /* request count */, eq(true) /* slot switch */,
anyBoolean(), anyInt(), eq(1) /* lskf capture count */);
assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true)); assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
assertThat( assertThat(
mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true), mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true),
is(true)); is(true));
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean()); verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(2000),
eq(1) /* client count */, eq(1) /* request count */, eq(true) /* slot switch */,
anyBoolean(), anyInt(), eq(1) /* lskf capture count */);
} }
@Test @Test

View File

@@ -32,11 +32,12 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
private final UncryptSocket mUncryptSocket; private final UncryptSocket mUncryptSocket;
private final LockSettingsInternal mLockSettingsInternal; private final LockSettingsInternal mLockSettingsInternal;
private final IBootControl mIBootControl; private final IBootControl mIBootControl;
private final IMetricsReporter mIMetricsReporter;
MockInjector(Context context, FakeSystemProperties systemProperties, MockInjector(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter, PowerManager powerManager, FileWriter uncryptPackageFileWriter,
UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal, UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
IBootControl bootControl) { IBootControl bootControl, IMetricsReporter metricsReporter) {
super(context); super(context);
mSystemProperties = systemProperties; mSystemProperties = systemProperties;
mPowerManager = powerManager; mPowerManager = powerManager;
@@ -44,6 +45,7 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
mUncryptSocket = uncryptSocket; mUncryptSocket = uncryptSocket;
mLockSettingsInternal = lockSettingsInternal; mLockSettingsInternal = lockSettingsInternal;
mIBootControl = bootControl; mIBootControl = bootControl;
mIMetricsReporter = metricsReporter;
} }
@Override @Override
@@ -94,14 +96,45 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
public IBootControl getBootControl() { public IBootControl getBootControl() {
return mIBootControl; return mIBootControl;
} }
@Override
public int getUidFromPackageName(String packageName) {
if ("fake.ota.package".equals(packageName)) {
return 1000;
}
if ("fake.other.package".equals(packageName)) {
return 2000;
}
return 3000;
}
@Override
public void reportRebootEscrowPreparationMetrics(int uid, int requestResult,
int requestedClientCount) {
mIMetricsReporter.reportRebootEscrowPreparationMetrics(uid, requestResult,
requestedClientCount);
}
public void reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount,
int requestedToLskfCapturedDurationInSeconds) {
mIMetricsReporter.reportRebootEscrowLskfCapturedMetrics(uid, requestedClientCount,
requestedToLskfCapturedDurationInSeconds);
}
public void reportRebootEscrowRebootMetrics(int errorCode, int uid, int preparedClientCount,
int requestCount, boolean slotSwitch, boolean serverBased,
int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts) {
mIMetricsReporter.reportRebootEscrowRebootMetrics(errorCode, uid, preparedClientCount,
requestCount, slotSwitch, serverBased, lskfCapturedToRebootDurationInSeconds,
lskfCapturedCounts);
}
} }
RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties, RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter, PowerManager powerManager, FileWriter uncryptPackageFileWriter,
UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal, UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
IBootControl bootControl) { IBootControl bootControl, IMetricsReporter metricsReporter) {
super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter, super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
uncryptSocket, lockSettingsInternal, bootControl)); uncryptSocket, lockSettingsInternal, bootControl, metricsReporter));
} }
public static class FakeSystemProperties { public static class FakeSystemProperties {
@@ -131,4 +164,17 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
return mCtlStart; return mCtlStart;
} }
} }
public interface IMetricsReporter {
void reportRebootEscrowPreparationMetrics(int uid, int requestResult,
int requestedClientCount);
void reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount,
int requestedToLskfCapturedDurationInSeconds);
void reportRebootEscrowRebootMetrics(int errorCode, int uid, int preparedClientCount,
int requestCount, boolean slotSwitch, boolean serverBased,
int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts);
}
} }