1 /*
2  * Copyright (C) 2022 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 android.appenumeration.cts;
18 
19 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 import static com.google.common.truth.Truth.assertWithMessage;
23 
24 import android.content.Context;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageInstaller;
27 import android.content.pm.PackageManager;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.content.pm.PackageManager.PackageInfoFlags;
30 import android.os.Bundle;
31 
32 import androidx.annotation.Nullable;
33 import androidx.test.platform.app.InstrumentationRegistry;
34 
35 import com.android.bedstead.nene.users.UserReference;
36 import com.android.cts.install.lib.InstallUtils;
37 import com.android.cts.install.lib.LocalIntentSender;
38 
39 import org.junit.Assert;
40 
41 import java.io.File;
42 import java.util.List;
43 
44 public class Utils {
45 
46     private static Context sContext;
47     private static PackageManager sPm;
48 
49     interface Result {
await()50         Bundle await() throws Exception;
51     }
52 
53     interface ThrowingBiFunction<T, U, R> {
apply(T arg1, U arg2)54         R apply(T arg1, U arg2) throws Exception;
55     }
56 
57     interface ThrowingFunction<T, R> {
apply(T arg1)58         R apply(T arg1) throws Exception;
59     }
60 
getContext()61     private static Context getContext() {
62         if (sContext == null) {
63             sContext = InstrumentationRegistry.getInstrumentation().getContext();
64         }
65         return sContext;
66     }
67 
getPackageManager()68     private static PackageManager getPackageManager() {
69         if (sPm == null) {
70             sPm = getContext().getPackageManager();
71         }
72         return sPm;
73     }
74 
75     /**
76      * Install the APK on all users.
77      **/
installPackage(String apkPath)78     static void installPackage(String apkPath) {
79         installPackageForUser(apkPath, null /* user */, null /* installerPackageName */);
80     }
81 
82     /**
83      * Install the APK on all users with specific installer.
84      **/
installPackage(String apkPath, String installerPackageName)85     static void installPackage(String apkPath, String installerPackageName) {
86         installPackageForUser(apkPath, null /* user */, installerPackageName);
87     }
88 
89     /**
90      * Install the APK on the given user.
91      **/
installPackageForUser(String apkPath, UserReference user)92     static void installPackageForUser(String apkPath, UserReference user) {
93         installPackageForUser(apkPath, user, getContext().getPackageName());
94     }
95 
96     /**
97      * Install the APK on the given user with specific installer.
98      **/
installPackageForUser(String apkPath, UserReference user, String installerPackageName)99     private static void installPackageForUser(String apkPath, UserReference user,
100             String installerPackageName) {
101         assertWithMessage(apkPath).that(new File(apkPath).exists()).isTrue();
102         final StringBuilder cmd = new StringBuilder("pm install ");
103         if (user != null) {
104             cmd.append("--user ").append(user.id()).append(" ");
105         }
106         if (installerPackageName != null) {
107             cmd.append("-i ").append(installerPackageName).append(" ");
108         }
109         cmd.append(apkPath);
110         final String result = runShellCommand(cmd.toString()).trim();
111         assertWithMessage(result).that(result).contains("Success");
112     }
113 
114     /**
115      * Install the existing package for the APK on the given user.
116      **/
installExistPackageForUser(String apkPath, UserReference user)117     static void installExistPackageForUser(String apkPath, UserReference user) {
118         final StringBuilder cmd = new StringBuilder("pm install-existing ");
119         // If the user reference is null, it only effects on the current user.
120         if (user != null) {
121             cmd.append("--user ").append(user.id()).append(" ");
122         }
123         cmd.append(apkPath);
124         final String result = runShellCommand(cmd.toString());
125         assertThat(result.trim()).contains("installed");
126     }
127 
128     /**
129      * Uninstall the package on all users.
130      **/
uninstallPackage(String packageName)131     static void uninstallPackage(String packageName) {
132         uninstallPackageForUser(packageName, null /* user */);
133     }
134 
135     /**
136      * Uninstall the package on the given user.
137      **/
uninstallPackageForUser(String packageName, UserReference user)138     static void uninstallPackageForUser(String packageName, UserReference user) {
139         final StringBuilder cmd = new StringBuilder("pm uninstall ");
140         // If the user reference is null, it effects on all users.
141         if (user != null) {
142             cmd.append("--user ").append(user.id()).append(" ");
143         }
144         cmd.append(packageName);
145         runShellCommand(cmd.toString());
146     }
147 
148     /**
149      * Force stopping the processes of the package on all users.
150      **/
forceStopPackage(String packageName)151     static void forceStopPackage(String packageName) {
152         forceStopPackageForUser(packageName, null /* user */);
153     }
154 
155     /**
156      * Force stopping the processes of the package on the given user.
157      **/
forceStopPackageForUser(String packageName, UserReference user)158     static void forceStopPackageForUser(String packageName, UserReference user) {
159         final StringBuilder cmd = new StringBuilder("am force-stop ");
160         // If the user reference is null, it effects on all users.
161         if (user != null) {
162             cmd.append("--user ").append(user.id()).append(" ");
163         }
164         cmd.append(packageName);
165         runShellCommand(cmd.toString());
166     }
167 
168     /**
169      * Clear app data of the given package on the given user.
170      **/
clearAppDataForUser(String packageName, UserReference user)171     static void clearAppDataForUser(String packageName, UserReference user) {
172         final StringBuilder cmd = new StringBuilder("pm clear ");
173         // If the user reference is null, it only effects on the system user.
174         if (user != null) {
175             cmd.append("--user ").append(user.id()).append(" ");
176         }
177         cmd.append(packageName);
178         runShellCommand(cmd.toString());
179     }
180 
181     /**
182      * Suspend/Unsuspend the packages on the current user.
183      **/
suspendPackages(boolean suspend, List<String> packages)184     static void suspendPackages(boolean suspend, List<String> packages) {
185         suspendPackagesForUser(suspend, packages, UserReference.of(getContext().getUser()),
186                 false /* extraPersistableBundle */);
187     }
188 
189     /**
190      * Suspend/Unsuspend the packages on the given user.
191      **/
suspendPackagesForUser(boolean suspend, List<String> packages, UserReference user, boolean extraPersistableBundle)192     static void suspendPackagesForUser(boolean suspend, List<String> packages,
193             UserReference user, boolean extraPersistableBundle) {
194         final StringBuilder cmd = new StringBuilder("pm ");
195         if (suspend) {
196             cmd.append("suspend").append(" ");
197         } else {
198             cmd.append("unsuspend").append(" ");
199         }
200         // If the user reference is null, it only effects on the system user.
201         if (user != null) {
202             cmd.append("--user ").append(user.id()).append(" ");
203         }
204         if (extraPersistableBundle) {
205             cmd.append("--les foo bar").append(" ");
206         }
207         packages.stream().forEach(p -> cmd.append(p).append(" "));
208         runShellCommand(cmd.toString());
209     }
210 
211     /**
212      * Ensure the given package is installed; otherwise install via the APK.
213      */
ensurePackageIsInstalled(String packageName, String apkPath)214     static void ensurePackageIsInstalled(String packageName, String apkPath) {
215         runShellCommand("pm install -R " + apkPath);
216         PackageInfo info = null;
217         try {
218             info = getPackageManager().getPackageInfo(packageName, PackageInfoFlags.of(0));
219         } catch (NameNotFoundException e) {
220             // Ignore
221         }
222         Assert.assertNotNull(packageName + " should be installed", info);
223     }
224 
225     /**
226      * Ensure the given package isn't install; otherwise uninstall it.
227      */
ensurePackageIsNotInstalled(String packageName)228     static void ensurePackageIsNotInstalled(String packageName) {
229         uninstallPackage(packageName);
230         PackageInfo info = null;
231         try {
232             info = getPackageManager().getPackageInfo(packageName, PackageInfoFlags.of(0));
233         } catch (NameNotFoundException e) {
234             // Expected
235         }
236         Assert.assertNull(packageName + " shouldn't be installed", info);
237     }
238 
239     /**
240      * Adopt the permission identity of the shell UID only for the provided permissions.
241      * @param permissions The permissions to adopt or <code>null</code> to adopt all.
242      */
adoptShellPermissions(@ullable String... permissions)243     static void adoptShellPermissions(@Nullable String... permissions) {
244         InstrumentationRegistry
245                 .getInstrumentation()
246                 .getUiAutomation()
247                 .adoptShellPermissionIdentity(permissions);
248     }
249 
250     /**
251      * Drop the shell permission identity adopted.
252      */
dropShellPermissions()253     static void dropShellPermissions() {
254         InstrumentationRegistry
255                 .getInstrumentation()
256                 .getUiAutomation()
257                 .dropShellPermissionIdentity();
258     }
259 
260     /**
261      * Attempt to commit everything staged in this session.
262      */
commitSession(int sessionId)263     static void commitSession(int sessionId) throws Exception {
264         final PackageInstaller.Session session =
265                 InstallUtils.openPackageInstallerSession(sessionId);
266         final LocalIntentSender sender = new LocalIntentSender();
267         session.commit(sender.getIntentSender());
268         InstallUtils.assertStatusSuccess(sender.getResult());
269     }
270 
271     /**
272      * Abandon all the sessions owned by you, destroying all staged data and rendering them invalid.
273      */
cleanUpMySessions()274     static void cleanUpMySessions() {
275         InstallUtils.getPackageInstaller().getMySessions().forEach(info -> {
276             try {
277                 InstallUtils.getPackageInstaller().abandonSession(info.getSessionId());
278             } catch (Exception e) {
279                 // Ignore
280             }
281         });
282     }
283 
284     /**
285      * Grants access to APIs marked as {@code @TestApi}.
286      */
allowTestApiAccess(String pgkName)287     static void allowTestApiAccess(String pgkName)  {
288         final StringBuilder cmd = new StringBuilder("am compat enable ALLOW_TEST_API_ACCESS ");
289         cmd.append(pgkName);
290         final String result = runShellCommand(cmd.toString()).trim();
291         assertWithMessage(result).that(result).startsWith("Enabled change");
292     }
293 
294     /**
295      * Resets access to APIs marked as {@code @TestApi}.
296      */
resetTestApiAccess(String pgkName)297     static void resetTestApiAccess(String pgkName)  {
298         final StringBuilder cmd = new StringBuilder("am compat reset ALLOW_TEST_API_ACCESS ");
299         cmd.append(pgkName);
300         final String result = runShellCommand(cmd.toString()).trim();
301         assertWithMessage(result).that(result).startsWith("Reset change");
302     }
303 }
304