diff options
author | Tianjie Xu <xunchang@google.com> | 2021-03-19 06:00:46 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-03-19 06:00:46 +0000 |
commit | b079fe666f2f8a222acad3952856a0fda6983247 (patch) | |
tree | 1ef27fbf3654b7cabb51c83e1f0d18cc4669126a | |
parent | eed88a705d77b5f5a2636eb03fe2ee0f15330605 (diff) | |
parent | 9a8538c8195189ea0ffbe742f7f5f02ee5688e40 (diff) |
Merge "Report RoR metrics in recovery system"
3 files changed, 204 insertions, 11 deletions
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index fd2d8e1b834b..beebb3145018 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -16,6 +16,8 @@ package com.android.server.recoverysystem; +import static android.os.UserHandle.USER_SYSTEM; + import android.annotation.IntDef; import android.content.Context; import android.content.IntentSender; @@ -33,12 +35,14 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.SystemProperties; +import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockSettingsInternal; import com.android.internal.widget.RebootEscrowListener; import com.android.server.LocalServices; @@ -52,6 +56,8 @@ import java.io.FileDescriptor; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; /** * 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. */ - @IntDef({ROR_NOT_REQUESTED, + @IntDef({ ROR_NOT_REQUESTED, ROR_REQUESTED_NEED_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 { protected final Context mContext; @@ -202,6 +226,35 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo public void threadSleep(long millis) throws InterruptedException { 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 public boolean requestLskf(String packageName, IntentSender intentSender) { enforcePermissionForResumeOnReboot(); @@ -378,6 +441,8 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo @ResumeOnRebootActionsOnRequest int action = updateRoRPreparationStateOnNewRequest( packageName, intentSender); + reportMetricsOnRequestLskf(packageName, action); + switch (action) { case ROR_SKIP_PREPARATION_AND_NOTIFY: // 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; } + 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 public void onPreparedForReboot(boolean ready) { if (!ready) { return; } updateRoRPreparationStateOnPreparedForReboot(); + reportMetricsOnPreparedForReboot(); } private synchronized void updateRoRPreparationStateOnPreparedForReboot() { @@ -548,22 +627,49 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return true; } - private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) { + private @ResumeOnRebootRebootErrorCode int armRebootEscrow(String packageName, + boolean slotSwitch) { if (packageName == null) { Slog.w(TAG, "Missing packageName when rebooting with lskf."); - return false; + return REBOOT_ERROR_INVALID_PACKAGE_NAME; } if (!isLskfCaptured(packageName)) { - return false; + return REBOOT_ERROR_LSKF_NOT_CAPTURED; } 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()) { 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; } diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java index 9b8a2a82c6df..324e5929f77f 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.recoverysystem; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -70,6 +71,7 @@ public class RecoverySystemServiceTest { private FileWriter mUncryptUpdateFileWriter; private LockSettingsInternal mLockSettingsInternal; private IBootControl mIBootControl; + private RecoverySystemServiceTestable.IMetricsReporter mMetricsReporter; private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.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.getActiveBootSlot()).thenReturn(1); + mMetricsReporter = mock(RecoverySystemServiceTestable.IMetricsReporter.class); + mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties, powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal, - mIBootControl); + mIBootControl, mMetricsReporter); } @Test @@ -227,12 +231,24 @@ public class RecoverySystemServiceTest { } @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 public void requestLskf_success() throws Exception { IntentSender intentSender = mock(IntentSender.class); assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender), is(true)); mRecoverySystemService.onPreparedForReboot(true); verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any()); + verify(mMetricsReporter).reportRebootEscrowLskfCapturedMetrics( + eq(1000), eq(1) /* client count */, anyInt() /* duration */); } @Test @@ -255,6 +271,8 @@ public class RecoverySystemServiceTest { assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender), is(true)); verify(intentSender, never()).sendIntent(any(), anyInt(), any(), any(), any()); + verify(mMetricsReporter, never()).reportRebootEscrowLskfCapturedMetrics( + anyInt(), anyInt(), anyInt()); } @Test @@ -337,6 +355,9 @@ public class RecoverySystemServiceTest { 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(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()); } + @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 public void rebootWithLskf_multiClient_ClientBSuccess() throws Exception { @@ -384,12 +419,18 @@ public class RecoverySystemServiceTest { assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true), is(false)); 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.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true), is(true)); 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 diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java index 0727e5adb9ca..a894178fca06 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java @@ -32,11 +32,12 @@ public class RecoverySystemServiceTestable extends RecoverySystemService { private final UncryptSocket mUncryptSocket; private final LockSettingsInternal mLockSettingsInternal; private final IBootControl mIBootControl; + private final IMetricsReporter mIMetricsReporter; MockInjector(Context context, FakeSystemProperties systemProperties, PowerManager powerManager, FileWriter uncryptPackageFileWriter, UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal, - IBootControl bootControl) { + IBootControl bootControl, IMetricsReporter metricsReporter) { super(context); mSystemProperties = systemProperties; mPowerManager = powerManager; @@ -44,6 +45,7 @@ public class RecoverySystemServiceTestable extends RecoverySystemService { mUncryptSocket = uncryptSocket; mLockSettingsInternal = lockSettingsInternal; mIBootControl = bootControl; + mIMetricsReporter = metricsReporter; } @Override @@ -94,14 +96,45 @@ public class RecoverySystemServiceTestable extends RecoverySystemService { public IBootControl getBootControl() { 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, PowerManager powerManager, FileWriter uncryptPackageFileWriter, UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal, - IBootControl bootControl) { + IBootControl bootControl, IMetricsReporter metricsReporter) { super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter, - uncryptSocket, lockSettingsInternal, bootControl)); + uncryptSocket, lockSettingsInternal, bootControl, metricsReporter)); } public static class FakeSystemProperties { @@ -131,4 +164,17 @@ public class RecoverySystemServiceTestable extends RecoverySystemService { 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); + } + } |