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.cts_root.packageinstaller;
18 
19 import static com.android.cts.install.lib.InstallUtils.getInstalledVersion;
20 import static com.android.cts.install.lib.InstallUtils.openPackageInstallerSession;
21 import static com.android.cts.install.lib.PackageInstallerSessionInfoSubject.assertThat;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import android.Manifest;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.SharedPreferences;
29 import android.content.pm.PackageInstaller;
30 
31 import androidx.test.platform.app.InstrumentationRegistry;
32 
33 import com.android.cts.install.lib.Install;
34 import com.android.cts.install.lib.InstallUtils;
35 import com.android.cts.install.lib.LocalIntentSender;
36 import com.android.cts.install.lib.TestApp;
37 
38 import org.junit.After;
39 import org.junit.Before;
40 import org.junit.Test;
41 
42 import java.util.function.Predicate;
43 import java.util.function.Supplier;
44 
45 public class SessionCleanUpTest {
46     private static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
47     /**
48      * Time between repeated checks in {@link #retry}.
49      */
50     private static final long RETRY_CHECK_INTERVAL_MILLIS = 500;
51     /**
52      * Maximum number of checks in {@link #retry} before a timeout occurs.
53      */
54     private static final long RETRY_MAX_INTERVALS = 20;
55 
56     @Before
setUp()57     public void setUp() {
58         InstrumentationRegistry.getInstrumentation().getUiAutomation()
59                 .adoptShellPermissionIdentity(
60                         Manifest.permission.CLEAR_APP_CACHE,
61                         Manifest.permission.INSTALL_PACKAGES,
62                         Manifest.permission.DELETE_PACKAGES);
63     }
64 
65     @After
tearDown()66     public void tearDown() {
67         InstrumentationRegistry.getInstrumentation().getUiAutomation()
68                 .dropShellPermissionIdentity();
69     }
70 
retry(Supplier<T> supplier, Predicate<T> predicate, String message)71     private static <T> T retry(Supplier<T> supplier, Predicate<T> predicate, String message)
72             throws InterruptedException {
73         for (int i = 0; i < RETRY_MAX_INTERVALS; i++) {
74             T result = supplier.get();
75             if (predicate.test(result)) {
76                 return result;
77             }
78             Thread.sleep(RETRY_CHECK_INTERVAL_MILLIS);
79         }
80         throw new AssertionError(message);
81     }
82 
assertSessionNotExists(int sessionId)83     private void assertSessionNotExists(int sessionId) throws Exception {
84         // The session is cleaned up asynchronously.
85         // Retry until the session no longer exists.
86         retry(() -> InstallUtils.getPackageInstaller().getSessionInfo(sessionId),
87                 info -> info == null,
88                 "Session " + sessionId + " not cleaned up");
89     }
90 
91     @Test
testSessionCleanUp_Single_Success()92     public void testSessionCleanUp_Single_Success() throws Exception {
93         int sessionId = Install.single(TestApp.A1).commit();
94         assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
95         assertSessionNotExists(sessionId);
96     }
97 
98     @Test
testSessionCleanUp_Multi_Success()99     public void testSessionCleanUp_Multi_Success() throws Exception {
100         int parentId = Install.multi(TestApp.A1, TestApp.B1).createSession();
101         try (PackageInstaller.Session parent = openPackageInstallerSession(parentId)) {
102             int[] childIds = parent.getChildSessionIds();
103             LocalIntentSender sender = new LocalIntentSender();
104             parent.commit(sender.getIntentSender());
105             InstallUtils.assertStatusSuccess(sender.getResult());
106             assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
107             assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
108             assertSessionNotExists(parentId);
109             for (int childId : childIds) {
110                 assertSessionNotExists(childId);
111             }
112         }
113     }
114 
115     @Test
testSessionCleanUp_Single_VerificationFailed()116     public void testSessionCleanUp_Single_VerificationFailed() throws Exception {
117         Install.single(TestApp.A2).commit();
118         int sessionId = Install.single(TestApp.A1).createSession();
119         try (PackageInstaller.Session session = openPackageInstallerSession(sessionId)) {
120             LocalIntentSender sender = new LocalIntentSender();
121             session.commit(sender.getIntentSender());
122             InstallUtils.assertStatusFailure(sender.getResult());
123             assertSessionNotExists(sessionId);
124         }
125     }
126 
127     @Test
testSessionCleanUp_Multi_VerificationFailed()128     public void testSessionCleanUp_Multi_VerificationFailed() throws Exception {
129         Install.single(TestApp.A2).commit();
130         int parentId = Install.multi(TestApp.A1, TestApp.B1).createSession();
131         try (PackageInstaller.Session parent = openPackageInstallerSession(parentId)) {
132             int[] childIds = parent.getChildSessionIds();
133             LocalIntentSender sender = new LocalIntentSender();
134             parent.commit(sender.getIntentSender());
135             InstallUtils.assertStatusFailure(sender.getResult());
136             assertSessionNotExists(parentId);
137             for (int childId : childIds) {
138                 assertSessionNotExists(childId);
139             }
140         }
141     }
142 
143     @Test
testSessionCleanUp_Single_ValidationFailed()144     public void testSessionCleanUp_Single_ValidationFailed() throws Exception {
145         int sessionId = Install.single(TestApp.AIncompleteSplit).createSession();
146         try (PackageInstaller.Session session = openPackageInstallerSession(sessionId)) {
147             LocalIntentSender sender = new LocalIntentSender();
148             session.commit(sender.getIntentSender());
149             InstallUtils.assertStatusFailure(sender.getResult());
150             assertSessionNotExists(sessionId);
151         }
152     }
153 
154     @Test
testSessionCleanUp_Multi_ValidationFailed()155     public void testSessionCleanUp_Multi_ValidationFailed() throws Exception {
156         int parentId = Install.multi(TestApp.AIncompleteSplit, TestApp.B1).createSession();
157         try (PackageInstaller.Session parent = openPackageInstallerSession(parentId)) {
158             int[] childIds = parent.getChildSessionIds();
159             LocalIntentSender sender = new LocalIntentSender();
160             parent.commit(sender.getIntentSender());
161             InstallUtils.assertStatusFailure(sender.getResult());
162             assertSessionNotExists(parentId);
163             for (int childId : childIds) {
164                 assertSessionNotExists(childId);
165             }
166         }
167     }
168 
169     @Test
testSessionCleanUp_Single_NoPermission()170     public void testSessionCleanUp_Single_NoPermission() throws Exception {
171         int sessionId = Install.single(TestApp.A1)
172                 .addInstallFlags(INSTALL_FORCE_PERMISSION_PROMPT).createSession();
173         try (PackageInstaller.Session session = openPackageInstallerSession(sessionId)) {
174             LocalIntentSender sender = new LocalIntentSender();
175             session.commit(sender.getIntentSender());
176             Intent intent = sender.getResult();
177             int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
178                     PackageInstaller.STATUS_FAILURE);
179             assertThat(status).isEqualTo(PackageInstaller.STATUS_PENDING_USER_ACTION);
180             int idNeedsUserAction = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
181             InstallUtils.getPackageInstaller().setPermissionsResult(idNeedsUserAction, false);
182             InstallUtils.assertStatusFailure(sender.getResult());
183             assertSessionNotExists(sessionId);
184         }
185     }
186 
187     @Test
testSessionCleanUp_Multi_NoPermission()188     public void testSessionCleanUp_Multi_NoPermission() throws Exception {
189         int parentId = Install.multi(TestApp.A1, TestApp.B1)
190                 .addInstallFlags(INSTALL_FORCE_PERMISSION_PROMPT).createSession();
191         try (PackageInstaller.Session parent = openPackageInstallerSession(parentId)) {
192             int[] childIds = parent.getChildSessionIds();
193             LocalIntentSender sender = new LocalIntentSender();
194             parent.commit(sender.getIntentSender());
195             Intent intent = sender.getResult();
196             int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
197                     PackageInstaller.STATUS_FAILURE);
198             assertThat(status).isEqualTo(PackageInstaller.STATUS_PENDING_USER_ACTION);
199             int idNeedsUserAction = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
200             InstallUtils.getPackageInstaller().setPermissionsResult(idNeedsUserAction, false);
201             InstallUtils.assertStatusFailure(sender.getResult());
202             assertSessionNotExists(parentId);
203             for (int childId : childIds) {
204                 assertSessionNotExists(childId);
205             }
206         }
207     }
208 
209     @Test
testSessionCleanUp_Single_Expire_Install()210     public void testSessionCleanUp_Single_Expire_Install() throws Exception {
211         int sessionId = Install.single(TestApp.A1).setStaged().commit();
212 
213         Context context = InstrumentationRegistry.getInstrumentation().getContext();
214         SharedPreferences prefs = context.getSharedPreferences("test", 0);
215         prefs.edit().putInt("sessionId", sessionId).commit();
216     }
217 
218     @Test
testSessionCleanUp_Single_Expire_VerifyInstall()219     public void testSessionCleanUp_Single_Expire_VerifyInstall() throws Exception {
220         Context context = InstrumentationRegistry.getInstrumentation().getContext();
221         SharedPreferences prefs = context.getSharedPreferences("test", 0);
222         int sessionId = prefs.getInt("sessionId", -1);
223         assertThat(InstallUtils.getStagedSessionInfo(sessionId)).isStagedSessionApplied();
224     }
225 
226     @Test
testSessionCleanUp_Single_Expire_CleanUp()227     public void testSessionCleanUp_Single_Expire_CleanUp() throws Exception {
228         Context context = InstrumentationRegistry.getInstrumentation().getContext();
229         SharedPreferences prefs = context.getSharedPreferences("test", 0);
230         int sessionId = prefs.getInt("sessionId", -1);
231         assertSessionNotExists(sessionId);
232     }
233 
234     @Test
testSessionCleanUp_Multi_Expire_Install()235     public void testSessionCleanUp_Multi_Expire_Install() throws Exception {
236         int parentId = Install.multi(TestApp.A1, TestApp.B1).setStaged().commit();
237         int[] childIds;
238         try (PackageInstaller.Session parent = openPackageInstallerSession(parentId)) {
239             childIds = parent.getChildSessionIds();
240         }
241 
242         Context context = InstrumentationRegistry.getInstrumentation().getContext();
243         SharedPreferences prefs = context.getSharedPreferences("test", 0);
244         prefs.edit().putInt("parentId", parentId).commit();
245         prefs.edit().putInt("childId1", childIds[0]).commit();
246         prefs.edit().putInt("childId2", childIds[1]).commit();
247     }
248 
249     @Test
testSessionCleanUp_Multi_Expire_VerifyInstall()250     public void testSessionCleanUp_Multi_Expire_VerifyInstall() throws Exception {
251         Context context = InstrumentationRegistry.getInstrumentation().getContext();
252         SharedPreferences prefs = context.getSharedPreferences("test", 0);
253         int parentId = prefs.getInt("parentId", -1);
254         assertThat(InstallUtils.getStagedSessionInfo(parentId)).isStagedSessionApplied();
255     }
256 
257     @Test
testSessionCleanUp_Multi_Expire_CleanUp()258     public void testSessionCleanUp_Multi_Expire_CleanUp() throws Exception {
259         Context context = InstrumentationRegistry.getInstrumentation().getContext();
260         SharedPreferences prefs = context.getSharedPreferences("test", 0);
261         int parentId = prefs.getInt("parentId", -1);
262         int childId1 = prefs.getInt("childId1", -1);
263         int childId2 = prefs.getInt("childId2", -1);
264         assertSessionNotExists(parentId);
265         assertSessionNotExists(childId1);
266         assertSessionNotExists(childId2);
267     }
268 
269     @Test
testSessionCleanUp_LowStorage_Install()270     public void testSessionCleanUp_LowStorage_Install() throws Exception {
271         int parentId = Install.multi(TestApp.A1, TestApp.B1).createSession();
272         int[] childIds;
273         try (PackageInstaller.Session parent = openPackageInstallerSession(parentId)) {
274             childIds = parent.getChildSessionIds();
275         }
276 
277         Context context = InstrumentationRegistry.getInstrumentation().getContext();
278         SharedPreferences prefs = context.getSharedPreferences("test", 0);
279         prefs.edit().putInt("parentId", parentId).commit();
280         prefs.edit().putInt("childId1", childIds[0]).commit();
281         prefs.edit().putInt("childId2", childIds[1]).commit();
282     }
283 
284     @Test
testSessionCleanUp_LowStorage_CleanUp()285     public void testSessionCleanUp_LowStorage_CleanUp() throws Exception {
286         Context context = InstrumentationRegistry.getInstrumentation().getContext();
287         // Pass Long.MAX_VALUE to ensure old sessions will be abandoned
288         context.getPackageManager().freeStorage(Long.MAX_VALUE, null);
289         SharedPreferences prefs = context.getSharedPreferences("test", 0);
290         int parentId = prefs.getInt("parentId", -1);
291         int childId1 = prefs.getInt("childId1", -1);
292         int childId2 = prefs.getInt("childId2", -1);
293         assertSessionNotExists(parentId);
294         assertSessionNotExists(childId1);
295         assertSessionNotExists(childId2);
296     }
297 }
298