Merge "Check slot in reboot function"
This commit is contained in:
@@ -112,6 +112,9 @@ java_library_static {
|
|||||||
"time_zone_distro",
|
"time_zone_distro",
|
||||||
"time_zone_distro_installer",
|
"time_zone_distro_installer",
|
||||||
"android.hardware.authsecret-V1.0-java",
|
"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.broadcastradio-V2.0-java",
|
||||||
"android.hardware.health-V1.0-java",
|
"android.hardware.health-V1.0-java",
|
||||||
"android.hardware.health-V2.0-java",
|
"android.hardware.health-V2.0-java",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.boot.V1_0.IBootControl;
|
||||||
import android.net.LocalSocket;
|
import android.net.LocalSocket;
|
||||||
import android.net.LocalSocketAddress;
|
import android.net.LocalSocketAddress;
|
||||||
import android.os.Binder;
|
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";
|
static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
|
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();
|
private static final Object sRequestLock = new Object();
|
||||||
|
|
||||||
@@ -177,6 +180,25 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
|
|||||||
return socket;
|
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 {
|
public void threadSleep(long millis) throws InterruptedException {
|
||||||
Thread.sleep(millis);
|
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;
|
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) {
|
private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
|
||||||
if (packageName == null) {
|
if (packageName == null) {
|
||||||
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
|
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
|
||||||
@@ -485,7 +557,10 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
|
|||||||
return false;
|
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.
|
// TODO(xunchang) write the vbmeta digest along with the escrowKey before reboot.
|
||||||
if (!mInjector.getLockSettingsService().armRebootEscrow()) {
|
if (!mInjector.getLockSettingsService().armRebootEscrow()) {
|
||||||
Slog.w(TAG, "Failure to escrow key for reboot");
|
Slog.w(TAG, "Failure to escrow key for reboot");
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class RecoverySystemShellCommand extends ShellCommand {
|
|||||||
private int rebootAndApply() throws RemoteException {
|
private int rebootAndApply() throws RemoteException {
|
||||||
String packageName = getNextArgRequired();
|
String packageName = getNextArgRequired();
|
||||||
String rebootReason = getNextArgRequired();
|
String rebootReason = getNextArgRequired();
|
||||||
boolean success = mService.rebootWithLskf(packageName, rebootReason, true);
|
boolean success = mService.rebootWithLskf(packageName, rebootReason, false);
|
||||||
PrintWriter pw = getOutPrintWriter();
|
PrintWriter pw = getOutPrintWriter();
|
||||||
// Keep the old message for cts test.
|
// Keep the old message for cts test.
|
||||||
pw.printf("%s Reboot and apply status: %s\n", packageName,
|
pw.printf("%s Reboot and apply status: %s\n", packageName,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.boot.V1_2.IBootControl;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IPowerManager;
|
import android.os.IPowerManager;
|
||||||
import android.os.IRecoverySystemProgressListener;
|
import android.os.IRecoverySystemProgressListener;
|
||||||
@@ -68,12 +69,13 @@ public class RecoverySystemServiceTest {
|
|||||||
private IThermalService mIThermalService;
|
private IThermalService mIThermalService;
|
||||||
private FileWriter mUncryptUpdateFileWriter;
|
private FileWriter mUncryptUpdateFileWriter;
|
||||||
private LockSettingsInternal mLockSettingsInternal;
|
private LockSettingsInternal mLockSettingsInternal;
|
||||||
|
private IBootControl mIBootControl;
|
||||||
|
|
||||||
private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
|
private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
|
||||||
private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
|
private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() throws Exception {
|
||||||
mContext = mock(Context.class);
|
mContext = mock(Context.class);
|
||||||
mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties();
|
mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties();
|
||||||
mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class);
|
mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class);
|
||||||
@@ -88,8 +90,13 @@ public class RecoverySystemServiceTest {
|
|||||||
PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
|
PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
|
||||||
mIThermalService, new Handler(looper));
|
mIThermalService, new Handler(looper));
|
||||||
|
|
||||||
|
mIBootControl = mock(IBootControl.class);
|
||||||
|
when(mIBootControl.getCurrentSlot()).thenReturn(0);
|
||||||
|
when(mIBootControl.getActiveBootSlot()).thenReturn(1);
|
||||||
|
|
||||||
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
|
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
|
||||||
powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
|
powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal,
|
||||||
|
mIBootControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -332,6 +339,15 @@ public class RecoverySystemServiceTest {
|
|||||||
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
|
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
|
@Test
|
||||||
public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
|
public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
|
||||||
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
|
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server.recoverysystem;
|
package com.android.server.recoverysystem;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.hardware.boot.V1_2.IBootControl;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
|
||||||
import com.android.internal.widget.LockSettingsInternal;
|
import com.android.internal.widget.LockSettingsInternal;
|
||||||
@@ -30,16 +31,19 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
|
|||||||
private final FileWriter mUncryptPackageFileWriter;
|
private final FileWriter mUncryptPackageFileWriter;
|
||||||
private final UncryptSocket mUncryptSocket;
|
private final UncryptSocket mUncryptSocket;
|
||||||
private final LockSettingsInternal mLockSettingsInternal;
|
private final LockSettingsInternal mLockSettingsInternal;
|
||||||
|
private final IBootControl mIBootControl;
|
||||||
|
|
||||||
MockInjector(Context context, FakeSystemProperties systemProperties,
|
MockInjector(Context context, FakeSystemProperties systemProperties,
|
||||||
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
|
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
|
||||||
UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
|
UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
|
||||||
|
IBootControl bootControl) {
|
||||||
super(context);
|
super(context);
|
||||||
mSystemProperties = systemProperties;
|
mSystemProperties = systemProperties;
|
||||||
mPowerManager = powerManager;
|
mPowerManager = powerManager;
|
||||||
mUncryptPackageFileWriter = uncryptPackageFileWriter;
|
mUncryptPackageFileWriter = uncryptPackageFileWriter;
|
||||||
mUncryptSocket = uncryptSocket;
|
mUncryptSocket = uncryptSocket;
|
||||||
mLockSettingsInternal = lockSettingsInternal;
|
mLockSettingsInternal = lockSettingsInternal;
|
||||||
|
mIBootControl = bootControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,13 +89,19 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
|
|||||||
public LockSettingsInternal getLockSettingsService() {
|
public LockSettingsInternal getLockSettingsService() {
|
||||||
return mLockSettingsInternal;
|
return mLockSettingsInternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBootControl getBootControl() {
|
||||||
|
return mIBootControl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
|
RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
|
||||||
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
|
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
|
||||||
UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
|
UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
|
||||||
|
IBootControl bootControl) {
|
||||||
super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
|
super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
|
||||||
uncryptSocket, lockSettingsInternal));
|
uncryptSocket, lockSettingsInternal, bootControl));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FakeSystemProperties {
|
public static class FakeSystemProperties {
|
||||||
@@ -102,6 +112,8 @@ public class RecoverySystemServiceTestable extends RecoverySystemService {
|
|||||||
|| RecoverySystemService.INIT_SERVICE_SETUP_BCB.equals(key)
|
|| RecoverySystemService.INIT_SERVICE_SETUP_BCB.equals(key)
|
||||||
|| RecoverySystemService.INIT_SERVICE_CLEAR_BCB.equals(key)) {
|
|| RecoverySystemService.INIT_SERVICE_CLEAR_BCB.equals(key)) {
|
||||||
return null;
|
return null;
|
||||||
|
} else if (RecoverySystemService.AB_UPDATE.equals(key)) {
|
||||||
|
return "true";
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("unexpected test key: " + key);
|
throw new IllegalArgumentException("unexpected test key: " + key);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user