Merge "Expose error code for RoR reboot system API"

This commit is contained in:
Tianjie Xu
2021-04-09 22:07:29 +00:00
committed by Gerrit Code Review
6 changed files with 126 additions and 67 deletions

View File

@@ -7183,10 +7183,15 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static int rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000; // 0x7d0
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000; // 0xbb8
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000; // 0x1388
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH = 4000; // 0xfa0
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED = 1000; // 0x3e8
}
public final class RemoteCallback implements android.os.Parcelable {

View File

@@ -30,6 +30,6 @@ interface IRecoverySystem {
boolean requestLskf(in String packageName, in IntentSender sender);
boolean clearLskf(in String packageName);
boolean isLskfCaptured(in String packageName);
boolean rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
boolean rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
int rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
int rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
}

View File

@@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static java.nio.charset.StandardCharsets.UTF_8;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -154,6 +155,65 @@ public class RecoverySystem {
private final IRecoverySystem mService;
/**
* The error codes for reboots initiated by resume on reboot clients.
* @hide
*/
@IntDef(prefix = { "RESUME_ON_REBOOT_REBOOT_ERROR_" }, value = {
RESUME_ON_REBOOT_REBOOT_ERROR_NONE,
RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED,
RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME,
RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE})
public @interface ResumeOnRebootRebootErrorCode {}
/**
* The preparation of resume on reboot succeeds. Don't expose it because a successful reboot
* should just reboot the device.
* @hide
*/
public static final int RESUME_ON_REBOOT_REBOOT_ERROR_NONE = 0;
/**
* The resume on reboot fails due to an unknown reason.
* @hide
*/
@SystemApi
public static final int RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED = 1000;
/**
* The resume on reboot fails because the package name of the client is invalid, e.g. null
* packageName, name contains invalid characters, etc.
* @hide
*/
@SystemApi
public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000;
/**
* The resume on reboot fails because the Lock Screen Knowledge Factor hasn't been captured.
* This error is also reported if the client attempts to reboot without preparing RoR.
* @hide
*/
@SystemApi
public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000;
/**
* The resume on reboot fails because the client expects a different boot slot for the next boot
* on A/B devices.
* @hide
*/
@SystemApi
public static final int RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH = 4000;
/**
* The resume on reboot fails because the resume on reboot provider, e.g. HAL / server based,
* fails to arm/store the escrow key.
* @hide
*/
@SystemApi
public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000;
/**
* Interface definition for a callback to be invoked regularly as
* verification proceeds.
@@ -723,7 +783,8 @@ public class RecoverySystem {
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
// OTA is the sole user, who expects a slot switch.
if (!rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)) {
if (rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)
!= RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
throw new IOException("system not prepared to apply update");
}
}
@@ -752,19 +813,19 @@ public class RecoverySystem {
* @param context the Context to use.
* @param reason the reboot reason to give to the {@link PowerManager}
* @param slotSwitch true if the caller expects the slot to be switched on A/B devices.
* @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
* unattended reboot.
*
* @return 0 on success, and a non-zero error code if the reboot couldn't proceed because the
* device wasn't ready for an unattended reboot.
* @throws IOException on remote exceptions from the RecoverySystemService
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
android.Manifest.permission.REBOOT})
public static void rebootAndApply(@NonNull Context context,
public static @ResumeOnRebootRebootErrorCode int rebootAndApply(@NonNull Context context,
@NonNull String reason, boolean slotSwitch) throws IOException {
RecoverySystem rs = context.getSystemService(RecoverySystem.class);
if (!rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch)) {
throw new IOException("system not prepared to apply update");
}
return rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch);
}
/**
@@ -1399,8 +1460,8 @@ public class RecoverySystem {
* Calls the recovery system service to reboot and apply update.
*
*/
private boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch)
throws IOException {
private @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason,
boolean slotSwitch) throws IOException {
try {
return mService.rebootWithLskf(packageName, reason, slotSwitch);
} catch (RemoteException e) {
@@ -1414,8 +1475,8 @@ public class RecoverySystem {
* expects a slot switch for A/B devices.
*
*/
private boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason)
throws IOException {
private @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName,
String reason) throws IOException {
try {
return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason);
} catch (RemoteException e) {

View File

@@ -16,6 +16,13 @@
package com.android.server.recoverysystem;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_NONE;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
import static android.os.RecoverySystem.ResumeOnRebootRebootErrorCode;
import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.IntDef;
@@ -147,24 +154,6 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
ROR_REQUESTED_SKIP_CLEAR})
private @interface ResumeOnRebootActionsOnClear {}
/**
* The error codes 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 {}
/**
* Manages shared preference, i.e. the storage used for metrics reporting.
*/
@@ -724,14 +713,14 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
boolean slotSwitch) {
if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
return REBOOT_ERROR_INVALID_PACKAGE_NAME;
return RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
}
if (!isLskfCaptured(packageName)) {
return REBOOT_ERROR_LSKF_NOT_CAPTURED;
return RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
}
if (!verifySlotForNextBoot(slotSwitch)) {
return REBOOT_ERROR_SLOT_MISMATCH;
return RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
}
final long origId = Binder.clearCallingIdentity();
@@ -744,10 +733,10 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
if (!result) {
Slog.w(TAG, "Failure to escrow key for reboot");
return REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE;
return RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
}
return REBOOT_ERROR_NONE;
return RESUME_ON_REBOOT_REBOOT_ERROR_NONE;
}
private boolean useServerBasedRoR() {
@@ -788,12 +777,13 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
requestCount, slotSwitch, serverBased, durationSeconds, lskfCapturedCount);
}
private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
private @ResumeOnRebootRebootErrorCode int rebootWithLskfImpl(String packageName, String reason,
boolean slotSwitch) {
@ResumeOnRebootRebootErrorCode int errorCode = armRebootEscrow(packageName, slotSwitch);
reportMetricsOnRebootWithLskf(packageName, slotSwitch, errorCode);
if (errorCode != REBOOT_ERROR_NONE) {
return false;
if (errorCode != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
return errorCode;
}
// Clear the metrics prefs after a successful RoR reboot.
@@ -801,17 +791,19 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
PowerManager pm = mInjector.getPowerManager();
pm.reboot(reason);
return true;
return RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
}
@Override // Binder call for the legacy rebootWithLskf
public boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) {
public @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName,
String reason) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
return rebootWithLskfImpl(packageName, reason, true);
}
@Override // Binder call
public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
public @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason,
boolean slotSwitch) {
enforcePermissionForResumeOnReboot();
return rebootWithLskfImpl(packageName, reason, slotSwitch);
}

View File

@@ -17,6 +17,7 @@
package com.android.server.recoverysystem;
import android.os.IRecoverySystem;
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.ShellCommand;
@@ -76,7 +77,8 @@ public class RecoverySystemShellCommand extends ShellCommand {
private int rebootAndApply() throws RemoteException {
String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
boolean success = mService.rebootWithLskf(packageName, rebootReason, false);
boolean success = (mService.rebootWithLskf(packageName, rebootReason, false)
== RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_NONE);
PrintWriter pw = getOutPrintWriter();
// Keep the old message for cts test.
pw.printf("%s Reboot and apply status: %s\n", packageName,

View File

@@ -16,7 +16,12 @@
package com.android.server.recoverysystem;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
@@ -274,8 +279,7 @@ public class RecoverySystemServiceTest {
verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "foobar", true),
is(true));
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "foobar", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("foobar"), anyBoolean());
}
@@ -373,8 +377,7 @@ public class RecoverySystemServiceTest {
anyInt())).thenReturn(3);
when(mSharedPreferences.getLong(eq(RecoverySystemService.LSKF_CAPTURED_TIMESTAMP_PREF),
anyLong())).thenReturn(40_000L);
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
is(true));
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
eq(1) /* client count */, eq(2) /* request count */, eq(true) /* slot switch */,
@@ -386,19 +389,20 @@ public class RecoverySystemServiceTest {
public void rebootWithLskf_slotMismatch_Failure() throws Exception {
assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
mRecoverySystemService.onPreparedForReboot(true);
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", false),
is(false));
assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", false));
}
@Test
public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
is(false));
assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true));
}
@Test
public void rebootWithLskf_withNullCallerId_Failure() throws Exception {
assertThat(mRecoverySystemService.rebootWithLskf(null, null, true), is(false));
assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME,
mRecoverySystemService.rebootWithLskf(null, null, true));
verifyNoMoreInteractions(mIPowerManager);
}
@@ -410,8 +414,7 @@ public class RecoverySystemServiceTest {
// Client B's clear won't affect client A's preparation.
assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
is(true));
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
}
@@ -428,8 +431,7 @@ public class RecoverySystemServiceTest {
when(mSharedPreferences.getLong(eq(RecoverySystemService.LSKF_CAPTURED_TIMESTAMP_PREF),
anyLong())).thenReturn(60_000L);
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
is(true));
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
eq(2) /* client count */, eq(2) /* request count */, eq(true) /* slot switch */,
@@ -450,17 +452,15 @@ public class RecoverySystemServiceTest {
anyLong())).thenReturn(60_000L);
assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
is(false));
assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true));
verifyNoMoreInteractions(mIPowerManager);
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(not(eq(0)), eq(1000),
eq(1) /* client count */, anyInt() /* request count */, eq(true) /* slot switch */,
anyBoolean(), eq(40), 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));
mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics((eq(0)), eq(2000),
@@ -476,16 +476,15 @@ public class RecoverySystemServiceTest {
// Client A clears
assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
is(false));
assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true));
verifyNoMoreInteractions(mIPowerManager);
// Client B clears
assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
verify(mLockSettingsInternal).clearRebootEscrow();
assertThat(
mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true),
is(false));
assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true));
verifyNoMoreInteractions(mIPowerManager);
}