1 /* 2 * Copyright (C) 2020 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.tests.atomicinstall; 18 19 import static com.android.compatibility.common.util.MatcherUtils.assertThrows; 20 import static com.android.compatibility.common.util.MatcherUtils.hasMessageThat; 21 import static com.android.compatibility.common.util.MatcherUtils.instanceOf; 22 import static com.android.cts.install.lib.InstallUtils.openPackageInstallerSession; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.hamcrest.CoreMatchers.containsString; 27 28 import android.Manifest; 29 import android.content.pm.PackageInstaller; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 33 import androidx.annotation.NonNull; 34 import androidx.core.util.Preconditions; 35 import androidx.test.ext.junit.runners.AndroidJUnit4; 36 import androidx.test.platform.app.InstrumentationRegistry; 37 38 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 39 import com.android.compatibility.common.util.PollingCheck; 40 import com.android.cts.install.lib.Install; 41 import com.android.cts.install.lib.InstallUtils; 42 import com.android.cts.install.lib.TestApp; 43 import com.android.cts.install.lib.Uninstall; 44 45 import org.junit.After; 46 import org.junit.Before; 47 import org.junit.Rule; 48 import org.junit.Test; 49 import org.junit.rules.TestName; 50 import org.junit.runner.RunWith; 51 52 import java.io.Closeable; 53 import java.io.IOException; 54 import java.io.OutputStream; 55 import java.util.ArrayList; 56 import java.util.List; 57 import java.util.concurrent.CountDownLatch; 58 import java.util.concurrent.TimeUnit; 59 60 61 /** 62 * There are the following factors need to combine for testing the abandon behavior. 63 * <ul> 64 * <li>staged vs. noStaged</li> 65 * <li>Single Package vs. MultiPackage</li> 66 * <li>Receive callback, Session Info abandoned, getNames, openWrite, open abandoned session 67 * etc.</li> 68 * </ul>* 69 */ 70 @RunWith(AndroidJUnit4.class) 71 public class SessionAbandonBehaviorTest { 72 /** 73 * Please don't change too small to ensure the test run normally. 74 */ 75 public static final int CALLBACK_TIMEOUT_SECONDS = 10; 76 77 /** 78 * To wait 1 second prevents the race condition from the framework services. 79 * The child session is cleaned up asynchronously after abandoning the parent session. Even 80 * if receiving the callback to tell the session is finished, it may be the race condition 81 * between executing {@link PackageInstaller#getSessionInfo(int)} and cleaning up the 82 * {@link android.content.pm.PackageInstaller.Session}. 83 */ 84 public static final long PREVENT_RACE_CONDITION_TIMEOUT_SECONDS = TimeUnit.SECONDS.toMillis(1); 85 private static final byte[] PLACE_HOLDER_STRING_BYTES = "Place Holder".getBytes(); 86 87 /** 88 * This is a wrapper class to let the test easier to focus on the "onFinish". It implements 89 * all of abstract methods with nothing in {@link PackageInstaller.SessionCallback} except for 90 * onFinish function. 91 */ 92 private static class AbandonSessionCallBack extends PackageInstaller.SessionCallback { 93 private final CountDownLatch mCountDownLatch; 94 private final List<Integer> mSessionIds; 95 AbandonSessionCallBack(CountDownLatch countDownLatch, int[] sessionIds)96 AbandonSessionCallBack(CountDownLatch countDownLatch, int[] sessionIds) { 97 mCountDownLatch = countDownLatch; 98 mSessionIds = new ArrayList<>(); 99 for (int sessionId : sessionIds) { 100 mSessionIds.add(sessionId); 101 } 102 } 103 AbandonSessionCallBack(CountDownLatch countDownLatch, int sessionId)104 AbandonSessionCallBack(CountDownLatch countDownLatch, int sessionId) { 105 this(countDownLatch, new int[]{sessionId}); 106 } 107 108 @Override onCreated(int sessionId)109 public void onCreated(int sessionId) { 110 /* Do nothing to make sub class no need to implement it*/ 111 } 112 113 @Override onBadgingChanged(int sessionId)114 public void onBadgingChanged(int sessionId) { 115 /* Do nothing to make sub class no need to implement it*/ 116 } 117 118 @Override onActiveChanged(int sessionId, boolean active)119 public void onActiveChanged(int sessionId, boolean active) { 120 /* Do nothing to make sub class no need to implement it*/ 121 } 122 123 @Override onProgressChanged(int sessionId, float progress)124 public void onProgressChanged(int sessionId, float progress) { 125 /* Do nothing to make sub class no need to implement it*/ 126 } 127 128 @Override onFinished(int sessionId, boolean success)129 public void onFinished(int sessionId, boolean success) { 130 if (!success) { 131 if (mSessionIds.contains(sessionId)) { 132 mCountDownLatch.countDown(); 133 } 134 } 135 } 136 } 137 138 @Rule 139 public final AdoptShellPermissionsRule mAdoptShellPermissionsRule = 140 new AdoptShellPermissionsRule( 141 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 142 Manifest.permission.INSTALL_PACKAGES, Manifest.permission.DELETE_PACKAGES); 143 144 @Rule 145 public final TestName mTestName = new TestName(); 146 147 private final List<PackageInstaller.SessionCallback> mSessionCallbacks = new ArrayList<>(); 148 149 private Handler mHandler; 150 private HandlerThread mHandlerThread; 151 private List<Closeable> mCloseableList = new ArrayList<>(); 152 153 @After tearDown()154 public void tearDown() { 155 for (Closeable closeable : mCloseableList) { 156 try { 157 closeable.close(); 158 } catch (IOException e) { 159 /* ensure close the resources and do no nothing */ 160 } 161 } 162 163 for (PackageInstaller.SessionCallback sessionCallback : mSessionCallbacks) { 164 InstallUtils.getPackageInstaller().unregisterSessionCallback(sessionCallback); 165 } 166 mSessionCallbacks.clear(); 167 168 if (mHandlerThread != null) { 169 mHandlerThread.quit(); 170 } 171 } 172 173 174 /** 175 * To help the test to register the {@link PackageInstaller.SessionCallback} easier and the 176 * parameter {@link PackageInstaller.SessionCallback} will unregister after the end of the 177 * test. 178 * 179 * @param sessionCallback registers by the {@link PackageInstaller} 180 */ registerSessionCallbacks( @onNull PackageInstaller.SessionCallback sessionCallback)181 private void registerSessionCallbacks( 182 @NonNull PackageInstaller.SessionCallback sessionCallback) { 183 Preconditions.checkNotNull(sessionCallback); 184 Preconditions.checkArgument(!mSessionCallbacks.contains(sessionCallback), 185 "The callback has registered."); 186 187 if (mHandler == null) { 188 mHandlerThread = new HandlerThread(mTestName.getMethodName()); 189 mHandlerThread.start(); 190 mHandler = new Handler(mHandlerThread.getLooper()); 191 } 192 193 InstallUtils.getPackageInstaller().registerSessionCallback(sessionCallback, mHandler); 194 mSessionCallbacks.add(sessionCallback); 195 } 196 197 /** 198 * To get all of child session IDs. 199 * 200 * @param parentSessionId the parent session id 201 * @return the array of child session IDs 202 * @throws IOException caused by opening parent session fail. 203 */ getChildSessionIds(int parentSessionId)204 private int[] getChildSessionIds(int parentSessionId) throws IOException { 205 try (PackageInstaller.Session parentSession = 206 openPackageInstallerSession(parentSessionId)) { 207 return parentSession.getChildSessionIds(); 208 } 209 } 210 getAllChildSessions(int[] sessionIds)211 private static List<PackageInstaller.SessionInfo> getAllChildSessions(int[] sessionIds) { 212 List<PackageInstaller.SessionInfo> result = new ArrayList<>(); 213 for (int sessionId : sessionIds) { 214 final PackageInstaller.SessionInfo session = 215 InstallUtils.getPackageInstaller().getSessionInfo(sessionId); 216 if (session != null) { 217 result.add(session); 218 } 219 } 220 return result; 221 } 222 223 /** 224 * To open the specified session. 225 * <p> 226 * The opened resources will be closed in {@link #tearDown()} automatically. 227 * </p> 228 * 229 * @param sessionId the session want to open 230 * @return the opened {@link PackageInstaller.Session} instance 231 * @throws IOException caused by opening {@link PackageInstaller.Session} fail. 232 */ openSession(int sessionId)233 private PackageInstaller.Session openSession(int sessionId) throws IOException { 234 PackageInstaller.Session session = openPackageInstallerSession(sessionId); 235 mCloseableList.add(session); 236 237 return session; 238 } 239 240 /** 241 * To open and write the file for the specified session. 242 * <p> 243 * The opened resources will be closed in {@link #tearDown()} automatically. 244 * </p> 245 * 246 * @param sessionId the session want to open 247 * @param fileName the expected file name 248 * @return the opened {@link OutputStream} instance 249 * @throws IOException caused by opening file fail. 250 */ openSessionForWrite(int sessionId, String fileName)251 private OutputStream openSessionForWrite(int sessionId, String fileName) throws IOException { 252 PackageInstaller.Session session = openSession(sessionId); 253 OutputStream os = session.openWrite(fileName, 0, -1); 254 mCloseableList.add(os); 255 256 return os; 257 } 258 259 @Before setUp()260 public void setUp() throws Exception { 261 Uninstall.packages(TestApp.A, TestApp.B); 262 } 263 264 @Test abandon_stagedSession_shouldReceiveAbandonCallBack()265 public void abandon_stagedSession_shouldReceiveAbandonCallBack() 266 throws Exception { 267 final int sessionId = Install.single(TestApp.A1).setStaged().createSession(); 268 final CountDownLatch countDownLatch = new CountDownLatch(1); 269 registerSessionCallbacks( 270 new AbandonSessionCallBack(countDownLatch, sessionId)); 271 272 InstallUtils.getPackageInstaller().abandonSession(sessionId); 273 274 assertThat( 275 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue(); 276 } 277 278 @Test abandon_nonStagedSession_shouldReceiveAbandonCallBack()279 public void abandon_nonStagedSession_shouldReceiveAbandonCallBack() 280 throws Exception { 281 final int sessionId = Install.single(TestApp.A1).createSession(); 282 final CountDownLatch countDownLatch = new CountDownLatch(1); 283 registerSessionCallbacks( 284 new AbandonSessionCallBack(countDownLatch, sessionId)); 285 286 InstallUtils.getPackageInstaller().abandonSession(sessionId); 287 288 assertThat( 289 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue(); 290 } 291 292 293 @Test abandon_stagedSession_openedSession_canNotGetNames()294 public void abandon_stagedSession_openedSession_canNotGetNames() 295 throws Exception { 296 final int sessionId = Install.single(TestApp.A1).setStaged().createSession(); 297 final CountDownLatch countDownLatch = new CountDownLatch(1); 298 final PackageInstaller.Session session = openSession(sessionId); 299 registerSessionCallbacks( 300 new AbandonSessionCallBack(countDownLatch, sessionId)); 301 302 InstallUtils.getPackageInstaller().abandonSession(sessionId); 303 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 304 305 assertThrows(instanceOf(SecurityException.class, 306 hasMessageThat(containsString("getNames not allowed"))), 307 () -> session.getNames()); 308 } 309 310 @Test abandon_nonStagedSession_openedSession_canNotGetNames()311 public void abandon_nonStagedSession_openedSession_canNotGetNames() 312 throws Exception { 313 final int sessionId = Install.single(TestApp.A1).createSession(); 314 final CountDownLatch countDownLatch = new CountDownLatch(1); 315 final PackageInstaller.Session session = openSession(sessionId); 316 registerSessionCallbacks( 317 new AbandonSessionCallBack(countDownLatch, sessionId)); 318 319 InstallUtils.getPackageInstaller().abandonSession(sessionId); 320 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 321 322 assertThrows(instanceOf(SecurityException.class, 323 hasMessageThat(containsString("getNames not allowed"))), 324 () -> session.getNames()); 325 } 326 327 @Test abandon_stagedSession_openForWriting_shouldFail()328 public void abandon_stagedSession_openForWriting_shouldFail() 329 throws Exception { 330 final int sessionId = Install.single(TestApp.A1).setStaged().createSession(); 331 final CountDownLatch countDownLatch = new CountDownLatch(1); 332 registerSessionCallbacks( 333 new AbandonSessionCallBack(countDownLatch, sessionId)); 334 final OutputStream outputStream = openSessionForWrite(sessionId, 335 mTestName.getMethodName()); 336 337 InstallUtils.getPackageInstaller().abandonSession(sessionId); 338 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 339 340 assertThrows(instanceOf(IOException.class, 341 hasMessageThat(containsString("write failed"))), 342 () -> outputStream.write(PLACE_HOLDER_STRING_BYTES)); 343 } 344 345 @Test abandon_nonStagedSession_openForWriting_shouldFail()346 public void abandon_nonStagedSession_openForWriting_shouldFail() 347 throws Exception { 348 final int sessiondId = Install.single(TestApp.A1).createSession(); 349 final CountDownLatch countDownLatch = new CountDownLatch(1); 350 registerSessionCallbacks( 351 new AbandonSessionCallBack(countDownLatch, sessiondId)); 352 final OutputStream outputStream = openSessionForWrite(sessiondId, 353 mTestName.getMethodName()); 354 355 InstallUtils.getPackageInstaller().abandonSession(sessiondId); 356 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 357 358 assertThrows(instanceOf(IOException.class, 359 hasMessageThat(containsString("write failed"))), 360 () -> outputStream.write(PLACE_HOLDER_STRING_BYTES)); 361 } 362 363 @Test abandon_stagedSession_canNotOpenAgain()364 public void abandon_stagedSession_canNotOpenAgain() 365 throws Exception { 366 final int sessionId = Install.single(TestApp.A1).setStaged().createSession(); 367 final CountDownLatch countDownLatch = new CountDownLatch(1); 368 registerSessionCallbacks( 369 new AbandonSessionCallBack(countDownLatch, sessionId)); 370 371 InstallUtils.getPackageInstaller().abandonSession(sessionId); 372 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 373 374 assertThrows(instanceOf(SecurityException.class, 375 hasMessageThat(containsString(String.valueOf(sessionId)))), 376 () -> InstallUtils.getPackageInstaller().openSession(sessionId)); 377 } 378 379 @Test abandon_nonStagedSession_canNotOpenAgain()380 public void abandon_nonStagedSession_canNotOpenAgain() 381 throws Exception { 382 final int sessionId = Install.single(TestApp.A1).createSession(); 383 final CountDownLatch countDownLatch = new CountDownLatch(1); 384 registerSessionCallbacks( 385 new AbandonSessionCallBack(countDownLatch, sessionId)); 386 387 InstallUtils.getPackageInstaller().abandonSession(sessionId); 388 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 389 390 assertThrows(instanceOf(SecurityException.class, 391 hasMessageThat(containsString(String.valueOf(sessionId)))), 392 () -> InstallUtils.getPackageInstaller().openSession(sessionId)); 393 } 394 395 @Test abandon_stagedParentSession_shouldReceiveAllChildrenAbandonCallBack()396 public void abandon_stagedParentSession_shouldReceiveAllChildrenAbandonCallBack() 397 throws Exception { 398 final int parentSessionId = Install.multi(TestApp.A1, 399 TestApp.B1).setStaged().createSession(); 400 final int[] childSessionIds = getChildSessionIds(parentSessionId); 401 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 402 registerSessionCallbacks( 403 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 404 405 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 406 407 assertThat( 408 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue(); 409 } 410 411 @Test abandon_nonStagedParentSession_shouldReceiveAllChildrenAbandonCallBack()412 public void abandon_nonStagedParentSession_shouldReceiveAllChildrenAbandonCallBack() 413 throws Exception { 414 final int parentSessionId = Install.multi(TestApp.A1, TestApp.B1).createSession(); 415 final int[] childSessionIds = getChildSessionIds(parentSessionId); 416 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 417 registerSessionCallbacks( 418 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 419 420 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 421 422 assertThat( 423 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue(); 424 } 425 426 @Test abandon_stagedParentSession_shouldAbandonAllChildrenSessions()427 public void abandon_stagedParentSession_shouldAbandonAllChildrenSessions() 428 throws Exception { 429 final int parentSessionId = Install.multi(TestApp.A1, TestApp.B1) 430 .setStaged().createSession(); 431 final int[] childSessionIds = getChildSessionIds(parentSessionId); 432 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 433 registerSessionCallbacks( 434 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 435 436 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 437 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 438 439 // The child session is cleaned up asynchronously after abandoning the parent session. 440 PollingCheck.check("The result should be an empty list.", 441 PREVENT_RACE_CONDITION_TIMEOUT_SECONDS, 442 () -> getAllChildSessions(childSessionIds).isEmpty()); 443 } 444 445 @Test abandon_nonStagedParentSession_shouldAbandonAllChildrenSessions()446 public void abandon_nonStagedParentSession_shouldAbandonAllChildrenSessions() 447 throws Exception { 448 final int parentSessionId = Install.multi(TestApp.A1, TestApp.B1).createSession(); 449 final int[] childSessionIds = getChildSessionIds(parentSessionId); 450 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 451 registerSessionCallbacks( 452 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 453 454 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 455 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 456 457 // The child session is cleaned up asynchronously after abandoning the parent session. 458 PollingCheck.check("The result should be empty list", 459 PREVENT_RACE_CONDITION_TIMEOUT_SECONDS, 460 () -> getAllChildSessions(childSessionIds).isEmpty()); 461 } 462 463 @Test abandon_stagedParentSession_openedChildSession_getNamesShouldReturnEmptyList()464 public void abandon_stagedParentSession_openedChildSession_getNamesShouldReturnEmptyList() 465 throws Exception { 466 final int parentSessionId = Install.multi(TestApp.A1).setStaged().createSession(); 467 final int[] childSessionIds = getChildSessionIds(parentSessionId); 468 final int firstChildSession = childSessionIds[0]; 469 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 470 final PackageInstaller.Session childSession = openSession(firstChildSession); 471 registerSessionCallbacks( 472 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 473 474 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 475 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 476 477 // TODO(b/171774482): the inconsistent behavior between staged and non-staged child session 478 // The child session is cleaned up asynchronously after abandoning the parent session. 479 PollingCheck.check("The result should be empty list", 480 PREVENT_RACE_CONDITION_TIMEOUT_SECONDS, () -> { 481 final String[] names; 482 try { 483 names = childSession.getNames(); 484 } catch (IOException e) { 485 return false; 486 } 487 return names != null && names.length == 0; 488 }); 489 } 490 491 @Test abandon_nonStagedParentSession_openedChildSession_canNotGetNames()492 public void abandon_nonStagedParentSession_openedChildSession_canNotGetNames() 493 throws Exception { 494 final int parentSessionId = Install.multi(TestApp.A1).createSession(); 495 final int[] childSessionIds = getChildSessionIds(parentSessionId); 496 final int firstChildSession = childSessionIds[0]; 497 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 498 final PackageInstaller.Session childSession = openSession(firstChildSession); 499 registerSessionCallbacks( 500 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 501 502 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 503 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 504 505 // The child session is cleaned up asynchronously after abandoning the parent session. 506 PollingCheck.check("getNames should get the security exception", 507 PREVENT_RACE_CONDITION_TIMEOUT_SECONDS, () -> { 508 try { 509 childSession.getNames(); 510 } catch (SecurityException e) { 511 if (e.getMessage().contains("getNames")) { 512 return true; 513 } 514 } catch (IOException e) { 515 return false; 516 } 517 return false; 518 }); 519 } 520 521 @Test abandon_stagedParentSession_openChildSessionForWriting_shouldFail()522 public void abandon_stagedParentSession_openChildSessionForWriting_shouldFail() 523 throws Exception { 524 final int parentSessionId = Install.multi(TestApp.A1).setStaged().createSession(); 525 final int[] childSessionIds = getChildSessionIds(parentSessionId); 526 final int firstChildSession = childSessionIds[0]; 527 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 528 registerSessionCallbacks( 529 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 530 final OutputStream outputStream = openSessionForWrite(firstChildSession, 531 mTestName.getMethodName()); 532 533 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 534 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 535 536 assertThrows(instanceOf(IOException.class, 537 hasMessageThat(containsString("write failed"))), 538 () -> outputStream.write(PLACE_HOLDER_STRING_BYTES)); 539 } 540 541 @Test abandon_nonStagedParentSession_openChildSessionForWriting_shouldFail()542 public void abandon_nonStagedParentSession_openChildSessionForWriting_shouldFail() 543 throws Exception { 544 final int parentSessionId = Install.multi(TestApp.A1).createSession(); 545 final int[] childSessionIds = getChildSessionIds(parentSessionId); 546 final int firstChildSession = childSessionIds[0]; 547 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 548 registerSessionCallbacks( 549 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 550 final OutputStream outputStream = 551 openSessionForWrite(firstChildSession, mTestName.getMethodName()); 552 553 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 554 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 555 556 assertThrows(instanceOf(IOException.class, 557 hasMessageThat(containsString("write failed"))), 558 () -> outputStream.write(PLACE_HOLDER_STRING_BYTES)); 559 } 560 561 @Test abandon_stagedParentSession_childSession_canNotOpenAgain()562 public void abandon_stagedParentSession_childSession_canNotOpenAgain() 563 throws Exception { 564 final int parentSessionId = Install.multi(TestApp.A1).setStaged().createSession(); 565 final int[] childSessionIds = getChildSessionIds(parentSessionId); 566 final int firstChildSession = childSessionIds[0]; 567 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 568 registerSessionCallbacks( 569 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 570 571 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 572 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 573 574 assertThrows(instanceOf(SecurityException.class, 575 hasMessageThat(containsString(String.valueOf(firstChildSession)))), 576 () -> InstallUtils.getPackageInstaller().openSession(firstChildSession)); 577 } 578 579 @Test abandon_nonStagedParentSession_childSession_canNotOpenAgain()580 public void abandon_nonStagedParentSession_childSession_canNotOpenAgain() 581 throws Exception { 582 final int parentSessionId = Install.multi(TestApp.A1).createSession(); 583 final int[] childSessionIds = getChildSessionIds(parentSessionId); 584 final int firstChildSession = childSessionIds[0]; 585 final CountDownLatch countDownLatch = new CountDownLatch(childSessionIds.length); 586 registerSessionCallbacks( 587 new AbandonSessionCallBack(countDownLatch, childSessionIds)); 588 589 InstallUtils.getPackageInstaller().abandonSession(parentSessionId); 590 countDownLatch.await(CALLBACK_TIMEOUT_SECONDS, TimeUnit.SECONDS); 591 592 assertThrows(instanceOf(SecurityException.class, 593 hasMessageThat(containsString(String.valueOf(firstChildSession)))), 594 () -> InstallUtils.getPackageInstaller().openSession(firstChildSession)); 595 } 596 } 597