285 lines
11 KiB
Java
285 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2021 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.sdkext.extensions;
|
|
|
|
import static com.google.common.truth.Truth.assertWithMessage;
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertFalse;
|
|
import static org.junit.Assert.assertNull;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.junit.Assume.assumeTrue;
|
|
import org.junit.Ignore;
|
|
|
|
import android.cts.install.lib.host.InstallUtilsHost;
|
|
|
|
import com.android.os.ext.testing.CurrentVersion;
|
|
import com.android.tests.rollback.host.AbandonSessionsRule;
|
|
import com.android.tradefed.device.ITestDevice.ApexInfo;
|
|
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
|
|
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
|
|
import com.android.tradefed.util.CommandResult;
|
|
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Rule;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
|
|
import java.io.File;
|
|
import java.lang.NumberFormatException;
|
|
import java.time.Duration;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
@RunWith(DeviceJUnit4ClassRunner.class)
|
|
public class SdkExtensionsHostTest extends BaseHostJUnit4Test {
|
|
|
|
private static final String APP_FILENAME = "sdkextensions_e2e_test_app.apk";
|
|
private static final String APP_PACKAGE = "com.android.sdkext.extensions.apps";
|
|
private static final String APP_R12_FILENAME = "sdkextensions_e2e_test_app_req_r12.apk";
|
|
private static final String APP_R12_PACKAGE = "com.android.sdkext.extensions.apps.r12";
|
|
private static final String APP_S12_FILENAME = "sdkextensions_e2e_test_app_req_s12.apk";
|
|
private static final String APP_S12_PACKAGE = "com.android.sdkext.extensions.apps.s12";
|
|
private static final String APP_R45_FILENAME = "sdkextensions_e2e_test_app_req_r45.apk";
|
|
private static final String APP_R45_PACKAGE = "com.android.sdkext.extensions.apps.r45";
|
|
private static final String APP_S45_FILENAME = "sdkextensions_e2e_test_app_req_s45.apk";
|
|
private static final String APP_S45_PACKAGE = "com.android.sdkext.extensions.apps.s45";
|
|
private static final String MEDIA_FILENAME = "test_com.android.media.apex";
|
|
private static final String SDKEXTENSIONS_FILENAME = "test_com.android.sdkext.apex";
|
|
|
|
private static final Duration BOOT_COMPLETE_TIMEOUT = Duration.ofMinutes(2);
|
|
|
|
private final InstallUtilsHost mInstallUtils = new InstallUtilsHost(this);
|
|
|
|
private Boolean mIsAtLeastS = null;
|
|
private Boolean mIsAtLeastT = null;
|
|
|
|
@Rule
|
|
public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
|
|
|
|
@Before
|
|
public void setUp() throws Exception {
|
|
assumeTrue("Updating APEX is not supported", mInstallUtils.isApexUpdateSupported());
|
|
}
|
|
|
|
@Before
|
|
public void installTestApp() throws Exception {
|
|
File testAppFile = mInstallUtils.getTestFile(APP_FILENAME);
|
|
String installResult = getDevice().installPackage(testAppFile, true);
|
|
assertNull(installResult);
|
|
}
|
|
|
|
@Before // Generally not needed, but local test devices are sometimes in a "bad" start state.
|
|
@After
|
|
public void cleanup() throws Exception {
|
|
getDevice().uninstallPackage(APP_PACKAGE);
|
|
uninstallApexes(SDKEXTENSIONS_FILENAME, MEDIA_FILENAME);
|
|
}
|
|
|
|
@Test
|
|
@Ignore("b/274764792")
|
|
public void testDefault() throws Exception {
|
|
assertVersionDefault();
|
|
}
|
|
|
|
@Test
|
|
@Ignore("b/274764792")
|
|
public void upgradeOneApexWithBump() throws Exception {
|
|
assertVersionDefault();
|
|
mInstallUtils.installApexes(SDKEXTENSIONS_FILENAME);
|
|
reboot();
|
|
|
|
// Version 12 requires sdkext, which is fulfilled
|
|
// Version 45 requires sdkext + media, which isn't fulfilled
|
|
assertRVersionEquals(12);
|
|
assertSVersionEquals(12);
|
|
assertTestMethodsPresent(); // 45 APIs are available on 12 too.
|
|
}
|
|
|
|
@Test
|
|
@Ignore("b/274764792")
|
|
public void upgradeOneApex() throws Exception {
|
|
// Version 45 requires updated sdkext and media, so updating just media changes nothing.
|
|
assertVersionDefault();
|
|
mInstallUtils.installApexes(MEDIA_FILENAME);
|
|
reboot();
|
|
assertVersionDefault();
|
|
}
|
|
|
|
@Test
|
|
@Ignore("b/274764792")
|
|
public void upgradeTwoApexes() throws Exception {
|
|
// Updating sdkext and media bumps the version to 45.
|
|
assertVersionDefault();
|
|
mInstallUtils.installApexes(MEDIA_FILENAME, SDKEXTENSIONS_FILENAME);
|
|
reboot();
|
|
assertVersion45();
|
|
}
|
|
|
|
private boolean canInstallApp(String filename, String packageName) throws Exception {
|
|
File appFile = mInstallUtils.getTestFile(filename);
|
|
String installResult = getDevice().installPackage(appFile, true);
|
|
if (installResult != null) {
|
|
return false;
|
|
}
|
|
assertNull(getDevice().uninstallPackage(packageName));
|
|
return true;
|
|
}
|
|
|
|
private String getExtensionVersionFromSysprop(String v) throws Exception {
|
|
String command = "getprop build.version.extensions." + v;
|
|
CommandResult res = getDevice().executeShellV2Command(command);
|
|
assertEquals(0, (int) res.getExitCode());
|
|
return res.getStdout().replace("\n", "");
|
|
}
|
|
|
|
private String broadcast(String action, String extra) throws Exception {
|
|
String command = getBroadcastCommand(action, extra);
|
|
CommandResult res = getDevice().executeShellV2Command(command);
|
|
assertEquals(0, (int) res.getExitCode());
|
|
Matcher matcher = Pattern.compile("data=\"([^\"]+)\"").matcher(res.getStdout());
|
|
assertTrue("Unexpected output from am broadcast: " + res.getStdout(), matcher.find());
|
|
return matcher.group(1);
|
|
}
|
|
|
|
private boolean broadcastForBoolean(String action, String extra) throws Exception {
|
|
String result = broadcast(action, extra);
|
|
if (result.equals("true") || result.equals("false")) {
|
|
return result.equals("true");
|
|
}
|
|
throw getAppParsingError(result);
|
|
}
|
|
|
|
private int broadcastForInt(String action, String extra) throws Exception {
|
|
String result = broadcast(action, extra);
|
|
try {
|
|
return Integer.parseInt(result);
|
|
} catch (NumberFormatException e) {
|
|
throw getAppParsingError(result);
|
|
}
|
|
}
|
|
|
|
private Error getAppParsingError(String result) {
|
|
String message = "App error! Full stack trace in logcat (grep for SdkExtensionsE2E): ";
|
|
return new AssertionError(message + result);
|
|
}
|
|
|
|
private void assertVersionDefault() throws Exception {
|
|
int expected = isAtLeastT() ? CurrentVersion.T_BASE_VERSION
|
|
: isAtLeastS() ? CurrentVersion.S_BASE_VERSION
|
|
: CurrentVersion.R_BASE_VERSION;
|
|
assertRVersionEquals(expected);
|
|
assertSVersionEquals(expected);
|
|
assertTestMethodsNotPresent();
|
|
}
|
|
|
|
private void assertVersion45() throws Exception {
|
|
assertRVersionEquals(45);
|
|
assertSVersionEquals(45);
|
|
assertTestMethodsPresent();
|
|
}
|
|
|
|
private void assertTestMethodsNotPresent() throws Exception {
|
|
assertTrue(broadcastForBoolean("MAKE_CALLS_DEFAULT", null));
|
|
}
|
|
|
|
private void assertTestMethodsPresent() throws Exception {
|
|
if (isAtLeastS()) {
|
|
assertTrue(broadcastForBoolean("MAKE_CALLS_45", null));
|
|
} else {
|
|
// The APIs in the test apex are not currently getting installed correctly
|
|
// on Android R devices because they rely on the dynamic classpath feature.
|
|
// TODO(b/234361913): fix this
|
|
assertTestMethodsNotPresent();
|
|
}
|
|
}
|
|
|
|
private void assertRVersionEquals(int version) throws Exception {
|
|
int appValue = broadcastForInt("GET_SDK_VERSION", "r");
|
|
String syspropValue = getExtensionVersionFromSysprop("r");
|
|
assertEquals(version, appValue);
|
|
assertEquals(String.valueOf(version), syspropValue);
|
|
assertEquals(version >= 12, canInstallApp(APP_R12_FILENAME, APP_R12_PACKAGE));
|
|
assertEquals(version >= 45, canInstallApp(APP_R45_FILENAME, APP_R45_PACKAGE));
|
|
}
|
|
|
|
private void assertSVersionEquals(int version) throws Exception {
|
|
int appValue = broadcastForInt("GET_SDK_VERSION", "s");
|
|
String syspropValue = getExtensionVersionFromSysprop("s");
|
|
if (isAtLeastS()) {
|
|
assertEquals(version, appValue);
|
|
assertEquals(String.valueOf(version), syspropValue);
|
|
|
|
// These APKs require the same R version as they do S version.
|
|
int minVersion = Math.min(version, broadcastForInt("GET_SDK_VERSION", "r"));
|
|
assertEquals(minVersion >= 12, canInstallApp(APP_S12_FILENAME, APP_S12_PACKAGE));
|
|
assertEquals(minVersion >= 45, canInstallApp(APP_S45_FILENAME, APP_S45_PACKAGE));
|
|
} else {
|
|
assertEquals(0, appValue);
|
|
assertEquals("", syspropValue);
|
|
assertFalse(canInstallApp(APP_S12_FILENAME, APP_S12_PACKAGE));
|
|
assertFalse(canInstallApp(APP_S45_FILENAME, APP_S45_PACKAGE));
|
|
}
|
|
}
|
|
|
|
private static String getBroadcastCommand(String action, String extra) {
|
|
String cmd = "am broadcast";
|
|
cmd += " -a com.android.sdkext.extensions.apps." + action;
|
|
if (extra != null) {
|
|
cmd += " -e extra " + extra;
|
|
}
|
|
cmd += " -n com.android.sdkext.extensions.apps/.Receiver";
|
|
return cmd;
|
|
}
|
|
|
|
private boolean isAtLeastS() throws Exception {
|
|
if (mIsAtLeastS == null) {
|
|
mIsAtLeastS = broadcastForBoolean("IS_AT_LEAST", "s");
|
|
}
|
|
return mIsAtLeastS;
|
|
}
|
|
|
|
private boolean isAtLeastT() throws Exception {
|
|
if (mIsAtLeastT == null) {
|
|
mIsAtLeastT = broadcastForBoolean("IS_AT_LEAST", "t");
|
|
}
|
|
return mIsAtLeastT;
|
|
}
|
|
|
|
private boolean uninstallApexes(String... filenames) throws Exception {
|
|
boolean reboot = false;
|
|
for (String filename : filenames) {
|
|
ApexInfo apex = mInstallUtils.getApexInfo(mInstallUtils.getTestFile(filename));
|
|
String res = getDevice().uninstallPackage(apex.name);
|
|
// res is null for successful uninstalls (non-null likely implesfactory version).
|
|
reboot |= res == null;
|
|
}
|
|
if (reboot) {
|
|
reboot();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void reboot() throws Exception {
|
|
getDevice().reboot();
|
|
boolean success = getDevice().waitForBootComplete(BOOT_COMPLETE_TIMEOUT.toMillis());
|
|
assertWithMessage("Device didn't boot in %s", BOOT_COMPLETE_TIMEOUT).that(success).isTrue();
|
|
}
|
|
}
|