1 /* 2 * Copyright (C) 2014 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 android.appsecurity.cts; 18 19 import com.android.compatibility.common.util.AbiUtils; 20 import com.android.cts.migration.MigrationHelper; 21 import com.android.tradefed.build.IBuildInfo; 22 import com.android.tradefed.device.DeviceNotAvailableException; 23 import com.android.tradefed.device.ITestDevice; 24 import com.android.tradefed.testtype.DeviceTestCase; 25 import com.android.tradefed.testtype.IAbi; 26 import com.android.tradefed.testtype.IAbiReceiver; 27 import com.android.tradefed.testtype.IBuildReceiver; 28 29 import java.io.File; 30 import java.io.FileNotFoundException; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 import java.util.List; 34 35 /** 36 * Tests that verify installing of various split APKs from host side. 37 */ 38 public class SplitTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver { 39 static final String PKG_NO_RESTART = "com.android.cts.norestart"; 40 static final String APK_NO_RESTART_BASE = "CtsNoRestartBase.apk"; 41 static final String APK_NO_RESTART_FEATURE = "CtsNoRestartFeature.apk"; 42 43 static final String PKG = "com.android.cts.splitapp"; 44 static final String CLASS = ".SplitAppTest"; 45 46 static final String APK = "CtsSplitApp.apk"; 47 48 static final String APK_mdpi = "CtsSplitApp_mdpi-v4.apk"; 49 static final String APK_hdpi = "CtsSplitApp_hdpi-v4.apk"; 50 static final String APK_xhdpi = "CtsSplitApp_xhdpi-v4.apk"; 51 static final String APK_xxhdpi = "CtsSplitApp_xxhdpi-v4.apk"; 52 53 private static final String APK_v7 = "CtsSplitApp_v7.apk"; 54 private static final String APK_fr = "CtsSplitApp_fr.apk"; 55 private static final String APK_de = "CtsSplitApp_de.apk"; 56 57 private static final String APK_x86 = "CtsSplitApp_x86.apk"; 58 private static final String APK_x86_64 = "CtsSplitApp_x86_64.apk"; 59 private static final String APK_armeabi_v7a = "CtsSplitApp_armeabi-v7a.apk"; 60 private static final String APK_armeabi = "CtsSplitApp_armeabi.apk"; 61 private static final String APK_arm64_v8a = "CtsSplitApp_arm64-v8a.apk"; 62 private static final String APK_mips64 = "CtsSplitApp_mips64.apk"; 63 private static final String APK_mips = "CtsSplitApp_mips.apk"; 64 65 private static final String APK_DIFF_REVISION = "CtsSplitAppDiffRevision.apk"; 66 private static final String APK_DIFF_REVISION_v7 = "CtsSplitAppDiffRevision_v7.apk"; 67 68 private static final String APK_DIFF_VERSION = "CtsSplitAppDiffVersion.apk"; 69 private static final String APK_DIFF_VERSION_v7 = "CtsSplitAppDiffVersion_v7.apk"; 70 71 private static final String APK_DIFF_CERT = "CtsSplitAppDiffCert.apk"; 72 private static final String APK_DIFF_CERT_v7 = "CtsSplitAppDiffCert_v7.apk"; 73 74 private static final String APK_FEATURE = "CtsSplitAppFeature.apk"; 75 private static final String APK_FEATURE_v7 = "CtsSplitAppFeature_v7.apk"; 76 77 static final HashMap<String, String> ABI_TO_APK = new HashMap<>(); 78 79 static { 80 ABI_TO_APK.put("x86", APK_x86); 81 ABI_TO_APK.put("x86_64", APK_x86_64); 82 ABI_TO_APK.put("armeabi-v7a", APK_armeabi_v7a); 83 ABI_TO_APK.put("armeabi", APK_armeabi); 84 ABI_TO_APK.put("arm64-v8a", APK_arm64_v8a); 85 ABI_TO_APK.put("mips64", APK_mips64); 86 ABI_TO_APK.put("mips", APK_mips); 87 } 88 89 private IAbi mAbi; 90 private IBuildInfo mCtsBuild; 91 92 @Override setAbi(IAbi abi)93 public void setAbi(IAbi abi) { 94 mAbi = abi; 95 } 96 97 @Override setBuild(IBuildInfo buildInfo)98 public void setBuild(IBuildInfo buildInfo) { 99 mCtsBuild = buildInfo; 100 } 101 102 @Override setUp()103 protected void setUp() throws Exception { 104 super.setUp(); 105 106 assertNotNull(mAbi); 107 assertNotNull(mCtsBuild); 108 109 getDevice().uninstallPackage(PKG); 110 } 111 112 @Override tearDown()113 protected void tearDown() throws Exception { 114 super.tearDown(); 115 116 getDevice().uninstallPackage(PKG); 117 getDevice().uninstallPackage(PKG_NO_RESTART); 118 } 119 testSingleBase()120 public void testSingleBase() throws Exception { 121 new InstallMultiple().addApk(APK).run(); 122 runDeviceTests(PKG, CLASS, "testSingleBase"); 123 } 124 testDensitySingle()125 public void testDensitySingle() throws Exception { 126 new InstallMultiple().addApk(APK).addApk(APK_mdpi).run(); 127 runDeviceTests(PKG, CLASS, "testDensitySingle"); 128 } 129 testDensityAll()130 public void testDensityAll() throws Exception { 131 new InstallMultiple().addApk(APK).addApk(APK_mdpi).addApk(APK_hdpi).addApk(APK_xhdpi) 132 .addApk(APK_xxhdpi).run(); 133 runDeviceTests(PKG, CLASS, "testDensityAll"); 134 } 135 136 /** 137 * Install first with low-resolution resources, then add a split that offers 138 * higher-resolution resources. 139 */ testDensityBest()140 public void testDensityBest() throws Exception { 141 new InstallMultiple().addApk(APK).addApk(APK_mdpi).run(); 142 runDeviceTests(PKG, CLASS, "testDensityBest1"); 143 144 // Now splice in an additional split which offers better resources 145 new InstallMultiple().inheritFrom(PKG).addApk(APK_xxhdpi).run(); 146 runDeviceTests(PKG, CLASS, "testDensityBest2"); 147 } 148 149 /** 150 * Verify that an API-based split can change enabled/disabled state of 151 * manifest elements. 152 */ testApi()153 public void testApi() throws Exception { 154 new InstallMultiple().addApk(APK).addApk(APK_v7).run(); 155 runDeviceTests(PKG, CLASS, "testApi"); 156 } 157 testLocale()158 public void testLocale() throws Exception { 159 new InstallMultiple().addApk(APK).addApk(APK_de).addApk(APK_fr).run(); 160 runDeviceTests(PKG, CLASS, "testLocale"); 161 } 162 163 /** 164 * Install test app with <em>single</em> split that exactly matches the 165 * currently active ABI. This also explicitly forces ABI when installing. 166 */ testNativeSingle()167 public void testNativeSingle() throws Exception { 168 final String abi = mAbi.getName(); 169 final String apk = ABI_TO_APK.get(abi); 170 assertNotNull("Failed to find APK for ABI " + abi, apk); 171 172 new InstallMultiple().addApk(APK).addApk(apk).run(); 173 runDeviceTests(PKG, CLASS, "testNative"); 174 } 175 176 /** 177 * Install test app with <em>single</em> split that exactly matches the 178 * currently active ABI. This variant <em>does not</em> force the ABI when 179 * installing, instead exercising the system's ability to choose the ABI 180 * through inspection of the installed app. 181 */ testNativeSingleNatural()182 public void testNativeSingleNatural() throws Exception { 183 final String abi = mAbi.getName(); 184 final String apk = ABI_TO_APK.get(abi); 185 assertNotNull("Failed to find APK for ABI " + abi, apk); 186 187 new InstallMultiple().useNaturalAbi().addApk(APK).addApk(apk).run(); 188 runDeviceTests(PKG, CLASS, "testNative"); 189 } 190 191 /** 192 * Install test app with <em>all</em> possible ABI splits. This also 193 * explicitly forces ABI when installing. 194 */ testNativeAll()195 public void testNativeAll() throws Exception { 196 final InstallMultiple inst = new InstallMultiple().addApk(APK); 197 for (String apk : ABI_TO_APK.values()) { 198 inst.addApk(apk); 199 } 200 inst.run(); 201 runDeviceTests(PKG, CLASS, "testNative"); 202 } 203 204 /** 205 * Install test app with <em>all</em> possible ABI splits. This variant 206 * <em>does not</em> force the ABI when installing, instead exercising the 207 * system's ability to choose the ABI through inspection of the installed 208 * app. 209 */ testNativeAllNatural()210 public void testNativeAllNatural() throws Exception { 211 final InstallMultiple inst = new InstallMultiple().useNaturalAbi().addApk(APK); 212 for (String apk : ABI_TO_APK.values()) { 213 inst.addApk(apk); 214 } 215 inst.run(); 216 runDeviceTests(PKG, CLASS, "testNative"); 217 } 218 testDuplicateBase()219 public void testDuplicateBase() throws Exception { 220 new InstallMultiple().addApk(APK).addApk(APK).runExpectingFailure(); 221 } 222 testDuplicateSplit()223 public void testDuplicateSplit() throws Exception { 224 new InstallMultiple().addApk(APK).addApk(APK_v7).addApk(APK_v7).runExpectingFailure(); 225 } 226 testDiffCert()227 public void testDiffCert() throws Exception { 228 new InstallMultiple().addApk(APK).addApk(APK_DIFF_CERT_v7).runExpectingFailure(); 229 } 230 testDiffCertInherit()231 public void testDiffCertInherit() throws Exception { 232 new InstallMultiple().addApk(APK).run(); 233 new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_CERT_v7).runExpectingFailure(); 234 } 235 testDiffVersion()236 public void testDiffVersion() throws Exception { 237 new InstallMultiple().addApk(APK).addApk(APK_DIFF_VERSION_v7).runExpectingFailure(); 238 } 239 testDiffVersionInherit()240 public void testDiffVersionInherit() throws Exception { 241 new InstallMultiple().addApk(APK).run(); 242 new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure(); 243 } 244 testDiffRevision()245 public void testDiffRevision() throws Exception { 246 new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run(); 247 runDeviceTests(PKG, CLASS, "testRevision0_12"); 248 } 249 testDiffRevisionInheritBase()250 public void testDiffRevisionInheritBase() throws Exception { 251 new InstallMultiple().addApk(APK).addApk(APK_v7).run(); 252 runDeviceTests(PKG, CLASS, "testRevision0_0"); 253 new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7).run(); 254 runDeviceTests(PKG, CLASS, "testRevision0_12"); 255 } 256 testDiffRevisionInheritSplit()257 public void testDiffRevisionInheritSplit() throws Exception { 258 new InstallMultiple().addApk(APK).addApk(APK_v7).run(); 259 runDeviceTests(PKG, CLASS, "testRevision0_0"); 260 new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION).run(); 261 runDeviceTests(PKG, CLASS, "testRevision12_0"); 262 } 263 testDiffRevisionDowngrade()264 public void testDiffRevisionDowngrade() throws Exception { 265 new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run(); 266 new InstallMultiple().inheritFrom(PKG).addApk(APK_v7).runExpectingFailure(); 267 } 268 testFeatureBase()269 public void testFeatureBase() throws Exception { 270 new InstallMultiple().addApk(APK).addApk(APK_FEATURE).run(); 271 runDeviceTests(PKG, CLASS, "testFeatureBase"); 272 } 273 testFeatureApi()274 public void testFeatureApi() throws Exception { 275 new InstallMultiple().addApk(APK).addApk(APK_FEATURE).addApk(APK_FEATURE_v7).run(); 276 runDeviceTests(PKG, CLASS, "testFeatureApi"); 277 } 278 testInheritUpdatedBase()279 public void testInheritUpdatedBase() throws Exception { 280 // TODO: flesh out this test 281 } 282 testInheritUpdatedSplit()283 public void testInheritUpdatedSplit() throws Exception { 284 // TODO: flesh out this test 285 } 286 testFeatureWithoutRestart()287 public void testFeatureWithoutRestart() throws Exception { 288 new InstallMultiple().addApk(APK).run(); 289 new InstallMultiple().addApk(APK_NO_RESTART_BASE).run(); 290 runDeviceTests(PKG, CLASS, "testBaseInstalled"); 291 new InstallMultiple() 292 .addArg("--dont-kill") 293 .inheritFrom(PKG_NO_RESTART) 294 .addApk(APK_NO_RESTART_FEATURE) 295 .run(); 296 runDeviceTests(PKG, CLASS, "testFeatureInstalled"); 297 } 298 299 /** 300 * Verify that installing a new version of app wipes code cache. 301 */ testClearCodeCache()302 public void testClearCodeCache() throws Exception { 303 new InstallMultiple().addApk(APK).run(); 304 runDeviceTests(PKG, CLASS, "testCodeCacheWrite"); 305 new InstallMultiple().addArg("-r").addApk(APK_DIFF_VERSION).run(); 306 runDeviceTests(PKG, CLASS, "testCodeCacheRead"); 307 } 308 309 private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> { InstallMultiple()310 public InstallMultiple() { 311 super(getDevice(), mCtsBuild, mAbi); 312 } 313 } 314 315 public static class BaseInstallMultiple<T extends BaseInstallMultiple<?>> { 316 private final ITestDevice mDevice; 317 private final IBuildInfo mBuild; 318 private final IAbi mAbi; 319 320 private final List<String> mArgs = new ArrayList<>(); 321 private final List<File> mApks = new ArrayList<>(); 322 private boolean mUseNaturalAbi; 323 BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi)324 public BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi) { 325 mDevice = device; 326 mBuild = buildInfo; 327 mAbi = abi; 328 addArg("-g"); 329 } 330 addArg(String arg)331 T addArg(String arg) { 332 mArgs.add(arg); 333 return (T) this; 334 } 335 addApk(String apk)336 T addApk(String apk) throws FileNotFoundException { 337 mApks.add(MigrationHelper.getTestFile(mBuild, apk)); 338 return (T) this; 339 } 340 inheritFrom(String packageName)341 T inheritFrom(String packageName) { 342 addArg("-r"); 343 addArg("-p " + packageName); 344 return (T) this; 345 } 346 useNaturalAbi()347 T useNaturalAbi() { 348 mUseNaturalAbi = true; 349 return (T) this; 350 } 351 locationAuto()352 T locationAuto() { 353 addArg("--install-location 0"); 354 return (T) this; 355 } 356 locationInternalOnly()357 T locationInternalOnly() { 358 addArg("--install-location 1"); 359 return (T) this; 360 } 361 locationPreferExternal()362 T locationPreferExternal() { 363 addArg("--install-location 2"); 364 return (T) this; 365 } 366 forceUuid(String uuid)367 T forceUuid(String uuid) { 368 addArg("--force-uuid " + uuid); 369 return (T) this; 370 } 371 run()372 void run() throws DeviceNotAvailableException { 373 run(true); 374 } 375 runExpectingFailure()376 void runExpectingFailure() throws DeviceNotAvailableException { 377 run(false); 378 } 379 run(boolean expectingSuccess)380 private void run(boolean expectingSuccess) throws DeviceNotAvailableException { 381 final ITestDevice device = mDevice; 382 383 // Create an install session 384 final StringBuilder cmd = new StringBuilder(); 385 cmd.append("pm install-create"); 386 for (String arg : mArgs) { 387 cmd.append(' ').append(arg); 388 } 389 if (!mUseNaturalAbi) { 390 cmd.append(' ').append(AbiUtils.createAbiFlag(mAbi.getName())); 391 } 392 393 String result = device.executeShellCommand(cmd.toString()); 394 assertTrue(result, result.startsWith("Success")); 395 396 final int start = result.lastIndexOf("["); 397 final int end = result.lastIndexOf("]"); 398 int sessionId = -1; 399 try { 400 if (start != -1 && end != -1 && start < end) { 401 sessionId = Integer.parseInt(result.substring(start + 1, end)); 402 } 403 } catch (NumberFormatException e) { 404 } 405 if (sessionId == -1) { 406 throw new IllegalStateException("Failed to create install session: " + result); 407 } 408 409 // Push our files into session. Ideally we'd use stdin streaming, 410 // but ddmlib doesn't support it yet. 411 for (int i = 0; i < mApks.size(); i++) { 412 final File apk = mApks.get(i); 413 final String remotePath = "/data/local/tmp/" + i + "_" + apk.getName(); 414 if (!device.pushFile(apk, remotePath)) { 415 throw new IllegalStateException("Failed to push " + apk); 416 } 417 418 cmd.setLength(0); 419 cmd.append("pm install-write"); 420 cmd.append(' ').append(sessionId); 421 cmd.append(' ').append(i + "_" + apk.getName()); 422 cmd.append(' ').append(remotePath); 423 424 result = device.executeShellCommand(cmd.toString()); 425 assertTrue(result, result.startsWith("Success")); 426 } 427 428 // Everything staged; let's pull trigger 429 cmd.setLength(0); 430 cmd.append("pm install-commit"); 431 cmd.append(' ').append(sessionId); 432 433 result = device.executeShellCommand(cmd.toString()); 434 if (expectingSuccess) { 435 assertTrue(result, result.startsWith("Success")); 436 } else { 437 assertFalse(result, result.startsWith("Success")); 438 } 439 } 440 } 441 runDeviceTests(String packageName, String testClassName, String testMethodName)442 public void runDeviceTests(String packageName, String testClassName, String testMethodName) 443 throws DeviceNotAvailableException { 444 Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName); 445 } 446 } 447