1 /* 2 * Copyright (C) 2016 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 static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertNull; 21 import static org.junit.Assert.fail; 22 23 import android.platform.test.annotations.AppModeFull; 24 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 25 import com.android.tradefed.log.LogUtil.CLog; 26 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 27 28 import org.junit.After; 29 import org.junit.Before; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 38 /** 39 * Tests for ephemeral packages. 40 */ 41 @RunWith(DeviceJUnit4ClassRunner.class) 42 @AppModeFull(reason = "Already handles instant installs when needed") 43 public class EphemeralTest extends BaseAppSecurityTest { 44 45 // a normally installed application 46 private static final String NORMAL_APK = "CtsEphemeralTestsNormalApp.apk"; 47 private static final String NORMAL_PKG = "com.android.cts.normalapp"; 48 49 // the first ephemerally installed application 50 private static final String EPHEMERAL_1_APK = "CtsEphemeralTestsEphemeralApp1.apk"; 51 private static final String EPHEMERAL_1_PKG = "com.android.cts.ephemeralapp1"; 52 53 // the second ephemerally installed application 54 private static final String EPHEMERAL_2_APK = "CtsEphemeralTestsEphemeralApp2.apk"; 55 private static final String EPHEMERAL_2_PKG = "com.android.cts.ephemeralapp2"; 56 57 // a normally installed application with implicitly exposed components 58 private static final String IMPLICIT_APK = "CtsEphemeralTestsImplicitApp.apk"; 59 private static final String IMPLICIT_PKG = "com.android.cts.implicitapp"; 60 61 // a normally installed application with no exposed components 62 private static final String UNEXPOSED_APK = "CtsEphemeralTestsUnexposedApp.apk"; 63 private static final String UNEXPOSED_PKG = "com.android.cts.unexposedapp"; 64 65 // an application that gets upgraded from 'instant' to 'full' 66 private static final String UPGRADED_APK = "CtsInstantUpgradeApp.apk"; 67 private static final String UPGRADED_PKG = "com.android.cts.instantupgradeapp"; 68 69 private static final String TEST_CLASS = ".ClientTest"; 70 private static final String WEBVIEW_TEST_CLASS = ".WebViewTest"; 71 72 private static final List<Map<String, String>> EXPECTED_EXPOSED_INTENTS = 73 new ArrayList<>(); 74 static { 75 // Framework intents we expect the system to expose to instant applications 76 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 77 "android.intent.action.CHOOSER", 78 null, null)); 79 // Contact intents we expect the system to expose to instant applications 80 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 81 "android.intent.action.PICK", null, "vnd.android.cursor.dir/contact")); 82 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 83 "android.intent.action.PICK", null, "vnd.android.cursor.dir/phone_v2")); 84 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 85 "android.intent.action.PICK", null, "vnd.android.cursor.dir/email_v2")); 86 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 87 "android.intent.action.PICK", null, "vnd.android.cursor.dir/postal-address_v2")); 88 // Storage intents we expect the system to expose to instant applications 89 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 90 "android.intent.action.OPEN_DOCUMENT", 91 "android.intent.category.OPENABLE", "\\*/\\*")); 92 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 93 "android.intent.action.OPEN_DOCUMENT", null, "\\*/\\*")); 94 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 95 "android.intent.action.GET_CONTENT", 96 "android.intent.category.OPENABLE", "\\*/\\*")); 97 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 98 "android.intent.action.GET_CONTENT", null, "\\*/\\*")); 99 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 100 "android.intent.action.OPEN_DOCUMENT_TREE", null, null)); 101 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 102 "android.intent.action.CREATE_DOCUMENT", 103 "android.intent.category.OPENABLE", "text/plain")); 104 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 105 "android.intent.action.CREATE_DOCUMENT", null, "text/plain")); 106 /** Camera intents we expect the system to expose to instant applications */ 107 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 108 "android.media.action.IMAGE_CAPTURE", null, null)); 109 EXPECTED_EXPOSED_INTENTS.add(makeArgs( 110 "android.media.action.VIDEO_CAPTURE", null, null)); 111 } 112 113 private String mOldVerifierValue; 114 private Boolean mIsUnsupportedDevice; 115 116 @Before setUp()117 public void setUp() throws Exception { 118 mIsUnsupportedDevice = isDeviceUnsupported(); 119 if (mIsUnsupportedDevice) { 120 return; 121 } 122 123 Utils.prepareSingleUser(getDevice()); 124 assertNotNull(getAbi()); 125 assertNotNull(getBuild()); 126 127 uninstallTestPackages(); 128 installTestPackages(); 129 } 130 131 @After tearDown()132 public void tearDown() throws Exception { 133 if (mIsUnsupportedDevice) { 134 return; 135 } 136 uninstallTestPackages(); 137 } 138 139 @Test testNormalQuery()140 public void testNormalQuery() throws Exception { 141 if (mIsUnsupportedDevice) { 142 return; 143 } 144 Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS, "testQuery"); 145 } 146 147 @Test testNormalStartNormal()148 public void testNormalStartNormal() throws Exception { 149 if (mIsUnsupportedDevice) { 150 return; 151 } 152 Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS, "testStartNormal"); 153 } 154 155 @Test testNormalStartEphemeral()156 public void testNormalStartEphemeral() throws Exception { 157 if (mIsUnsupportedDevice) { 158 return; 159 } 160 Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS, 161 "testStartEphemeral"); 162 } 163 164 @Test testEphemeralQuery()165 public void testEphemeralQuery() throws Exception { 166 if (mIsUnsupportedDevice) { 167 return; 168 } 169 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, "testQuery"); 170 } 171 172 @Test testEphemeralStartNormal()173 public void testEphemeralStartNormal() throws Exception { 174 if (mIsUnsupportedDevice) { 175 return; 176 } 177 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 178 "testStartNormal"); 179 } 180 181 // each connection to an exposed component needs to run in its own test to 182 // avoid sharing state. once an instant app is exposed to a component, it's 183 // exposed until the device restarts or the instant app is removed. 184 @Test testEphemeralStartExposed01()185 public void testEphemeralStartExposed01() throws Exception { 186 if (mIsUnsupportedDevice) { 187 return; 188 } 189 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 190 "testStartExposed01"); 191 } 192 @Test testEphemeralStartExposed02()193 public void testEphemeralStartExposed02() throws Exception { 194 if (mIsUnsupportedDevice) { 195 return; 196 } 197 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 198 "testStartExposed02"); 199 } 200 @Test testEphemeralStartExposed03()201 public void testEphemeralStartExposed03() throws Exception { 202 if (mIsUnsupportedDevice) { 203 return; 204 } 205 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 206 "testStartExposed03"); 207 } 208 @Test testEphemeralStartExposed04()209 public void testEphemeralStartExposed04() throws Exception { 210 if (mIsUnsupportedDevice) { 211 return; 212 } 213 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 214 "testStartExposed04"); 215 } 216 @Test testEphemeralStartExposed05()217 public void testEphemeralStartExposed05() throws Exception { 218 if (mIsUnsupportedDevice) { 219 return; 220 } 221 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 222 "testStartExposed05"); 223 } 224 @Test testEphemeralStartExposed06()225 public void testEphemeralStartExposed06() throws Exception { 226 if (mIsUnsupportedDevice) { 227 return; 228 } 229 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 230 "testStartExposed06"); 231 } 232 @Test testEphemeralStartExposed07()233 public void testEphemeralStartExposed07() throws Exception { 234 if (mIsUnsupportedDevice) { 235 return; 236 } 237 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 238 "testStartExposed07"); 239 } 240 @Test testEphemeralStartExposed08()241 public void testEphemeralStartExposed08() throws Exception { 242 if (mIsUnsupportedDevice) { 243 return; 244 } 245 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 246 "testStartExposed08"); 247 } 248 @Test testEphemeralStartExposed09()249 public void testEphemeralStartExposed09() throws Exception { 250 if (mIsUnsupportedDevice) { 251 return; 252 } 253 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 254 "testStartExposed09"); 255 } 256 @Test testEphemeralStartExposed10()257 public void testEphemeralStartExposed10() throws Exception { 258 if (mIsUnsupportedDevice) { 259 return; 260 } 261 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 262 "testStartExposed10"); 263 } 264 265 @Test testEphemeralStartEphemeral()266 public void testEphemeralStartEphemeral() throws Exception { 267 if (mIsUnsupportedDevice) { 268 return; 269 } 270 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 271 "testStartEphemeral"); 272 } 273 274 @Test testEphemeralGetInstaller01()275 public void testEphemeralGetInstaller01() throws Exception { 276 if (mIsUnsupportedDevice) { 277 return; 278 } 279 installEphemeralApp(EPHEMERAL_1_APK, "com.android.cts.normalapp"); 280 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 281 "testGetInstaller01"); 282 } 283 @Test testEphemeralGetInstaller02()284 public void testEphemeralGetInstaller02() throws Exception { 285 if (mIsUnsupportedDevice) { 286 return; 287 } 288 installApp(NORMAL_APK, "com.android.cts.normalapp"); 289 installEphemeralApp(EPHEMERAL_1_APK, "com.android.cts.normalapp"); 290 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 291 "testGetInstaller02"); 292 } 293 @Test testEphemeralGetInstaller03()294 public void testEphemeralGetInstaller03() throws Exception { 295 if (mIsUnsupportedDevice) { 296 return; 297 } 298 installApp(NORMAL_APK, "com.android.cts.normalapp"); 299 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 300 "testGetInstaller03"); 301 } 302 303 @Test testExposedSystemActivities()304 public void testExposedSystemActivities() throws Exception { 305 if (mIsUnsupportedDevice) { 306 return; 307 } 308 for (Map<String, String> testArgs : EXPECTED_EXPOSED_INTENTS) { 309 final boolean exposed = isIntentExposed(testArgs); 310 if (exposed) { 311 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 312 "testExposedActivity", testArgs); 313 } else { 314 CLog.w("Skip intent; " + dumpArgs(testArgs)); 315 } 316 } 317 } 318 319 @Test testBuildSerialUnknown()320 public void testBuildSerialUnknown() throws Exception { 321 if (mIsUnsupportedDevice) { 322 return; 323 } 324 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 325 "testBuildSerialUnknown"); 326 } 327 328 @Test testPackageInfo()329 public void testPackageInfo() throws Exception { 330 if (mIsUnsupportedDevice) { 331 return; 332 } 333 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 334 "testPackageInfo"); 335 } 336 337 @Test testActivityInfo()338 public void testActivityInfo() throws Exception { 339 if (mIsUnsupportedDevice) { 340 return; 341 } 342 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 343 "testActivityInfo"); 344 } 345 346 @Test testWebViewLoads()347 public void testWebViewLoads() throws Exception { 348 if (mIsUnsupportedDevice) { 349 return; 350 } 351 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, WEBVIEW_TEST_CLASS, 352 "testWebViewLoads"); 353 } 354 355 @Test testInstallPermissionNotGranted()356 public void testInstallPermissionNotGranted() throws Exception { 357 if (mIsUnsupportedDevice) { 358 return; 359 } 360 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 361 "testInstallPermissionNotGranted"); 362 } 363 364 @Test testInstallPermissionGranted()365 public void testInstallPermissionGranted() throws Exception { 366 if (mIsUnsupportedDevice) { 367 return; 368 } 369 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 370 "testInstallPermissionGranted"); 371 } 372 373 /** Test for android.permission.INSTANT_APP_FOREGROUND_SERVICE */ 374 @Test testStartForegroundService()375 public void testStartForegroundService() throws Exception { 376 if (mIsUnsupportedDevice) { 377 return; 378 } 379 // Make sure the test package does not have INSTANT_APP_FOREGROUND_SERVICE 380 getDevice().executeShellCommand("cmd package revoke " + EPHEMERAL_1_PKG 381 + " android.permission.INSTANT_APP_FOREGROUND_SERVICE"); 382 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 383 "testStartForegroundService"); 384 } 385 386 /** Test for android.permission.RECORD_AUDIO */ 387 @Test testRecordAudioPermission()388 public void testRecordAudioPermission() throws Exception { 389 if (mIsUnsupportedDevice) { 390 return; 391 } 392 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 393 "testRecordAudioPermission"); 394 } 395 396 /** Test for android.permission.CAMERA */ 397 @Test testCameraPermission()398 public void testCameraPermission() throws Exception { 399 if (mIsUnsupportedDevice) { 400 return; 401 } 402 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 403 "testCameraPermission"); 404 } 405 406 /** Test for android.permission.READ_PHONE_NUMBERS */ 407 @Test testReadPhoneNumbersPermission()408 public void testReadPhoneNumbersPermission() throws Exception { 409 if (mIsUnsupportedDevice) { 410 return; 411 } 412 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 413 "testReadPhoneNumbersPermission"); 414 } 415 416 /** Test for android.permission.ACCESS_COARSE_LOCATION */ 417 @Test testAccessCoarseLocationPermission()418 public void testAccessCoarseLocationPermission() throws Throwable { 419 if (mIsUnsupportedDevice) { 420 return; 421 } 422 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 423 "testAccessCoarseLocationPermission"); 424 } 425 426 /** Test for android.permission.NETWORK */ 427 @Test testInternetPermission()428 public void testInternetPermission() throws Throwable { 429 if (mIsUnsupportedDevice) { 430 return; 431 } 432 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 433 "testInternetPermission"); 434 } 435 436 /** Test for android.permission.VIBRATE */ 437 @Test testVibratePermission()438 public void testVibratePermission() throws Throwable { 439 if (mIsUnsupportedDevice) { 440 return; 441 } 442 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 443 "testVibratePermission"); 444 } 445 446 /** Test for android.permission.WAKE_LOCK */ 447 @Test testWakeLockPermission()448 public void testWakeLockPermission() throws Throwable { 449 if (mIsUnsupportedDevice) { 450 return; 451 } 452 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 453 "testWakeLockPermission"); 454 } 455 456 /** Test for search manager */ 457 @Test testGetSearchableInfo()458 public void testGetSearchableInfo() throws Throwable { 459 if (mIsUnsupportedDevice) { 460 return; 461 } 462 Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, 463 "testGetSearchableInfo"); 464 } 465 466 /** Test for upgrade from instant --> full */ 467 @Test testInstantAppUpgrade()468 public void testInstantAppUpgrade() throws Throwable { 469 if (mIsUnsupportedDevice) { 470 return; 471 } 472 installEphemeralApp(UPGRADED_APK); 473 Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS, 474 "testInstantApplicationWritePreferences"); 475 Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS, 476 "testInstantApplicationWriteFile"); 477 installFullApp(UPGRADED_APK); 478 Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS, 479 "testFullApplicationReadPreferences"); 480 Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS, 481 "testFullApplicationReadFile"); 482 } 483 makeArgs( String action, String category, String mimeType)484 private static final HashMap<String, String> makeArgs( 485 String action, String category, String mimeType) { 486 if (action == null || action.length() == 0) { 487 fail("action not specified"); 488 } 489 final HashMap<String, String> testArgs = new HashMap<>(); 490 testArgs.put("action", action); 491 if (category != null && category.length() > 0) { 492 testArgs.put("category", category); 493 } 494 if (mimeType != null && mimeType.length() > 0) { 495 testArgs.put("mime_type", mimeType); 496 } 497 return testArgs; 498 } 499 500 private static final String[] sUnsupportedFeatures = { 501 "feature:android.hardware.type.watch", 502 "android.hardware.type.embedded", 503 }; isDeviceUnsupported()504 private boolean isDeviceUnsupported() throws Exception { 505 for (String unsupportedFeature : sUnsupportedFeatures) { 506 if (getDevice().hasFeature(unsupportedFeature)) { 507 return true; 508 } 509 } 510 return false; 511 } 512 isIntentExposed(Map<String, String> testArgs)513 private boolean isIntentExposed(Map<String, String> testArgs) 514 throws Exception { 515 final StringBuffer command = new StringBuffer(); 516 command.append("cmd package query-activities"); 517 command.append(" -a ").append(testArgs.get("action")); 518 if (testArgs.get("category") != null) { 519 command.append(" -c ").append(testArgs.get("category")); 520 } 521 if (testArgs.get("mime_type") != null) { 522 command.append(" -t ").append(testArgs.get("mime_type")); 523 } 524 final String output = getDevice().executeShellCommand(command.toString()).trim(); 525 return !"No activities found".equals(output); 526 } 527 dumpArgs(Map<String, String> testArgs)528 private static final String dumpArgs(Map<String, String> testArgs) { 529 final StringBuffer dump = new StringBuffer(); 530 dump.append("action: ").append(testArgs.get("action")); 531 if (testArgs.get("category") != null) { 532 dump.append(", category: ").append(testArgs.get("category")); 533 } 534 if (testArgs.get("mime_type") != null) { 535 dump.append(", type: ").append(testArgs.get("mime_type")); 536 } 537 return dump.toString(); 538 } 539 installApp(String apk)540 private void installApp(String apk) throws Exception { 541 new InstallMultiple(false /* instant */) 542 .addApk(apk) 543 .run(); 544 } 545 installApp(String apk, String installer)546 private void installApp(String apk, String installer) throws Exception { 547 new InstallMultiple(false /* instant */) 548 .addApk(apk) 549 .addArg("-i " + installer) 550 .run(); 551 } 552 installEphemeralApp(String apk)553 private void installEphemeralApp(String apk) throws Exception { 554 new InstallMultiple(true /* instant */) 555 .addApk(apk) 556 .run(); 557 } 558 installEphemeralApp(String apk, String installer)559 private void installEphemeralApp(String apk, String installer) throws Exception { 560 new InstallMultiple(true /* instant */) 561 .addApk(apk) 562 .addArg("-i " + installer) 563 .run(); 564 } 565 installFullApp(String apk)566 private void installFullApp(String apk) throws Exception { 567 new InstallMultiple(false /* instant */) 568 .addApk(apk) 569 .addArg("--full") 570 .run(); 571 } 572 installTestPackages()573 private void installTestPackages() throws Exception { 574 installApp(NORMAL_APK); 575 installApp(UNEXPOSED_APK); 576 installApp(IMPLICIT_APK); 577 578 installEphemeralApp(EPHEMERAL_1_APK); 579 installEphemeralApp(EPHEMERAL_2_APK); 580 } 581 uninstallTestPackages()582 private void uninstallTestPackages() throws Exception { 583 uninstallPackage(NORMAL_PKG); 584 uninstallPackage(UNEXPOSED_PKG); 585 uninstallPackage(IMPLICIT_PKG); 586 uninstallPackage(EPHEMERAL_1_PKG); 587 uninstallPackage(EPHEMERAL_2_PKG); 588 uninstallPackage(UPGRADED_PKG); 589 } 590 } 591