blob: f20922bb297a0ff7dea0da5d48c4f656ca06d363 [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.extractnativelibs.cts;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.targetprep.BuildError;
import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.targetprep.suite.SuiteApkInstaller;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.AbiUtils;
import com.android.tradefed.util.FileUtil;
import org.junit.After;
import org.junit.Before;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* TODO(b/147496159): add more tests.
*/
public class CtsExtractNativeLibsHostTestBase extends BaseHostJUnit4Test {
static final String TEST_REMOTE_DIR = "/data/local/tmp/extract_native_libs_test";
static final String TEST_APK_RESOURCE_PREFIX = "/prebuilt/";
static final String TEST_HOST_TMP_DIR_PREFIX = "cts_extract_native_libs_host_test";
static final String TEST_APK_NAME_BASE = "CtsExtractNativeLibsApp";
static final String TEST_PKG_NAME_BASE = "com.android.cts.extractnativelibs.app";
static final String TEST_NO_EXTRACT_PKG = TEST_PKG_NAME_BASE + ".noextract";
static final String TEST_NO_EXTRACT_CLASS =
TEST_NO_EXTRACT_PKG + ".ExtractNativeLibsFalseDeviceTest";
static final String TEST_NO_EXTRACT_TEST = "testNativeLibsNotExtracted";
static final String TEST_EXTRACT_PKG = TEST_PKG_NAME_BASE + ".extract";
static final String TEST_EXTRACT_CLASS =
TEST_EXTRACT_PKG + ".ExtractNativeLibsTrueDeviceTest";
static final String TEST_EXTRACT_TEST = "testNativeLibsExtracted";
static final String TEST_NATIVE_LIB_LOADED_TEST = "testNativeLibsLoaded";
static final String IDSIG_SUFFIX = ".idsig";
/** Setup test dir. */
@Before
public void setUp() throws Exception {
getDevice().executeShellCommand("mkdir " + TEST_REMOTE_DIR);
}
/** Uninstall apps after tests. */
@After
public void cleanUp() throws Exception {
uninstallPackage(getDevice(), TEST_NO_EXTRACT_PKG);
uninstallPackage(getDevice(), TEST_EXTRACT_PKG);
getDevice().executeShellCommand("rm -r " + TEST_REMOTE_DIR);
}
boolean isIncrementalInstallSupported() throws Exception {
return "true\n".equals(getDevice().executeShellCommand(
"pm has-feature android.software.incremental_delivery"));
}
static String getTestApkName(boolean isExtractNativeLibs, String abiSuffix) {
return TEST_APK_NAME_BASE + (isExtractNativeLibs ? "True" : "False") + abiSuffix + ".apk";
}
static String getTestPackageName(boolean isExtractNativeLibs) {
return isExtractNativeLibs ? TEST_EXTRACT_PKG : TEST_NO_EXTRACT_PKG;
}
static String getTestClassName(boolean isExtractNativeLibs) {
return isExtractNativeLibs ? TEST_EXTRACT_CLASS : TEST_NO_EXTRACT_CLASS;
}
final void installPackage(boolean isIncremental, String apkName) throws Exception {
installPackage(isIncremental, apkName, "");
}
final void installPackage(boolean isIncremental, String apkName, String abi) throws Exception {
if (isIncremental) {
installPackageIncremental(apkName, abi);
} else {
installPackageLegacy(apkName, abi);
}
}
final boolean checkNativeLibDir(boolean isExtractNativeLibs, String abi) throws Exception {
if (isExtractNativeLibs) {
return checkExtractedNativeLibDirForAbi(abi);
} else {
return runDeviceTests(
TEST_NO_EXTRACT_PKG, TEST_NO_EXTRACT_CLASS, TEST_NO_EXTRACT_TEST);
}
}
File getFileFromResource(String filenameInResources) throws Exception {
String fullResourceName = TEST_APK_RESOURCE_PREFIX + filenameInResources;
File tempDir = FileUtil.createTempDir(TEST_HOST_TMP_DIR_PREFIX);
File file = new File(tempDir, filenameInResources);
InputStream in = getClass().getResourceAsStream(fullResourceName);
if (in == null) {
throw new IllegalArgumentException("Resource not found: " + fullResourceName);
}
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
byte[] buf = new byte[65536];
int chunkSize;
while ((chunkSize = in.read(buf)) != -1) {
out.write(buf, 0, chunkSize);
}
out.close();
return file;
}
private boolean runDeviceTestsWithArgs(String pkgName, String testClassName,
String testMethodName, Map<String, String> testArgs) throws Exception {
final String testRunner = "androidx.test.runner.AndroidJUnitRunner";
final long defaultTestTimeoutMs = 60 * 1000L;
final long defaultMaxTimeoutToOutputMs = 60 * 1000L; // 1min
return runDeviceTests(getDevice(), testRunner, pkgName, testClassName, testMethodName,
null, defaultTestTimeoutMs, defaultMaxTimeoutToOutputMs,
0L, true, false, testArgs);
}
private void installPackageLegacy(String apkFileName, String abi)
throws DeviceNotAvailableException, TargetSetupError {
SuiteApkInstaller installer = new SuiteApkInstaller();
installer.addTestFileName(apkFileName);
final String abiFlag = createAbiFlag(abi);
if (!abiFlag.isEmpty()) {
installer.addInstallArg(abiFlag);
}
try {
installer.setUp(getTestInformation());
} catch (BuildError e) {
throw new TargetSetupError(e.getMessage(), e, getDevice().getDeviceDescriptor());
}
}
private boolean checkExtractedNativeLibDirForAbi(String abiSuffix) throws Exception {
final String libAbi = getExpectedLibAbi(abiSuffix);
assertNotNull(libAbi);
final String expectedSubDirArg = "expectedSubDir";
final String expectedNativeLibSubDir = AbiUtils.getArchForAbi(libAbi);
final Map<String, String> testArgs = new HashMap<>();
testArgs.put(expectedSubDirArg, expectedNativeLibSubDir);
return runDeviceTestsWithArgs(TEST_EXTRACT_PKG, TEST_EXTRACT_CLASS, TEST_EXTRACT_TEST,
testArgs);
}
/** Given the abi included in the APK, predict which abi libs will be installed
* @param abiSuffix "64" means the APK contains only 64-bit native libs
* "32" means the APK contains only 32-bit native libs
* "Both" means the APK contains both 32-bit and 64-bit native libs
* @return an ABI string from AbiUtils.ABI_*
* @return an ABI string from AbiUtils.ABI_*
*/
final String getExpectedLibAbi(String abiSuffix) throws Exception {
final String deviceAbi = getDeviceAbi();
final String deviceBitness = AbiUtils.getBitness(deviceAbi);
final String libBitness;
// Use 32-bit native libs if device only supports 32-bit or APK only has 32-libs native libs
if (abiSuffix.equals("32") || deviceBitness.equals("32")) {
libBitness = "32";
} else {
libBitness = "64";
}
final Set<String> libAbis = AbiUtils.getAbisForArch(AbiUtils.getBaseArchForAbi(deviceAbi));
for (String libAbi : libAbis) {
if (AbiUtils.getBitness(libAbi).equals(libBitness)) {
return libAbi;
}
}
return null;
}
final String getDeviceAbi() throws Exception {
return getDevice().getProperty("ro.product.cpu.abi");
}
final Set<String> getDeviceAbis() throws Exception {
String[] abiArray = getDevice().getProperty("ro.product.cpu.abilist").split(",");
return new HashSet<String>(Arrays.asList(abiArray));
}
private void installPackageIncremental(String apkName, String abi) throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final File apk = buildHelper.getTestFile(apkName);
assertNotNull(apk);
final File v4Signature = buildHelper.getTestFile(apkName + IDSIG_SUFFIX);
assertNotNull(v4Signature);
installPackageIncrementalFromFiles(apk, v4Signature, abi);
}
private String installPackageIncrementalFromFiles(File apk, File v4Signature, String abi)
throws Exception {
final String remoteApkPath = TEST_REMOTE_DIR + "/" + apk.getName();
final String remoteIdsigPath = remoteApkPath + IDSIG_SUFFIX;
assertTrue(getDevice().pushFile(apk, remoteApkPath));
assertTrue(getDevice().pushFile(v4Signature, remoteIdsigPath));
return getDevice().executeShellCommand("pm install-incremental "
+ createAbiFlag(abi)
+ " -t -g " + remoteApkPath);
}
private String createAbiFlag(String abi) {
return abi.isEmpty() ? "" : ("--abi " + abi);
}
final String installIncrementalPackageFromResource(String apkFilenameInRes)
throws Exception {
final File apkFile = getFileFromResource(apkFilenameInRes);
final File v4SignatureFile = getFileFromResource(
apkFilenameInRes + IDSIG_SUFFIX);
return installPackageIncrementalFromFiles(apkFile, v4SignatureFile, "");
}
}