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.server.pm; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyString; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.doAnswer; 25 import static org.mockito.Mockito.doReturn; 26 import static org.mockito.Mockito.never; 27 import static org.mockito.Mockito.spy; 28 import static org.mockito.Mockito.times; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 import static org.testng.Assert.assertThrows; 32 33 import android.apex.ApexSessionInfo; 34 import android.content.Context; 35 import android.content.IntentSender; 36 import android.content.pm.PackageInstaller; 37 import android.content.pm.PackageInstaller.SessionInfo; 38 import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode; 39 import android.os.SystemProperties; 40 import android.os.storage.IStorageManager; 41 import android.platform.test.annotations.Presubmit; 42 import android.util.SparseArray; 43 44 import com.android.dx.mockito.inline.extended.ExtendedMockito; 45 import com.android.internal.content.PackageHelper; 46 import com.android.internal.os.BackgroundThread; 47 import com.android.internal.util.Preconditions; 48 49 import org.junit.After; 50 import org.junit.Before; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.rules.TemporaryFolder; 54 import org.junit.runner.RunWith; 55 import org.junit.runners.JUnit4; 56 import org.mockito.Mock; 57 import org.mockito.MockitoAnnotations; 58 import org.mockito.MockitoSession; 59 import org.mockito.quality.Strictness; 60 61 import java.io.File; 62 import java.util.ArrayList; 63 import java.util.Arrays; 64 import java.util.List; 65 import java.util.function.Predicate; 66 67 @Presubmit 68 @RunWith(JUnit4.class) 69 public class StagingManagerTest { 70 @Rule 71 public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); 72 73 @Mock private Context mContext; 74 @Mock private IStorageManager mStorageManager; 75 @Mock private ApexManager mApexManager; 76 77 private File mTmpDir; 78 private StagingManager mStagingManager; 79 80 private MockitoSession mMockitoSession; 81 82 @Before setUp()83 public void setUp() throws Exception { 84 MockitoAnnotations.initMocks(this); 85 when(mContext.getSystemService(eq(Context.POWER_SERVICE))).thenReturn(null); 86 87 mMockitoSession = ExtendedMockito.mockitoSession() 88 .strictness(Strictness.LENIENT) 89 .mockStatic(SystemProperties.class) 90 .mockStatic(PackageHelper.class) 91 .startMocking(); 92 93 when(mStorageManager.supportsCheckpoint()).thenReturn(true); 94 when(mStorageManager.needsCheckpoint()).thenReturn(true); 95 when(PackageHelper.getStorageManager()).thenReturn(mStorageManager); 96 97 when(SystemProperties.get(eq("ro.apex.updatable"))).thenReturn("true"); 98 when(SystemProperties.get(eq("ro.apex.updatable"), anyString())).thenReturn("true"); 99 100 mTmpDir = mTemporaryFolder.newFolder("StagingManagerTest"); 101 mStagingManager = new StagingManager(mContext, null, mApexManager); 102 } 103 104 @After tearDown()105 public void tearDown() throws Exception { 106 if (mMockitoSession != null) { 107 mMockitoSession.finishMocking(); 108 } 109 } 110 111 /** 112 * Tests that sessions committed later shouldn't cause earlier ones to fail the overlapping 113 * check. 114 */ 115 @Test checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()116 public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes() 117 throws Exception { 118 // Create 2 sessions with overlapping packages 119 StagingManager.StagedSession session1 = createSession(111, "com.foo", 1); 120 StagingManager.StagedSession session2 = createSession(222, "com.foo", 2); 121 122 mStagingManager.createSession(session1); 123 mStagingManager.createSession(session2); 124 // Session1 should not fail in spite of the overlapping packages 125 mStagingManager.checkNonOverlappingWithStagedSessions(session1); 126 // Session2 should fail due to overlapping packages 127 assertThrows(PackageManagerException.class, 128 () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2)); 129 } 130 131 @Test restoreSessions_nonParentSession_throwsIAE()132 public void restoreSessions_nonParentSession_throwsIAE() throws Exception { 133 FakeStagedSession session = new FakeStagedSession(239); 134 session.setParentSessionId(1543); 135 136 assertThrows(IllegalArgumentException.class, 137 () -> mStagingManager.restoreSessions(Arrays.asList(session), false)); 138 } 139 140 @Test restoreSessions_nonCommittedSession_throwsIAE()141 public void restoreSessions_nonCommittedSession_throwsIAE() throws Exception { 142 FakeStagedSession session = new FakeStagedSession(239); 143 144 assertThrows(IllegalArgumentException.class, 145 () -> mStagingManager.restoreSessions(Arrays.asList(session), false)); 146 } 147 148 @Test restoreSessions_terminalSession_throwsIAE()149 public void restoreSessions_terminalSession_throwsIAE() throws Exception { 150 FakeStagedSession session = new FakeStagedSession(239); 151 session.setCommitted(true); 152 session.setSessionApplied(); 153 154 assertThrows(IllegalArgumentException.class, 155 () -> mStagingManager.restoreSessions(Arrays.asList(session), false)); 156 } 157 158 @Test restoreSessions_deviceUpgrading_failsAllSessions()159 public void restoreSessions_deviceUpgrading_failsAllSessions() throws Exception { 160 FakeStagedSession session1 = new FakeStagedSession(37); 161 session1.setCommitted(true); 162 FakeStagedSession session2 = new FakeStagedSession(57); 163 session2.setCommitted(true); 164 165 mStagingManager.restoreSessions(Arrays.asList(session1, session2), true); 166 167 assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 168 assertThat(session1.getErrorMessage()).isEqualTo("Build fingerprint has changed"); 169 170 assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 171 assertThat(session2.getErrorMessage()).isEqualTo("Build fingerprint has changed"); 172 } 173 174 @Test restoreSessions_multipleSessions_deviceWithoutFsCheckpointSupport_throwISE()175 public void restoreSessions_multipleSessions_deviceWithoutFsCheckpointSupport_throwISE() 176 throws Exception { 177 FakeStagedSession session1 = new FakeStagedSession(37); 178 session1.setCommitted(true); 179 FakeStagedSession session2 = new FakeStagedSession(57); 180 session2.setCommitted(true); 181 182 when(mStorageManager.supportsCheckpoint()).thenReturn(false); 183 184 assertThrows(IllegalStateException.class, 185 () -> mStagingManager.restoreSessions(Arrays.asList(session1, session2), false)); 186 } 187 188 @Test restoreSessions_handlesDestroyedAndNotReadySessions()189 public void restoreSessions_handlesDestroyedAndNotReadySessions() throws Exception { 190 FakeStagedSession destroyedApkSession = new FakeStagedSession(23); 191 destroyedApkSession.setCommitted(true); 192 destroyedApkSession.setDestroyed(true); 193 194 FakeStagedSession destroyedApexSession = new FakeStagedSession(37); 195 destroyedApexSession.setCommitted(true); 196 destroyedApexSession.setDestroyed(true); 197 destroyedApexSession.setIsApex(true); 198 199 FakeStagedSession nonReadyApkSession = new FakeStagedSession(57); 200 nonReadyApkSession.setCommitted(true); 201 202 FakeStagedSession nonReadyApexSession = new FakeStagedSession(73); 203 nonReadyApexSession.setCommitted(true); 204 nonReadyApexSession.setIsApex(true); 205 206 FakeStagedSession destroyedNonReadySession = new FakeStagedSession(101); 207 destroyedNonReadySession.setCommitted(true); 208 destroyedNonReadySession.setDestroyed(true); 209 210 FakeStagedSession regularApkSession = new FakeStagedSession(239); 211 regularApkSession.setCommitted(true); 212 regularApkSession.setSessionReady(); 213 214 List<StagingManager.StagedSession> sessions = new ArrayList<>(); 215 sessions.add(destroyedApkSession); 216 sessions.add(destroyedApexSession); 217 sessions.add(nonReadyApkSession); 218 sessions.add(nonReadyApexSession); 219 sessions.add(destroyedNonReadySession); 220 sessions.add(regularApkSession); 221 222 mStagingManager.restoreSessions(sessions, false); 223 224 assertThat(sessions).containsExactly(regularApkSession); 225 assertThat(destroyedApkSession.isDestroyed()).isTrue(); 226 assertThat(destroyedApexSession.isDestroyed()).isTrue(); 227 assertThat(destroyedNonReadySession.isDestroyed()).isTrue(); 228 229 mStagingManager.onBootCompletedBroadcastReceived(); 230 assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue(); 231 assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue(); 232 } 233 234 @Test restoreSessions_unknownApexSession_failsAllSessions()235 public void restoreSessions_unknownApexSession_failsAllSessions() throws Exception { 236 FakeStagedSession apkSession = new FakeStagedSession(239); 237 apkSession.setCommitted(true); 238 apkSession.setSessionReady(); 239 240 FakeStagedSession apexSession = new FakeStagedSession(1543); 241 apexSession.setCommitted(true); 242 apexSession.setIsApex(true); 243 apexSession.setSessionReady(); 244 245 List<StagingManager.StagedSession> sessions = new ArrayList<>(); 246 sessions.add(apkSession); 247 sessions.add(apexSession); 248 249 when(mApexManager.getSessions()).thenReturn(new SparseArray<>()); 250 mStagingManager.restoreSessions(sessions, false); 251 252 // Validate checkpoint wasn't aborted. 253 verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); 254 255 assertThat(apexSession.getErrorCode()) 256 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 257 assertThat(apexSession.getErrorMessage()).isEqualTo("apexd did not know anything about a " 258 + "staged session supposed to be activated"); 259 260 assertThat(apkSession.getErrorCode()) 261 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 262 assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); 263 } 264 265 @Test restoreSessions_failedApexSessions_failsAllSessions()266 public void restoreSessions_failedApexSessions_failsAllSessions() throws Exception { 267 FakeStagedSession apkSession = new FakeStagedSession(239); 268 apkSession.setCommitted(true); 269 apkSession.setSessionReady(); 270 271 FakeStagedSession apexSession1 = new FakeStagedSession(1543); 272 apexSession1.setCommitted(true); 273 apexSession1.setIsApex(true); 274 apexSession1.setSessionReady(); 275 276 FakeStagedSession apexSession2 = new FakeStagedSession(101); 277 apexSession2.setCommitted(true); 278 apexSession2.setIsApex(true); 279 apexSession2.setSessionReady(); 280 281 FakeStagedSession apexSession3 = new FakeStagedSession(57); 282 apexSession3.setCommitted(true); 283 apexSession3.setIsApex(true); 284 apexSession3.setSessionReady(); 285 286 ApexSessionInfo activationFailed = new ApexSessionInfo(); 287 activationFailed.sessionId = 1543; 288 activationFailed.isActivationFailed = true; 289 activationFailed.errorMessage = "Failed for test"; 290 291 ApexSessionInfo staged = new ApexSessionInfo(); 292 staged.sessionId = 101; 293 staged.isStaged = true; 294 295 SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>(); 296 apexdSessions.put(1543, activationFailed); 297 apexdSessions.put(101, staged); 298 when(mApexManager.getSessions()).thenReturn(apexdSessions); 299 300 List<StagingManager.StagedSession> sessions = new ArrayList<>(); 301 sessions.add(apkSession); 302 sessions.add(apexSession1); 303 sessions.add(apexSession2); 304 sessions.add(apexSession3); 305 306 mStagingManager.restoreSessions(sessions, false); 307 308 // Validate checkpoint wasn't aborted. 309 verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); 310 311 assertThat(apexSession1.getErrorCode()) 312 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 313 assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. " 314 + "Error: Failed for test"); 315 316 assertThat(apexSession2.getErrorCode()) 317 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 318 assertThat(apexSession2.getErrorMessage()).isEqualTo("Staged session 101 at boot didn't " 319 + "activate nor fail. Marking it as failed anyway."); 320 321 assertThat(apexSession3.getErrorCode()) 322 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 323 assertThat(apexSession3.getErrorMessage()).isEqualTo("apexd did not know anything about a " 324 + "staged session supposed to be activated"); 325 326 assertThat(apkSession.getErrorCode()) 327 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 328 assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); 329 } 330 331 @Test restoreSessions_stagedApexSession_failsAllSessions()332 public void restoreSessions_stagedApexSession_failsAllSessions() throws Exception { 333 FakeStagedSession apkSession = new FakeStagedSession(239); 334 apkSession.setCommitted(true); 335 apkSession.setSessionReady(); 336 337 FakeStagedSession apexSession = new FakeStagedSession(1543); 338 apexSession.setCommitted(true); 339 apexSession.setIsApex(true); 340 apexSession.setSessionReady(); 341 342 ApexSessionInfo staged = new ApexSessionInfo(); 343 staged.sessionId = 1543; 344 staged.isStaged = true; 345 346 SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>(); 347 apexdSessions.put(1543, staged); 348 when(mApexManager.getSessions()).thenReturn(apexdSessions); 349 350 List<StagingManager.StagedSession> sessions = new ArrayList<>(); 351 sessions.add(apkSession); 352 sessions.add(apexSession); 353 354 mStagingManager.restoreSessions(sessions, false); 355 356 // Validate checkpoint wasn't aborted. 357 verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); 358 359 assertThat(apexSession.getErrorCode()) 360 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 361 assertThat(apexSession.getErrorMessage()).isEqualTo("Staged session 1543 at boot didn't " 362 + "activate nor fail. Marking it as failed anyway."); 363 364 assertThat(apkSession.getErrorCode()) 365 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 366 assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); 367 } 368 369 @Test restoreSessions_failedAndActivatedApexSessions_abortsCheckpoint()370 public void restoreSessions_failedAndActivatedApexSessions_abortsCheckpoint() throws Exception { 371 FakeStagedSession apkSession = new FakeStagedSession(239); 372 apkSession.setCommitted(true); 373 apkSession.setSessionReady(); 374 375 FakeStagedSession apexSession1 = new FakeStagedSession(1543); 376 apexSession1.setCommitted(true); 377 apexSession1.setIsApex(true); 378 apexSession1.setSessionReady(); 379 380 FakeStagedSession apexSession2 = new FakeStagedSession(101); 381 apexSession2.setCommitted(true); 382 apexSession2.setIsApex(true); 383 apexSession2.setSessionReady(); 384 385 FakeStagedSession apexSession3 = new FakeStagedSession(57); 386 apexSession3.setCommitted(true); 387 apexSession3.setIsApex(true); 388 apexSession3.setSessionReady(); 389 390 FakeStagedSession apexSession4 = new FakeStagedSession(37); 391 apexSession4.setCommitted(true); 392 apexSession4.setIsApex(true); 393 apexSession4.setSessionReady(); 394 395 ApexSessionInfo activationFailed = new ApexSessionInfo(); 396 activationFailed.sessionId = 1543; 397 activationFailed.isActivationFailed = true; 398 399 ApexSessionInfo activated = new ApexSessionInfo(); 400 activated.sessionId = 101; 401 activated.isActivated = true; 402 403 ApexSessionInfo staged = new ApexSessionInfo(); 404 staged.sessionId = 57; 405 staged.isActivationFailed = true; 406 407 SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>(); 408 apexdSessions.put(1543, activationFailed); 409 apexdSessions.put(101, activated); 410 apexdSessions.put(57, staged); 411 when(mApexManager.getSessions()).thenReturn(apexdSessions); 412 413 List<StagingManager.StagedSession> sessions = new ArrayList<>(); 414 sessions.add(apkSession); 415 sessions.add(apexSession1); 416 sessions.add(apexSession2); 417 sessions.add(apexSession3); 418 sessions.add(apexSession4); 419 420 mStagingManager.restoreSessions(sessions, false); 421 422 // Validate checkpoint was aborted. 423 verify(mStorageManager, times(1)).abortChanges(eq("abort-staged-install"), eq(false)); 424 } 425 426 @Test restoreSessions_apexSessionInImpossibleState_failsAllSessions()427 public void restoreSessions_apexSessionInImpossibleState_failsAllSessions() throws Exception { 428 FakeStagedSession apkSession = new FakeStagedSession(239); 429 apkSession.setCommitted(true); 430 apkSession.setSessionReady(); 431 432 FakeStagedSession apexSession = new FakeStagedSession(1543); 433 apexSession.setCommitted(true); 434 apexSession.setIsApex(true); 435 apexSession.setSessionReady(); 436 437 ApexSessionInfo impossible = new ApexSessionInfo(); 438 impossible.sessionId = 1543; 439 440 SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>(); 441 apexdSessions.put(1543, impossible); 442 when(mApexManager.getSessions()).thenReturn(apexdSessions); 443 444 List<StagingManager.StagedSession> sessions = new ArrayList<>(); 445 sessions.add(apkSession); 446 sessions.add(apexSession); 447 448 mStagingManager.restoreSessions(sessions, false); 449 450 // Validate checkpoint wasn't aborted. 451 verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); 452 453 assertThat(apexSession.getErrorCode()) 454 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 455 assertThat(apexSession.getErrorMessage()).isEqualTo("Impossible state"); 456 457 assertThat(apkSession.getErrorCode()) 458 .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); 459 assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); 460 } 461 462 @Test getSessionIdByPackageName()463 public void getSessionIdByPackageName() throws Exception { 464 FakeStagedSession session = new FakeStagedSession(239); 465 session.setCommitted(true); 466 session.setSessionReady(); 467 session.setPackageName("com.foo"); 468 469 mStagingManager.createSession(session); 470 assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(239); 471 } 472 473 @Test getSessionIdByPackageName_appliedSession_ignores()474 public void getSessionIdByPackageName_appliedSession_ignores() throws Exception { 475 FakeStagedSession session = new FakeStagedSession(37); 476 session.setCommitted(true); 477 session.setSessionApplied(); 478 session.setPackageName("com.foo"); 479 480 mStagingManager.createSession(session); 481 assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1); 482 } 483 484 @Test getSessionIdByPackageName_failedSession_ignores()485 public void getSessionIdByPackageName_failedSession_ignores() throws Exception { 486 FakeStagedSession session = new FakeStagedSession(73); 487 session.setCommitted(true); 488 session.setSessionFailed(1, "whatevs"); 489 session.setPackageName("com.foo"); 490 491 mStagingManager.createSession(session); 492 assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1); 493 } 494 495 @Test getSessionIdByPackageName_destroyedSession_ignores()496 public void getSessionIdByPackageName_destroyedSession_ignores() throws Exception { 497 FakeStagedSession session = new FakeStagedSession(23); 498 session.setCommitted(true); 499 session.setDestroyed(true); 500 session.setPackageName("com.foo"); 501 502 mStagingManager.createSession(session); 503 assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1); 504 } 505 506 @Test getSessionIdByPackageName_noSessions()507 public void getSessionIdByPackageName_noSessions() throws Exception { 508 assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1); 509 } 510 511 @Test getSessionIdByPackageName_noSessionHasThisPackage()512 public void getSessionIdByPackageName_noSessionHasThisPackage() throws Exception { 513 FakeStagedSession session = new FakeStagedSession(37); 514 session.setCommitted(true); 515 session.setSessionApplied(); 516 session.setPackageName("com.foo"); 517 518 mStagingManager.createSession(session); 519 assertThat(mStagingManager.getSessionIdByPackageName("com.bar")).isEqualTo(-1); 520 } 521 createSession(int sessionId, String packageName, long committedMillis)522 private StagingManager.StagedSession createSession(int sessionId, String packageName, 523 long committedMillis) { 524 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 525 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 526 params.isStaged = true; 527 528 InstallSource installSource = InstallSource.create("testInstallInitiator", 529 "testInstallOriginator", "testInstaller", "testAttributionTag"); 530 531 PackageInstallerSession session = new PackageInstallerSession( 532 /* callback */ null, 533 /* context */ null, 534 /* pm */ null, 535 /* sessionProvider */ null, 536 /* silentUpdatePolicy */ null, 537 /* looper */ BackgroundThread.getHandler().getLooper(), 538 /* stagingManager */ null, 539 /* sessionId */ sessionId, 540 /* userId */ 456, 541 /* installerUid */ -1, 542 /* installSource */ installSource, 543 /* sessionParams */ params, 544 /* createdMillis */ 0L, 545 /* committedMillis */ committedMillis, 546 /* stageDir */ mTmpDir, 547 /* stageCid */ null, 548 /* files */ null, 549 /* checksums */ null, 550 /* prepared */ true, 551 /* committed */ true, 552 /* destroyed */ false, 553 /* sealed */ false, // Setting to true would trigger some PM logic. 554 /* childSessionIds */ null, 555 /* parentSessionId */ -1, 556 /* isReady */ false, 557 /* isFailed */ false, 558 /* isApplied */false, 559 /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR, 560 /* stagedSessionErrorMessage */ "no error"); 561 562 StagingManager.StagedSession stagedSession = spy(session.mStagedSession); 563 doReturn(packageName).when(stagedSession).getPackageName(); 564 doAnswer(invocation -> { 565 Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0); 566 return filter.test(stagedSession); 567 }).when(stagedSession).sessionContains(any()); 568 return stagedSession; 569 } 570 571 private static final class FakeStagedSession implements StagingManager.StagedSession { 572 private final int mSessionId; 573 private boolean mIsApex = false; 574 private boolean mIsCommitted = false; 575 private boolean mIsReady = false; 576 private boolean mIsApplied = false; 577 private boolean mIsFailed = false; 578 private @StagedSessionErrorCode int mErrorCode = -1; 579 private String mErrorMessage; 580 private boolean mIsDestroyed = false; 581 private int mParentSessionId = -1; 582 private String mPackageName; 583 private boolean mIsAbandonded = false; 584 private boolean mPreRebootVerificationStarted = false; 585 private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>(); 586 FakeStagedSession(int sessionId)587 private FakeStagedSession(int sessionId) { 588 mSessionId = sessionId; 589 } 590 setParentSessionId(int parentSessionId)591 private void setParentSessionId(int parentSessionId) { 592 mParentSessionId = parentSessionId; 593 } 594 setCommitted(boolean isCommitted)595 private void setCommitted(boolean isCommitted) { 596 mIsCommitted = isCommitted; 597 } 598 setIsApex(boolean isApex)599 private void setIsApex(boolean isApex) { 600 mIsApex = isApex; 601 } 602 setDestroyed(boolean isDestroyed)603 private void setDestroyed(boolean isDestroyed) { 604 mIsDestroyed = isDestroyed; 605 } 606 setPackageName(String packageName)607 private void setPackageName(String packageName) { 608 mPackageName = packageName; 609 } 610 isAbandonded()611 private boolean isAbandonded() { 612 return mIsAbandonded; 613 } 614 hasPreRebootVerificationStarted()615 private boolean hasPreRebootVerificationStarted() { 616 return mPreRebootVerificationStarted; 617 } 618 addChildSession(FakeStagedSession session)619 private FakeStagedSession addChildSession(FakeStagedSession session) { 620 mChildSessions.add(session); 621 session.setParentSessionId(sessionId()); 622 return this; 623 } 624 getErrorCode()625 private @StagedSessionErrorCode int getErrorCode() { 626 return mErrorCode; 627 } 628 getErrorMessage()629 private String getErrorMessage() { 630 return mErrorMessage; 631 } 632 633 @Override isMultiPackage()634 public boolean isMultiPackage() { 635 return !mChildSessions.isEmpty(); 636 } 637 638 @Override isApexSession()639 public boolean isApexSession() { 640 return mIsApex; 641 } 642 643 @Override isCommitted()644 public boolean isCommitted() { 645 return mIsCommitted; 646 } 647 648 @Override isInTerminalState()649 public boolean isInTerminalState() { 650 return isSessionApplied() || isSessionFailed(); 651 } 652 653 @Override isDestroyed()654 public boolean isDestroyed() { 655 return mIsDestroyed; 656 } 657 658 @Override isSessionReady()659 public boolean isSessionReady() { 660 return mIsReady; 661 } 662 663 @Override isSessionApplied()664 public boolean isSessionApplied() { 665 return mIsApplied; 666 } 667 668 @Override isSessionFailed()669 public boolean isSessionFailed() { 670 return mIsFailed; 671 } 672 673 @Override getChildSessions()674 public List<StagingManager.StagedSession> getChildSessions() { 675 return mChildSessions; 676 } 677 678 @Override getPackageName()679 public String getPackageName() { 680 return mPackageName; 681 } 682 683 @Override getParentSessionId()684 public int getParentSessionId() { 685 return mParentSessionId; 686 } 687 688 @Override sessionId()689 public int sessionId() { 690 return mSessionId; 691 } 692 693 @Override sessionParams()694 public PackageInstaller.SessionParams sessionParams() { 695 throw new UnsupportedOperationException(); 696 } 697 698 @Override sessionContains(Predicate<StagingManager.StagedSession> filter)699 public boolean sessionContains(Predicate<StagingManager.StagedSession> filter) { 700 return filter.test(this); 701 } 702 703 @Override containsApkSession()704 public boolean containsApkSession() { 705 Preconditions.checkState(!hasParentSessionId(), "Child session"); 706 if (!isMultiPackage()) { 707 return !isApexSession(); 708 } 709 for (StagingManager.StagedSession session : mChildSessions) { 710 if (!session.isApexSession()) { 711 return true; 712 } 713 } 714 return false; 715 } 716 717 @Override containsApexSession()718 public boolean containsApexSession() { 719 Preconditions.checkState(!hasParentSessionId(), "Child session"); 720 if (!isMultiPackage()) { 721 return isApexSession(); 722 } 723 for (StagingManager.StagedSession session : mChildSessions) { 724 if (session.isApexSession()) { 725 return true; 726 } 727 } 728 return false; 729 } 730 731 @Override setSessionReady()732 public void setSessionReady() { 733 mIsReady = true; 734 } 735 736 @Override setSessionFailed(@tagedSessionErrorCode int errorCode, String errorMessage)737 public void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) { 738 Preconditions.checkState(!mIsApplied, "Already marked as applied"); 739 mIsFailed = true; 740 mErrorCode = errorCode; 741 mErrorMessage = errorMessage; 742 } 743 744 @Override setSessionApplied()745 public void setSessionApplied() { 746 Preconditions.checkState(!mIsFailed, "Already marked as failed"); 747 mIsApplied = true; 748 } 749 750 @Override installSession(IntentSender statusReceiver)751 public void installSession(IntentSender statusReceiver) { 752 throw new UnsupportedOperationException(); 753 } 754 755 @Override hasParentSessionId()756 public boolean hasParentSessionId() { 757 return mParentSessionId != -1; 758 } 759 760 @Override getCommittedMillis()761 public long getCommittedMillis() { 762 throw new UnsupportedOperationException(); 763 } 764 765 @Override abandon()766 public void abandon() { 767 mIsAbandonded = true; 768 } 769 770 @Override notifyStartPreRebootVerification()771 public boolean notifyStartPreRebootVerification() { 772 mPreRebootVerificationStarted = true; 773 // TODO(ioffe): change to true when tests for pre-reboot verification are added. 774 return false; 775 } 776 777 @Override notifyEndPreRebootVerification()778 public void notifyEndPreRebootVerification() { 779 throw new UnsupportedOperationException(); 780 } 781 782 @Override verifySession()783 public void verifySession() { 784 throw new UnsupportedOperationException(); 785 } 786 } 787 } 788