blob: 2aef11860c7a2a1ce1537d85cc300599fa4ac4f7 [file] [log] [blame]
/*
* Copyright (C) 2018 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.tradefed.targetprep;
import com.android.compatibility.common.tradefed.build.VtsCompatibilityInvocationHelper;
import com.android.annotations.VisibleForTesting;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.targetprep.multi.IMultiTargetPreparer;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.util.CmdUtil;
import com.android.tradefed.util.FileUtil;
import org.junit.Assert;
import java.io.File;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.function.Predicate;
/**
* Starts and stops a HAL (Hardware Abstraction Layer) adapter.
* Only used for single-device testing or the primary device in multi-device
* testing.
*/
@OptionClass(alias = "vts-hal-adapter-preparer")
public class VtsHalAdapterPreparer implements ITargetCleaner, IAbiReceiver {
static final int THREAD_COUNT_DEFAULT = 1;
static final String HAL_INTERFACE_SEP = "::";
static final String HAL_INSTANCE_SEP = "/";
// Relative path to vts native tests directory.
static final String VTS_NATIVE_TEST_DIR = "DATA/nativetest%s/";
// Path of native tests directory on target device.
static final String TARGET_NATIVE_TEST_DIR = "/data/nativetest%s/";
// Sysprop to stop HIDL adapaters. Currently, there's one global flag for all adapters.
static final String ADAPTER_SYSPROP = "test.hidl.adapters.deactivated";
// The wrapper script to start an adapter binary in the background.
static final String SCRIPT_PATH = "/data/local/tmp/vts_adapter.sh";
// Command to list the registered instance for the given hal@version.
static final String LIST_HAL_CMD =
"lshal -ti --neat 2>/dev/null | grep -E '^hwbinder' | awk '{print $2}' | grep %s";
@Option(name = "adapter-binary-name",
description = "Adapter binary file name (typically under /data/nativetest*/)")
private String mAdapterBinaryName = null;
@Option(name = "hal-package-name", description = "Target hal to adapter")
private String mPackageName = null;
@Option(name = "thread-count", description = "HAL adapter's thread count")
private int mThreadCount = THREAD_COUNT_DEFAULT;
// Application Binary Interface (ABI) info of the current test run.
private IAbi mAbi = null;
// CmdUtil help to verify the cmd results.
private CmdUtil mCmdUtil = null;
// Predicates to stop retrying cmd.
private Predicate<String> mCheckEmpty = (String str) -> {
return str.isEmpty();
};
private Predicate<String> mCheckNonEmpty = (String str) -> {
return !str.isEmpty();
};
private Vector<String> mCommands = new Vector<String>();
/**
* {@inheritDoc}
*/
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo)
throws TargetSetupError, BuildError, DeviceNotAvailableException {
// adb root.
device.enableAdbRoot();
String bitness =
(mAbi != null) ? ((mAbi.getBitness() == "32") ? "" : mAbi.getBitness()) : "";
try {
pushAdapter(device, bitness);
} catch (IOException | NoSuchElementException e) {
CLog.e("Could not push adapter: " + e.toString());
throw new TargetSetupError("Could not push adapter.");
}
mCmdUtil = mCmdUtil != null ? mCmdUtil : new CmdUtil();
mCmdUtil.setSystemProperty(device, ADAPTER_SYSPROP, "false");
String out = device.executeShellCommand(String.format(LIST_HAL_CMD, mPackageName));
for (String line : out.split("\n")) {
if (!line.isEmpty()) {
if (!line.contains(HAL_INTERFACE_SEP)) {
throw new TargetSetupError("HAL instance with wrong format.");
}
String interfaceInstance = line.split(HAL_INTERFACE_SEP, 2)[1];
if (!interfaceInstance.contains(HAL_INSTANCE_SEP)) {
throw new TargetSetupError("HAL instance with wrong format.");
}
String interfaceName = interfaceInstance.split(HAL_INSTANCE_SEP, 2)[0];
String instanceName = interfaceInstance.split(HAL_INSTANCE_SEP, 2)[1];
// starts adapter
String command = String.format("chmod a+x %s", SCRIPT_PATH);
mCommands.add(command);
command = String.format("%s /data/nativetest%s/%s %s %s %d", SCRIPT_PATH, bitness,
mAdapterBinaryName, interfaceName, instanceName, mThreadCount);
CLog.i("Trying to adapter for %s",
mPackageName + "::" + interfaceName + "/" + instanceName);
mCommands.add(command);
}
}
if (mCommands.isEmpty()) {
CLog.w("The specific HAL service is not running.");
return;
}
if (!mCmdUtil.retry(
device, mCommands, String.format(LIST_HAL_CMD, mPackageName), mCheckEmpty)) {
throw new TargetSetupError("HAL adapter setup failed.");
}
mCmdUtil.restartFramework(device);
if (!mCmdUtil.waitCmdResultWithDelay(
device, "service list | grep IPackageManager", mCheckNonEmpty)) {
throw new TargetSetupError("Failed to start package service");
}
}
/**
* {@inheritDoc}
*/
@Override
public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
throws DeviceNotAvailableException {
if (!mCommands.isEmpty()) {
// stops adapter(s)
String command = String.format("setprop %s %s", ADAPTER_SYSPROP, "true");
mCmdUtil = mCmdUtil != null ? mCmdUtil : new CmdUtil();
Assert.assertTrue("HAL restore failed.",
mCmdUtil.retry(device, command, String.format(LIST_HAL_CMD, mPackageName),
mCheckNonEmpty, mCommands.size() + mCmdUtil.MAX_RETRY_COUNT));
// TODO: cleanup the pushed adapter files.
mCmdUtil.restartFramework(device);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setAbi(IAbi abi) {
mAbi = abi;
}
/**
* {@inheritDoc}
*/
@Override
public IAbi getAbi() {
return mAbi;
}
/**
* Push the required adapter binary to device.
*
* @param device device object.
* @param bitness ABI bitness.
* @throws DeviceNotAvailableException.
* @throws IOException.
* @throws NoSuchElementException.
*/
private void pushAdapter(ITestDevice device, String bitness)
throws DeviceNotAvailableException, IOException, NoSuchElementException {
VtsCompatibilityInvocationHelper invocationHelper = createVtsHelper();
File adapterDir = new File(
invocationHelper.getTestsDir(), String.format(VTS_NATIVE_TEST_DIR, bitness));
File adapter = FileUtil.findFile(adapterDir, mAdapterBinaryName);
if (adapter != null) {
CLog.i("Pushing %s", mAdapterBinaryName);
device.pushFile(
adapter, String.format(TARGET_NATIVE_TEST_DIR, bitness) + mAdapterBinaryName);
} else {
throw new NoSuchElementException("Could not find adapter: " + mAdapterBinaryName);
}
}
/**
* Create and return a {@link VtsCompatibilityInvocationHelper} to use during the preparer.
*/
@VisibleForTesting
VtsCompatibilityInvocationHelper createVtsHelper() {
return new VtsCompatibilityInvocationHelper();
}
@VisibleForTesting
void setCmdUtil(CmdUtil cmdUtil) {
mCmdUtil = cmdUtil;
}
@VisibleForTesting
void addCommand(String command) {
mCommands.add(command);
}
}