1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.targetprep;
18 
19 import com.android.annotations.VisibleForTesting;
20 import com.android.compatibility.common.tradefed.build.VtsCompatibilityInvocationHelper;
21 import com.android.tradefed.build.IBuildInfo;
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.config.OptionClass;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.log.LogUtil.CLog;
27 import com.android.tradefed.testtype.IAbi;
28 import com.android.tradefed.testtype.IAbiReceiver;
29 import com.android.tradefed.util.CmdUtil;
30 import com.android.tradefed.util.FileUtil;
31 import java.io.File;
32 import java.io.IOException;
33 import java.util.NoSuchElementException;
34 import java.util.Vector;
35 import java.util.function.Predicate;
36 
37 /**
38  * Starts and stops a HAL (Hardware Abstraction Layer) adapter.
39  * Only used for single-device testing or the primary device in multi-device
40  * testing.
41  */
42 @OptionClass(alias = "vts-hal-adapter-preparer")
43 public class VtsHalAdapterPreparer implements ITargetCleaner, IAbiReceiver {
44     static final int THREAD_COUNT_DEFAULT = 1;
45 
46     static final String HAL_INTERFACE_SEP = "::";
47     static final String HAL_INSTANCE_SEP = "/";
48     // Relative path to vts native tests directory.
49     static final String VTS_NATIVE_TEST_DIR = "DATA/nativetest%s/";
50     // Path of native tests directory on target device.
51     static final String TARGET_NATIVE_TEST_DIR = "/data/nativetest%s/";
52     // Sysprop to stop HIDL adapaters. Currently, there's one global flag for all adapters.
53     static final String ADAPTER_SYSPROP = "test.hidl.adapters.deactivated";
54     // The wrapper script to start an adapter binary in the background.
55     static final String SCRIPT_PATH = "/data/local/tmp/vts_adapter.sh";
56     // Command to list the registered instance for the given hal@version.
57     static final String LIST_HAL_CMD =
58             "lshal -ti --neat 2>/dev/null | grep -e '^hwbinder' | awk '{print $2}' | grep %s";
59 
60     @Option(name = "adapter-binary-name",
61             description = "Adapter binary file name (typically under /data/nativetest*/)")
62     private String mAdapterBinaryName = null;
63 
64     @Option(name = "hal-package-name", description = "Target hal to adapter")
65     private String mPackageName = null;
66 
67     @Option(name = "thread-count", description = "HAL adapter's thread count")
68     private int mThreadCount = THREAD_COUNT_DEFAULT;
69 
70     // Application Binary Interface (ABI) info of the current test run.
71     private IAbi mAbi = null;
72 
73     // CmdUtil help to verify the cmd results.
74     private CmdUtil mCmdUtil = null;
75     // Predicates to stop retrying cmd.
76     private Predicate<String> mCheckEmpty = (String str) -> {
77         return str.isEmpty();
78     };
79     private Predicate<String> mCheckNonEmpty = (String str) -> {
80         return !str.isEmpty();
81     };
82     private Vector<String> mCommands = new Vector<String>();
83 
84     /**
85      * {@inheritDoc}
86      */
87     @Override
setUp(ITestDevice device, IBuildInfo buildInfo)88     public void setUp(ITestDevice device, IBuildInfo buildInfo)
89             throws TargetSetupError, BuildError, DeviceNotAvailableException {
90         // adb root.
91         device.enableAdbRoot();
92         String bitness =
93                 (mAbi != null) ? ((mAbi.getBitness() == "32") ? "" : mAbi.getBitness()) : "";
94         try {
95             pushAdapter(device, bitness);
96         } catch (IOException | NoSuchElementException e) {
97             CLog.e("Could not push adapter: " + e.toString());
98             throw new TargetSetupError("Could not push adapter.");
99         }
100 
101         mCmdUtil = mCmdUtil != null ? mCmdUtil : new CmdUtil();
102         mCmdUtil.setSystemProperty(device, ADAPTER_SYSPROP, "false");
103 
104         String out = device.executeShellCommand(String.format(LIST_HAL_CMD, mPackageName));
105         for (String line : out.split("\n")) {
106             if (!line.isEmpty()) {
107                 if (!line.contains(HAL_INTERFACE_SEP)) {
108                     throw new TargetSetupError("HAL instance with wrong format.");
109                 }
110                 String interfaceInstance = line.split(HAL_INTERFACE_SEP, 2)[1];
111                 if (!interfaceInstance.contains(HAL_INSTANCE_SEP)) {
112                     throw new TargetSetupError("HAL instance with wrong format.");
113                 }
114                 String interfaceName = interfaceInstance.split(HAL_INSTANCE_SEP, 2)[0];
115                 String instanceName = interfaceInstance.split(HAL_INSTANCE_SEP, 2)[1];
116                 // starts adapter
117                 String command = String.format("chmod a+x %s", SCRIPT_PATH);
118                 mCommands.add(command);
119                 command = String.format("%s /data/nativetest%s/%s %s %s %d", SCRIPT_PATH, bitness,
120                         mAdapterBinaryName, interfaceName, instanceName, mThreadCount);
121                 CLog.d("Trying to adapter for %s",
122                         mPackageName + "::" + interfaceName + "/" + instanceName);
123                 mCommands.add(command);
124             }
125         }
126         if (mCommands.isEmpty()) {
127             CLog.w("The specific HAL service is not running.");
128             return;
129         }
130         if (!mCmdUtil.retry(
131                     device, mCommands, String.format(LIST_HAL_CMD, mPackageName), mCheckEmpty)) {
132             throw new TargetSetupError("HAL adapter setup failed.");
133         }
134 
135         mCmdUtil.restartFramework(device);
136         if (!mCmdUtil.waitCmdResultWithDelay(
137                     device, "service list | grep IPackageManager", mCheckNonEmpty)) {
138             throw new TargetSetupError("Failed to start package service");
139         }
140     }
141 
142     /**
143      * {@inheritDoc}
144      */
145     @Override
tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)146     public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
147             throws DeviceNotAvailableException {
148         if (!mCommands.isEmpty()) {
149             // stops adapter(s)
150             String command = String.format("setprop %s %s", ADAPTER_SYSPROP, "true");
151             mCmdUtil = mCmdUtil != null ? mCmdUtil : new CmdUtil();
152             if (!mCmdUtil.retry(device, command, String.format(LIST_HAL_CMD, mPackageName),
153                         mCheckNonEmpty, mCommands.size() + mCmdUtil.MAX_RETRY_COUNT)) {
154                 CLog.e("HAL restore failed.");
155             }
156             // TODO: cleanup the pushed adapter files.
157             mCmdUtil.restartFramework(device);
158         }
159     }
160 
161     /**
162      * {@inheritDoc}
163      */
164     @Override
setAbi(IAbi abi)165     public void setAbi(IAbi abi) {
166         mAbi = abi;
167     }
168 
169     /**
170      * {@inheritDoc}
171      */
172     @Override
getAbi()173     public IAbi getAbi() {
174         return mAbi;
175     }
176 
177     /**
178      * Push the required adapter binary to device.
179      *
180      * @param device device object.
181      * @param bitness ABI bitness.
182      * @throws DeviceNotAvailableException.
183      * @throws IOException.
184      * @throws NoSuchElementException.
185      */
pushAdapter(ITestDevice device, String bitness)186     private void pushAdapter(ITestDevice device, String bitness)
187             throws DeviceNotAvailableException, IOException, NoSuchElementException {
188         VtsCompatibilityInvocationHelper invocationHelper = createVtsHelper();
189         File adapterDir = new File(
190                 invocationHelper.getTestsDir(), String.format(VTS_NATIVE_TEST_DIR, bitness));
191         File adapter = FileUtil.findFile(adapterDir, mAdapterBinaryName);
192         if (adapter != null) {
193             CLog.d("Pushing %s", mAdapterBinaryName);
194             device.pushFile(
195                     adapter, String.format(TARGET_NATIVE_TEST_DIR, bitness) + mAdapterBinaryName);
196         } else {
197             throw new NoSuchElementException("Could not find adapter: " + mAdapterBinaryName);
198         }
199     }
200 
201     /**
202      * Create and return a {@link VtsCompatibilityInvocationHelper} to use during the preparer.
203      */
204     @VisibleForTesting
createVtsHelper()205     VtsCompatibilityInvocationHelper createVtsHelper() {
206         return new VtsCompatibilityInvocationHelper();
207     }
208 
209     @VisibleForTesting
setCmdUtil(CmdUtil cmdUtil)210     void setCmdUtil(CmdUtil cmdUtil) {
211         mCmdUtil = cmdUtil;
212     }
213 
214     @VisibleForTesting
addCommand(String command)215     void addCommand(String command) {
216         mCommands.add(command);
217     }
218 }
219