summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tianjie <xunchang@google.com> 2021-01-13 21:09:03 -0800
committer Tianjie <xunchang@google.com> 2021-01-16 21:08:35 -0800
commitc451ffb6ca52075eb93fbb7df1cfc11f2bdb2ec5 (patch)
tree83243808bd539c79bb0c5b12e2e67a0376128de9
parent4d20410bf4bdd1fe851145dcbd3ea1b5c7d5dcd7 (diff)
Check slot in reboot function
As part of the multi-client ror support, we want to compare the intended boot slot from the client with the actual slot to boot into. Note boot control 1.2 is required to query the actual boot slot; and we haven't installed V1.2 on Pixel3 and before. So the check is skipped today if an old boot control HAL is installed on device. Bug: 173808057 Test: atest CtsAppSecurityHostTestCases:ResumeOnRebootHostTest; atest FrameworksServicesTests:RecoverySystemServiceTest Change-Id: I13f9f5c75514424b70f96556b14a13783e9b6a7d
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/com/android/server/recoverysystem/RecoverySystemService.java77
-rw-r--r--services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java18
5 files changed, 113 insertions, 7 deletions
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 307d344bffa7..adebcc7ee54e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -112,6 +112,9 @@ java_library_static {
"time_zone_distro",
"time_zone_distro_installer",
"android.hardware.authsecret-V1.0-java",
+ "android.hardware.boot-V1.0-java",
+ "android.hardware.boot-V1.1-java",
+ "android.hardware.boot-V1.2-java",
"android.hardware.broadcastradio-V2.0-java",
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 5c01e43af5a9..fd2d8e1b834b 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.hardware.boot.V1_0.IBootControl;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Binder;
@@ -73,6 +74,8 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
@VisibleForTesting
static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
+ @VisibleForTesting
+ static final String AB_UPDATE = "ro.build.ab_update";
private static final Object sRequestLock = new Object();
@@ -177,6 +180,25 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return socket;
}
+ /**
+ * Throws remote exception if there's an error getting the boot control HAL.
+ * Returns null if the boot control HAL's version is older than V1_2.
+ */
+ public android.hardware.boot.V1_2.IBootControl getBootControl() throws RemoteException {
+ IBootControl bootControlV10 = IBootControl.getService(true);
+ if (bootControlV10 == null) {
+ throw new RemoteException("Failed to get boot control HAL V1_0.");
+ }
+
+ android.hardware.boot.V1_2.IBootControl bootControlV12 =
+ android.hardware.boot.V1_2.IBootControl.castFrom(bootControlV10);
+ if (bootControlV12 == null) {
+ Slog.w(TAG, "Device doesn't implement boot control HAL V1_2.");
+ return null;
+ }
+ return bootControlV12;
+ }
+
public void threadSleep(long millis) throws InterruptedException {
Thread.sleep(millis);
}
@@ -476,6 +498,56 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
}
+ private boolean isAbDevice() {
+ return "true".equalsIgnoreCase(mInjector.systemPropertiesGet(AB_UPDATE));
+ }
+
+ private boolean verifySlotForNextBoot(boolean slotSwitch) {
+ if (!isAbDevice()) {
+ Slog.w(TAG, "Device isn't a/b, skipping slot verification.");
+ return true;
+ }
+
+ android.hardware.boot.V1_2.IBootControl bootControl;
+ try {
+ bootControl = mInjector.getBootControl();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to get the boot control HAL " + e);
+ return false;
+ }
+
+ // TODO(xunchang) enforce boot control V1_2 HAL on devices using multi client RoR
+ if (bootControl == null) {
+ Slog.w(TAG, "Cannot get the boot control HAL, skipping slot verification.");
+ return true;
+ }
+
+ int current_slot;
+ int next_active_slot;
+ try {
+ current_slot = bootControl.getCurrentSlot();
+ if (current_slot != 0 && current_slot != 1) {
+ throw new IllegalStateException("Current boot slot should be 0 or 1, got "
+ + current_slot);
+ }
+ next_active_slot = bootControl.getActiveBootSlot();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to query the active slots", e);
+ return false;
+ }
+
+ int expected_active_slot = current_slot;
+ if (slotSwitch) {
+ expected_active_slot = current_slot == 0 ? 1 : 0;
+ }
+ if (next_active_slot != expected_active_slot) {
+ Slog.w(TAG, "The next active boot slot doesn't match the expected value, "
+ + "expected " + expected_active_slot + ", got " + next_active_slot);
+ return false;
+ }
+ return true;
+ }
+
private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
@@ -485,7 +557,10 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
return false;
}
- // TODO(xunchang) check the slot to boot into, and fail the reboot upon slot mismatch.
+ if (!verifySlotForNextBoot(slotSwitch)) {
+ return false;
+ }
+
// 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");
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index f20d80d57476..ae71c1a1e444 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -76,7 +76,7 @@ public class RecoverySystemShellCommand extends ShellCommand {
private int rebootAndApply() throws RemoteException {
String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
- boolean success = mService.rebootWithLskf(packageName, rebootReason, true);
+ boolean success = mService.rebootWithLskf(packageName, rebootReason, false);
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 b07b8fa059d1..9b8a2a82c6df 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.hardware.boot.V1_2.IBootControl;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IRecoverySystemProgressListener;
@@ -68,12 +69,13 @@ public class RecoverySystemServiceTest {
private IThermalService mIThermalService;
private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal;
+ private IBootControl mIBootControl;
private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
@Before
- public void setup() {
+ public void setup() throws Exception {
mContext = mock(Context.class);
mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties();
mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class);
@@ -88,8 +90,13 @@ public class RecoverySystemServiceTest {
PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
mIThermalService, new Handler(looper));
+ mIBootControl = mock(IBootControl.class);
+ when(mIBootControl.getCurrentSlot()).thenReturn(0);
+ when(mIBootControl.getActiveBootSlot()).thenReturn(1);
+
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
- powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
+ powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal,
+ mIBootControl);
}
@Test
@@ -332,6 +339,15 @@ public class RecoverySystemServiceTest {
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
}
+
+ @Test
+ 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));
+ }
+
@Test
public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
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 131e4f321a6c..0727e5adb9ca 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
@@ -17,6 +17,7 @@
package com.android.server.recoverysystem;
import android.content.Context;
+import android.hardware.boot.V1_2.IBootControl;
import android.os.PowerManager;
import com.android.internal.widget.LockSettingsInternal;
@@ -30,16 +31,19 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
private final FileWriter mUncryptPackageFileWriter;
private final UncryptSocket mUncryptSocket;
private final LockSettingsInternal mLockSettingsInternal;
+ private final IBootControl mIBootControl;
MockInjector(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
- UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
+ UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
+ IBootControl bootControl) {
super(context);
mSystemProperties = systemProperties;
mPowerManager = powerManager;
mUncryptPackageFileWriter = uncryptPackageFileWriter;
mUncryptSocket = uncryptSocket;
mLockSettingsInternal = lockSettingsInternal;
+ mIBootControl = bootControl;
}
@Override
@@ -85,13 +89,19 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
public LockSettingsInternal getLockSettingsService() {
return mLockSettingsInternal;
}
+
+ @Override
+ public IBootControl getBootControl() {
+ return mIBootControl;
+ }
}
RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
- UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
+ UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
+ IBootControl bootControl) {
super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
- uncryptSocket, lockSettingsInternal));
+ uncryptSocket, lockSettingsInternal, bootControl));
}
public static class FakeSystemProperties {
@@ -102,6 +112,8 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
|| RecoverySystemService.INIT_SERVICE_SETUP_BCB.equals(key)
|| RecoverySystemService.INIT_SERVICE_CLEAR_BCB.equals(key)) {
return null;
+ } else if (RecoverySystemService.AB_UPDATE.equals(key)) {
+ return "true";
} else {
throw new IllegalArgumentException("unexpected test key: " + key);
}