diff options
6 files changed, 126 insertions, 67 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d39c6e24a3ef..c496fddad819 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -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 { diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl index 205288303b1e..9368b68a91c6 100644 --- a/core/java/android/os/IRecoverySystem.aidl +++ b/core/java/android/os/IRecoverySystem.aidl @@ -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); } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 43184ea4b9a9..a42448c261d4 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -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; @@ -155,6 +156,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) { diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index fe21201f5cb7..6ea030f56d4d 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -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; @@ -148,24 +155,6 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo 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. */ public static class PreferencesManager { @@ -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); } diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java index ae71c1a1e444..3d78828888da 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java @@ -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, 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 7903a90979fb..2b358ea5e105 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java @@ -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); } |