blob: 157b2aec3051369bb6ad5dea2a4332cc7c52e605 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.rollback.host;
import static com.google.common.truth.Truth.assertThat;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
import com.android.ddmlib.Log;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* CTS host tests for RollbackManager APIs.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class RollbackManagerHostTest extends BaseHostJUnit4Test {
private static final String TAG = "RollbackManagerHostTest";
private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
/**
* Runs the helper app test method on device.
* Throws an exception if the test method fails.
* <p>
* For example, <code>run("testApkOnlyEnableRollback");</code>
*/
private void run(String method) throws Exception {
assertThat(runDeviceTests("com.android.cts.rollback.host.app",
"com.android.cts.rollback.host.app.HostTestHelper",
method)).isTrue();
}
/**
* Runs the helper app test method on device targeted for
* com.android.cts.rollback.host.app2.HostTestHelper.
*/
private void run2(String method) throws Exception {
assertThat(runDeviceTests("com.android.cts.rollback.host.app2",
"com.android.cts.rollback.host.app2.HostTestHelper",
method)).isTrue();
}
/**
* Return {@code true} if and only if device supports updating apex.
*/
private boolean isApexUpdateSupported() throws Exception {
return "true".equals(getDevice().getProperty("ro.apex.updatable"));
}
/**
* Uninstalls a shim apex only if it's latest version is installed on /data partition (i.e.
* it has a version higher than {@code 1}).
*
* <p>This is purely to optimize tests run time, since uninstalling an apex requires a reboot.
*/
private void uninstallShimApexIfNecessary() throws Exception {
if (!isApexUpdateSupported()) {
// Device doesn't support updating apex. Nothing to uninstall.
return;
}
if (getShimApex().sourceDir.startsWith("/data")) {
final String errorMessage = getDevice().uninstallPackage(SHIM_APEX_PACKAGE_NAME);
if (errorMessage == null) {
Log.i(TAG, "Uninstalling shim apex");
getDevice().reboot();
} else {
Log.e(TAG, "Failed to uninstall shim APEX : " + errorMessage);
}
}
assertThat(getShimApex().versionCode).isEqualTo(1L);
}
/**
* Get {@link ITestDevice.ApexInfo} for the installed shim apex.
*/
private ITestDevice.ApexInfo getShimApex() throws DeviceNotAvailableException {
return getDevice().getActiveApexes().stream().filter(
apex -> apex.name.equals(SHIM_APEX_PACKAGE_NAME)).findAny().orElseThrow(
() -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME));
}
private boolean isCheckpointSupported() throws Exception {
try {
run("isCheckpointSupported");
return true;
} catch (AssertionError ignore) {
return false;
}
}
/**
* Uninstalls any version greater than 1 of shim apex and reboots the device if necessary
* to complete the uninstall.
*
* <p>This is needed because the apex cannot be deleted using PackageInstaller API.
*
* Also abandon sessions left by previous tests so staged-installs won't fail.
*/
@Before
@After
public void cleanUp() throws Exception {
getDevice().executeShellCommand("for i in $(pm list staged-sessions --only-sessionid "
+ "--only-parent); do pm install-abandon $i; done");
getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.A");
getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.B");
run("cleanUp");
uninstallShimApexIfNecessary();
}
/**
* Tests staged rollbacks involving only apks.
*/
@Test
public void testApkOnlyStagedRollback() throws Exception {
run("testApkOnlyStagedRollback_Phase1");
getDevice().reboot();
run("testApkOnlyStagedRollback_Phase2");
getDevice().reboot();
run("testApkOnlyStagedRollback_Phase3");
}
/**
* Tests multiple staged rollbacks involving only apks.
*/
@Test
public void testApkOnlyMultipleStagedRollback() throws Exception {
assumeTrue(isCheckpointSupported());
run("testApkOnlyMultipleStagedRollback_Phase1");
getDevice().reboot();
run("testApkOnlyMultipleStagedRollback_Phase2");
getDevice().reboot();
run("testApkOnlyMultipleStagedRollback_Phase3");
}
/**
* Tests multiple staged partial rollbacks involving only apks.
*/
@Test
public void testApkOnlyMultipleStagedPartialRollback() throws Exception {
assumeTrue(isCheckpointSupported());
run("testApkOnlyMultipleStagedPartialRollback_Phase1");
getDevice().reboot();
run("testApkOnlyMultipleStagedPartialRollback_Phase2");
getDevice().reboot();
run("testApkOnlyMultipleStagedPartialRollback_Phase3");
}
/**
* Tests staged rollbacks involving only apex.
*/
@Test
public void testApexOnlyStagedRollback() throws Exception {
assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
run("testApexOnlyStagedRollback_Phase1");
getDevice().reboot();
run("testApexOnlyStagedRollback_Phase2");
getDevice().reboot();
run("testApexOnlyStagedRollback_Phase3");
getDevice().reboot();
run("testApexOnlyStagedRollback_Phase4");
}
/**
* Tests staged rollbacks to system version involving only apex.
*/
@Test
public void testApexOnlySystemVersionStagedRollback() throws Exception {
assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
run("testApexOnlySystemVersionStagedRollback_Phase1");
getDevice().reboot();
run("testApexOnlySystemVersionStagedRollback_Phase2");
getDevice().reboot();
run("testApexOnlySystemVersionStagedRollback_Phase3");
}
/**
* Tests staged rollbacks involving apex and apk.
*/
@Test
public void testApexAndApkStagedRollback() throws Exception {
assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
run("testApexAndApkStagedRollback_Phase1");
getDevice().reboot();
run("testApexAndApkStagedRollback_Phase2");
getDevice().reboot();
run("testApexAndApkStagedRollback_Phase3");
getDevice().reboot();
run("testApexAndApkStagedRollback_Phase4");
}
/**
* Tests that apex update expires existing rollbacks for that apex.
*/
@Test
public void testApexRollbackExpiration() throws Exception {
assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
run("testApexRollbackExpiration_Phase1");
getDevice().reboot();
run("testApexRollbackExpiration_Phase2");
getDevice().reboot();
run("testApexRollbackExpiration_Phase3");
}
/**
* Tests staged rollbacks involving apex with rotated keys.
*/
@Test
public void testApexKeyRotationStagedRollback() throws Exception {
assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
run("testApexKeyRotationStagedRollback_Phase1");
getDevice().reboot();
run("testApexKeyRotationStagedRollback_Phase2");
getDevice().reboot();
run("testApexKeyRotationStagedRollback_Phase3");
}
/**
* Tests installer B can't rollback a package installed by A.
*/
@Test
public void testApkRollbackByAnotherInstaller() throws Exception {
run("testApkRollbackByAnotherInstaller_Phase1");
run2("testApkRollbackByAnotherInstaller_Phase2");
}
/**
* Tests that rollbacks are invalidated upon fingerprint changes.
*/
@Test
public void testFingerprintChange() throws Exception {
assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
try {
getDevice().executeShellCommand("setprop persist.pm.mock-upgrade true");
run("testFingerprintChange_Phase1");
getDevice().reboot();
run("testFingerprintChange_Phase2");
} finally {
getDevice().executeShellCommand("setprop persist.pm.mock-upgrade false");
}
}
}