Loading services/core/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -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", Loading services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +76 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); } Loading Loading @@ -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."); Loading @@ -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"); Loading services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java +1 −1 Original line number Diff line number Diff line Loading @@ -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, Loading services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +18 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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 Loading Loading @@ -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), Loading services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java +15 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -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 { Loading @@ -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); } Loading Loading
services/core/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -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", Loading
services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +76 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); } Loading Loading @@ -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."); Loading @@ -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"); Loading
services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java +1 −1 Original line number Diff line number Diff line Loading @@ -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, Loading
services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +18 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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 Loading Loading @@ -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), Loading
services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java +15 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -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 { Loading @@ -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); } Loading