1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package android.jvmti.cts; 16 17 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 18 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.config.Option; 21 import com.android.tradefed.device.ITestDevice; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.result.ITestLifeCycleReceiver; 24 import com.android.tradefed.result.TestDescription; 25 import com.android.tradefed.testtype.DeviceTestCase; 26 import com.android.tradefed.testtype.IAbi; 27 import com.android.tradefed.testtype.IAbiReceiver; 28 import com.android.tradefed.testtype.IBuildReceiver; 29 import com.android.tradefed.util.AbiUtils; 30 import com.android.tradefed.util.FileUtil; 31 import com.android.tradefed.util.ZipUtil; 32 33 import java.io.File; 34 import java.util.LinkedList; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.concurrent.TimeUnit; 38 import java.util.zip.ZipFile; 39 40 /** 41 * Test a JVMTI device test. 42 * 43 * Reads the configuration (APK and package name) out of the embedded config.properties file. Runs 44 * the agent (expected to be packaged with the APK) into the app's /data/data directory, starts a 45 * test run and attaches the agent. 46 */ 47 public class JvmtiHostTest extends DeviceTestCase implements IBuildReceiver, IAbiReceiver { 48 private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner"; 49 // inject these options from HostTest directly using --set-option <option name>:<option value> 50 @Option(name = "package-name", 51 description = "The package name of the device test", 52 mandatory = true) 53 private String mTestPackageName = null; 54 55 @Option(name = "test-file-name", 56 description = "the name of a test zip file to install on device.", 57 mandatory = true) 58 private String mTestApk = null; 59 60 @Option(name = "hidden-api-checks", 61 description = "If we should enable hidden api checks. Default 'true'. Set to 'false' " + 62 "to disable hiddenapi.", 63 mandatory = false) 64 private String mHiddenApiChecksEnabled = null; 65 66 private CompatibilityBuildHelper mBuildHelper; 67 private IAbi mAbi; 68 private int mCurrentUser; 69 70 @Override setBuild(IBuildInfo arg0)71 public void setBuild(IBuildInfo arg0) { 72 mBuildHelper = new CompatibilityBuildHelper(arg0); 73 } 74 75 @Override setAbi(IAbi arg0)76 public void setAbi(IAbi arg0) { 77 mAbi = arg0; 78 } 79 80 @Override setUp()81 protected void setUp() throws Exception { 82 mCurrentUser = getDevice().getCurrentUser(); 83 } 84 testJvmti()85 public void testJvmti() throws Exception { 86 final ITestDevice device = getDevice(); 87 88 String testingArch = AbiUtils.getBaseArchForAbi(mAbi.getName()); 89 String deviceArch = getDeviceBaseArch(device); 90 91 //Only bypass if Base Archs are different 92 if (!testingArch.equals(deviceArch)) { 93 CLog.d( 94 "Bypass as testing Base Arch:" 95 + testingArch 96 + " is different from DUT Base Arch:" 97 + deviceArch); 98 return; 99 } 100 101 if (mTestApk == null || mTestPackageName == null) { 102 throw new IllegalStateException("Incorrect configuration"); 103 } 104 105 if (null != mHiddenApiChecksEnabled && 106 !"false".equals(mHiddenApiChecksEnabled) && 107 !"true".equals(mHiddenApiChecksEnabled)) { 108 throw new IllegalStateException( 109 "option hidden-api-checks must be 'true' or 'false' if present."); 110 } 111 boolean disable_hidden_api = 112 mHiddenApiChecksEnabled != null && "false".equals(mHiddenApiChecksEnabled); 113 String old_hiddenapi_setting = null; 114 if (disable_hidden_api) { 115 old_hiddenapi_setting = device.getSetting("global", "hidden_api_policy"); 116 device.setSetting("global", "hidden_api_policy", "1"); 117 } 118 119 try { 120 RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mTestPackageName, RUNNER, 121 device.getIDevice()); 122 // set a max deadline limit to avoid hanging forever 123 runner.setMaxTimeToOutputResponse(5, TimeUnit.MINUTES); 124 125 AttachAgent aa = new AttachAgent(device, mTestPackageName, mTestApk); 126 aa.prepare(); 127 TestResults tr = new TestResults(aa); 128 129 device.runInstrumentationTests(runner, tr); 130 131 assertTrue(tr.getErrors(), tr.hasStarted()); 132 assertFalse(tr.getErrors(), tr.hasFailed()); 133 } finally { 134 if (disable_hidden_api) { 135 device.setSetting("global", "hidden_api_policy", old_hiddenapi_setting); 136 } 137 } 138 } 139 getDeviceBaseArch(ITestDevice device)140 private String getDeviceBaseArch(ITestDevice device) throws Exception { 141 String abi = device.executeShellCommand("getprop ro.product.cpu.abi").replace("\n", ""); 142 CLog.d("DUT abi:" + abi); 143 return AbiUtils.getBaseArchForAbi(abi); 144 } 145 146 private class AttachAgent implements Runnable { 147 private ITestDevice mDevice; 148 private String mPkg; 149 private String mApk; 150 151 private String mAgentInDataData; 152 AttachAgent(ITestDevice device, String pkg, String apk)153 public AttachAgent(ITestDevice device, String pkg, String apk) { 154 this.mDevice = device; 155 this.mPkg = pkg; 156 this.mApk = apk; 157 } 158 prepare()159 public void prepare() { 160 try { 161 String pwd = mDevice.executeShellCommand( 162 "run-as " + mPkg + " --user " + mCurrentUser + " pwd"); 163 if (pwd == null) { 164 throw new RuntimeException("pwd failed"); 165 } 166 pwd = pwd.trim(); 167 if (pwd.isEmpty()) { 168 throw new RuntimeException("pwd failed"); 169 } 170 171 mAgentInDataData = installLibToDataData(pwd, "libctsjvmtiagent.so"); 172 } catch (Exception e) { 173 throw new RuntimeException("Failed installing", e); 174 } 175 } 176 177 @Override run()178 public void run() { 179 try { 180 if (mAgentInDataData == null) { 181 throw new IllegalStateException("prepare() has not been called"); 182 } 183 String attachCmd = "cmd activity attach-agent " + mPkg + " " + mAgentInDataData; 184 String attachReply = mDevice.executeShellCommand(attachCmd); 185 // Don't try to parse the output. The test will time out anyways if this didn't 186 // work. 187 if (attachReply != null && !attachReply.trim().isEmpty()) { 188 CLog.e(attachReply); 189 } 190 } catch (Exception e) { 191 throw new RuntimeException("Failed attaching", e); 192 } 193 } 194 installLibToDataData(String dataData, String library)195 private String installLibToDataData(String dataData, String library) throws Exception { 196 ZipFile zf = null; 197 File tmpFile = null; 198 String libInTmp = null; 199 try { 200 String libInDataData = dataData + "/" + library; 201 202 File apkFile = mBuildHelper.getTestFile(mApk); 203 zf = new ZipFile(apkFile); 204 205 String libPathInApk = "lib/" + mAbi.getName() + "/" + library; 206 tmpFile = ZipUtil.extractFileFromZip(zf, libPathInApk); 207 208 libInTmp = "/data/local/tmp/" + tmpFile.getName(); 209 if (!mDevice.pushFile(tmpFile, libInTmp)) { 210 throw new RuntimeException("Could not push library " + library + " to device"); 211 } 212 213 String runAsCp = mDevice.executeShellCommand( 214 "run-as " + mPkg + " --user " + mCurrentUser + 215 " cp " + libInTmp + " " + libInDataData); 216 if (runAsCp != null && !runAsCp.trim().isEmpty()) { 217 throw new RuntimeException(runAsCp.trim()); 218 } 219 220 String runAsChmod = mDevice.executeShellCommand( 221 "run-as " + mPkg + " --user " + mCurrentUser + 222 " chmod a+x " + libInDataData); 223 if (runAsChmod != null && !runAsChmod.trim().isEmpty()) { 224 throw new RuntimeException(runAsChmod.trim()); 225 } 226 227 return libInDataData; 228 } finally { 229 FileUtil.deleteFile(tmpFile); 230 ZipUtil.closeZip(zf); 231 if (libInTmp != null) { 232 try { 233 mDevice.executeShellCommand("rm " + libInTmp); 234 } catch (Exception e) { 235 CLog.e("Failed cleaning up library on device"); 236 } 237 } 238 } 239 } 240 } 241 242 private static class TestResults implements ITestLifeCycleReceiver { 243 private boolean mFailed = false; 244 private boolean mStarted = false; 245 private final Runnable mOnStart; 246 private List<String> mErrors = new LinkedList<>(); 247 TestResults(Runnable onStart)248 public TestResults(Runnable onStart) { 249 this.mOnStart = onStart; 250 } 251 hasFailed()252 public boolean hasFailed() { 253 return mFailed; 254 } 255 hasStarted()256 public boolean hasStarted() { 257 return mStarted; 258 } 259 getErrors()260 public String getErrors() { 261 if (mErrors.isEmpty()) { 262 return ""; 263 } 264 return mErrors.toString(); 265 } 266 267 @Override testAssumptionFailure(TestDescription arg0, String arg1)268 public void testAssumptionFailure(TestDescription arg0, String arg1) { 269 mFailed = true; 270 mErrors.add(arg0.toString() + " " + arg1); 271 } 272 273 @Override testEnded(TestDescription arg0, Map<String, String> arg1)274 public void testEnded(TestDescription arg0, Map<String, String> arg1) {} 275 276 @Override testFailed(TestDescription arg0, String arg1)277 public void testFailed(TestDescription arg0, String arg1) { 278 mFailed = true; 279 mErrors.add(arg0.toString() + " " + arg1); 280 } 281 282 @Override testIgnored(TestDescription arg0)283 public void testIgnored(TestDescription arg0) {} 284 285 @Override testRunEnded(long arg0, Map<String, String> arg1)286 public void testRunEnded(long arg0, Map<String, String> arg1) {} 287 288 @Override testRunFailed(String arg0)289 public void testRunFailed(String arg0) { 290 mFailed = true; 291 mErrors.add(arg0); 292 } 293 294 @Override testRunStarted(String arg0, int arg1)295 public void testRunStarted(String arg0, int arg1) { 296 if (mOnStart != null) { 297 mOnStart.run(); 298 } 299 } 300 301 @Override testRunStopped(long arg0)302 public void testRunStopped(long arg0) {} 303 304 @Override testStarted(TestDescription arg0)305 public void testStarted(TestDescription arg0) { 306 mStarted = true; 307 } 308 } 309 } 310