• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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