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.NullOutputReceiver; 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.testtype.DeviceTestCase; 24 import com.android.tradefed.testtype.IAbi; 25 import com.android.tradefed.testtype.IAbiReceiver; 26 import com.android.tradefed.testtype.IBuildReceiver; 27 import com.android.tradefed.util.AbiUtils; 28 import com.android.tradefed.util.FileUtil; 29 import com.android.tradefed.util.ZipUtil; 30 31 import java.io.File; 32 import java.util.concurrent.TimeUnit; 33 import java.util.zip.ZipFile; 34 35 /** 36 * Specialization of JvmtiHostTest to test attaching on startup. 37 */ 38 public class JvmtiAttachingHostTest extends DeviceTestCase implements IBuildReceiver, IAbiReceiver { 39 // inject these options from HostTest directly using --set-option <option name>:<option value> 40 @Option(name = "package-name", 41 description = "The package name of the device test", 42 mandatory = true) 43 private String mTestPackageName = null; 44 45 @Option(name = "test-file-name", 46 description = "the name of a test zip file to install on device.", 47 mandatory = true) 48 private String mTestApk = null; 49 50 private CompatibilityBuildHelper mBuildHelper; 51 private IAbi mAbi; 52 private int mCurrentUser; 53 54 @Override 55 public void setBuild(IBuildInfo arg0) { 56 mBuildHelper = new CompatibilityBuildHelper(arg0); 57 } 58 59 @Override 60 public void setAbi(IAbi arg0) { 61 mAbi = arg0; 62 } 63 64 private static interface TestRun { 65 public void run(ITestDevice device, String pkg, String apk, String abiName); 66 } 67 68 private final static String AGENT = "libctsjvmtiattachagent.so"; 69 70 @Override 71 protected void setUp() throws Exception { 72 mCurrentUser = getDevice().getCurrentUser(); 73 } 74 75 public void testJvmtiAttachDuringBind() throws Exception { 76 runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> { 77 try { 78 runAttachTestCmd(device, pkg, "--attach-agent-bind " + AGENT); 79 } catch (Exception e) { 80 throw new RuntimeException("Failed bind-time attaching", e); 81 } 82 }); 83 } 84 85 public void testJvmtiAttachEarly() throws Exception { 86 runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> { 87 try { 88 String pwd = device.executeShellCommand( 89 "run-as " + pkg + " --user " + mCurrentUser + " pwd"); 90 if (pwd == null) { 91 throw new RuntimeException("pwd failed"); 92 } 93 pwd = pwd.trim(); 94 if (pwd.isEmpty()) { 95 throw new RuntimeException("pwd failed"); 96 } 97 98 // Give it a different name, so we do not have "contamination" from 99 // the test APK. 100 String libInDataData = AGENT.substring(0, AGENT.length() - ".so".length()) 101 + "2.so"; 102 String agentInDataData = 103 installLibToDataData(device, pkg, abiName, apk, pwd, AGENT, 104 libInDataData); 105 runAttachTestCmd(device, pkg, "--attach-agent " + agentInDataData); 106 } catch (Exception e) { 107 throw new RuntimeException("Failed pre-bind attaching", e); 108 } 109 }); 110 } 111 112 public void testJvmtiAgentAppInternal() throws Exception { 113 runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> { 114 try { 115 String setAgentAppCmd = "cmd activity set-agent-app " + pkg + " " + AGENT; 116 device.executeShellCommand(setAgentAppCmd); 117 } catch (Exception e) { 118 throw new RuntimeException("Failed running set-agent-app", e); 119 } 120 121 try { 122 runAttachTestCmd(device, pkg, ""); 123 124 // And again. 125 runAttachTestCmd(device, pkg, ""); 126 } catch (Exception e) { 127 throw new RuntimeException("Failed agent-app attaching", e); 128 } 129 }); 130 } 131 132 public void testJvmtiAgentAppExternal() throws Exception { 133 runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> { 134 try { 135 String pwd = device.executeShellCommand( 136 "run-as " + pkg + " --user " + mCurrentUser + " pwd"); 137 if (pwd == null) { 138 throw new RuntimeException("pwd failed"); 139 } 140 pwd = pwd.trim(); 141 if (pwd.isEmpty()) { 142 throw new RuntimeException("pwd failed"); 143 } 144 145 // Give it a different name, so we do not have "contamination" from 146 // the test APK. 147 String libInDataData = AGENT.substring(0, AGENT.length() - ".so".length()) 148 + "2.so"; 149 String agentInDataData = 150 installLibToDataData(device, pkg, abiName, apk, pwd, AGENT, 151 libInDataData); 152 153 String setAgentAppCmd = "cmd activity set-agent-app " + pkg + " " + agentInDataData; 154 device.executeShellCommand(setAgentAppCmd); 155 } catch (Exception e) { 156 throw new RuntimeException("Failed running set-agent-app", e); 157 } 158 159 try { 160 runAttachTestCmd(device, pkg, ""); 161 162 // And again. 163 runAttachTestCmd(device, pkg, ""); 164 } catch (Exception e) { 165 throw new RuntimeException("Failed agent-app attaching", e); 166 } 167 }); 168 } 169 170 private void runJvmtiAgentLoadTest(TestRun runner) throws Exception { 171 final ITestDevice device = getDevice(); 172 173 String testingArch = AbiUtils.getBaseArchForAbi(mAbi.getName()); 174 String deviceArch = getDeviceBaseArch(device); 175 176 //Only bypass if Base Archs are different 177 if (!testingArch.equals(deviceArch)) { 178 CLog.d( 179 "Bypass as testing Base Arch:" 180 + testingArch 181 + " is different from DUT Base Arch:" 182 + deviceArch); 183 return; 184 } 185 186 if (mTestApk == null || mTestPackageName == null) { 187 throw new IllegalStateException("Incorrect configuration"); 188 } 189 190 // Wakeup the device if it is on the lockscreen and move it to the home screen. 191 device.executeShellCommand("input keyevent KEYCODE_WAKEUP"); 192 device.executeShellCommand("wm dismiss-keyguard"); 193 device.executeShellCommand("input keyevent KEYCODE_HOME"); 194 195 runner.run(device, mTestPackageName, mTestApk, mAbi.getName()); 196 } 197 198 private String getDeviceBaseArch(ITestDevice device) throws Exception { 199 String abi = device.executeShellCommand("getprop ro.product.cpu.abi").replace("\n", ""); 200 CLog.d("DUT abi:" + abi); 201 return AbiUtils.getBaseArchForAbi(abi); 202 } 203 204 private static void runAttachTestCmd(ITestDevice device, String pkg, String agentParams) 205 throws Exception { 206 String attachCmd = "cmd activity start -S -W " + agentParams + " -n " + pkg 207 + "/android.jvmti.JvmtiActivity"; 208 209 // Don't try to parse the output. The test will time out anyways if this didn't 210 // work. 211 device.executeShellCommand(attachCmd, NullOutputReceiver.getReceiver(), 10, 212 TimeUnit.SECONDS, 1); 213 } 214 215 private String installLibToDataData(ITestDevice device, String pkg, String abiName, 216 String apk, String dataData, String library, String newLibName) throws Exception { 217 ZipFile zf = null; 218 File tmpFile = null; 219 String libInTmp = null; 220 try { 221 String libInDataData = dataData + "/" + newLibName; 222 223 File apkFile = mBuildHelper.getTestFile(apk); 224 zf = new ZipFile(apkFile); 225 226 String libPathInApk = "lib/" + abiName + "/" + library; 227 tmpFile = ZipUtil.extractFileFromZip(zf, libPathInApk); 228 229 libInTmp = "/data/local/tmp/" + tmpFile.getName(); 230 if (!device.pushFile(tmpFile, libInTmp)) { 231 throw new RuntimeException("Could not push library " + library + " to device"); 232 } 233 234 String runAsCp = device.executeShellCommand( 235 "run-as " + pkg + " --user " + mCurrentUser + 236 " cp " + libInTmp + " " + libInDataData); 237 if (runAsCp != null && !runAsCp.trim().isEmpty()) { 238 throw new RuntimeException(runAsCp.trim()); 239 } 240 241 String runAsChmod = device.executeShellCommand( 242 "run-as " + pkg + " --user " + mCurrentUser + " chmod a+x " + libInDataData); 243 if (runAsChmod != null && !runAsChmod.trim().isEmpty()) { 244 throw new RuntimeException(runAsChmod.trim()); 245 } 246 247 return libInDataData; 248 } finally { 249 FileUtil.deleteFile(tmpFile); 250 ZipUtil.closeZip(zf); 251 if (libInTmp != null) { 252 try { 253 device.executeShellCommand("rm " + libInTmp); 254 } catch (Exception e) { 255 CLog.e("Failed cleaning up library on device"); 256 } 257 } 258 } 259 } 260 } 261