1 /*
2  * Copyright (C) 2021 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 org.junit.Assert.assertThrows;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.ArgumentMatchers.anyString;
23 import static org.mockito.Mockito.mock;
24 import static org.mockito.Mockito.never;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.content.pm.PackageInstaller;
30 import android.content.pm.PackageManager;
31 import android.platform.test.annotations.Presubmit;
32 
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.JUnit4;
36 
37 import java.util.function.Predicate;
38 
39 @Presubmit
40 @RunWith(JUnit4.class)
41 public class PackageSessionVerifierTest {
42     private PackageSessionVerifier mSessionVerifier = new PackageSessionVerifier();
43 
44     @Test
checkRebootlessApex()45     public void checkRebootlessApex() throws Exception {
46         StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
47         mSessionVerifier.storeSession(session1);
48 
49         // Should throw for package name conflicts
50         PackageInstallerSession session2 = createSession(false, true, "com.foo");
51         assertThrows(PackageManagerException.class,
52                 () -> mSessionVerifier.checkRebootlessApex(session2));
53 
54         // Shouldn't throw if no package name conflicts
55         PackageInstallerSession session3 = createSession(false, true, "com.bar");
56         mSessionVerifier.checkRebootlessApex(session3);
57     }
58 
59     @Test
checkActiveSessions()60     public void checkActiveSessions() throws Exception {
61         StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
62         mSessionVerifier.storeSession(session1);
63         // Shouldn't throw for a single session no matter if supporting checkpoint or not
64         mSessionVerifier.checkActiveSessions(true);
65         mSessionVerifier.checkActiveSessions(false);
66 
67         // Now we have multiple active sessions
68         StagingManager.StagedSession session2 = createStagedSession(222, "com.bar", 2);
69         mSessionVerifier.storeSession(session2);
70         // Shouldn't throw if supporting checkpoint
71         mSessionVerifier.checkActiveSessions(true);
72         // Should throw if not supporting checkpoint
73         assertThrows(PackageManagerException.class,
74                 () -> mSessionVerifier.checkActiveSessions(false));
75     }
76 
77     @Test
checkRollbacks()78     public void checkRollbacks() throws Exception {
79         StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
80         StagingManager.StagedSession session2 = createStagedSession(222, "com.bar", 2);
81         StagingManager.StagedSession session3 = createStagedSession(333, "com.baz", 3);
82         session2.sessionParams().setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
83         session3.sessionParams().setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
84         when(session2.isDestroyed()).thenReturn(true);
85         mSessionVerifier.storeSession(session1);
86         mSessionVerifier.storeSession(session2);
87         mSessionVerifier.storeSession(session3);
88 
89         // Non-rollback session shouldn't be failed by a destroyed session
90         mSessionVerifier.checkRollbacks(session2);
91         verify(session1, never()).setSessionFailed(anyInt(), anyString());
92 
93         // Non-rollback session should fail
94         mSessionVerifier.checkRollbacks(session3);
95         verify(session1, times(1)).setSessionFailed(anyInt(), anyString());
96 
97         // Yet another non-rollback session should fail
98         StagingManager.StagedSession session4 = createStagedSession(444, "com.fur", 4);
99         assertThrows(PackageManagerException.class,
100                 () -> mSessionVerifier.checkRollbacks(session4));
101     }
102 
103     @Test
checkOverlaps()104     public void checkOverlaps() throws Exception {
105         StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
106         StagingManager.StagedSession session2 = createStagedSession(222, "com.foo", 2);
107         mSessionVerifier.storeSession(session1);
108         mSessionVerifier.storeSession(session2);
109         // No exception should be thrown for the earlier session should not fail
110         mSessionVerifier.checkOverlaps(session1, session1);
111         // Later session should fail
112         verify(session2, times(1)).setSessionFailed(anyInt(), anyString());
113         // Yet another later session should fail
114         StagingManager.StagedSession session3 = createStagedSession(333, "com.foo", 3);
115         assertThrows(PackageManagerException.class,
116                 () -> mSessionVerifier.checkOverlaps(session3, session3));
117         // session4 is earlier than session1, but it shouldn't fail session1
118         StagingManager.StagedSession session4 = createStagedSession(444, "com.foo", 0);
119         when(session4.isDestroyed()).thenReturn(true);
120         mSessionVerifier.checkOverlaps(session4, session4);
121         verify(session1, never()).setSessionFailed(anyInt(), anyString());
122     }
123 
createSession(boolean isStaged, boolean isApex, String packageName)124     private PackageInstallerSession createSession(boolean isStaged, boolean isApex,
125             String packageName) {
126         PackageInstallerSession session = mock(PackageInstallerSession.class);
127         when(session.isStaged()).thenReturn(isStaged);
128         when(session.isApexSession()).thenReturn(isApex);
129         when(session.getPackageName()).thenReturn(packageName);
130         return session;
131     }
132 
createStagedSession(int sessionId, String packageName, long committedMillis)133     private StagingManager.StagedSession createStagedSession(int sessionId, String packageName,
134             long committedMillis) {
135         StagingManager.StagedSession session = mock(StagingManager.StagedSession.class);
136         when(session.sessionId()).thenReturn(sessionId);
137         when(session.getPackageName()).thenReturn(packageName);
138         when(session.getCommittedMillis()).thenReturn(committedMillis);
139         when(session.sessionContains(any())).then(invocation -> {
140             Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
141             return filter.test(session);
142         });
143         PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
144                 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
145         when(session.sessionParams()).thenReturn(params);
146         return session;
147     }
148 }
149