• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.functional.permissiontests;
18 
19 import android.app.UiAutomation;
20 import android.content.Context;
21 import android.content.pm.PackageInfo;
22 import android.content.pm.PackageManager;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.os.ParcelFileDescriptor;
25 import android.os.SystemClock;
26 import android.support.test.launcherhelper.ILauncherStrategy;
27 import android.support.test.launcherhelper.LauncherStrategyFactory;
28 import android.support.test.uiautomator.By;
29 import android.support.test.uiautomator.Direction;
30 import android.support.test.uiautomator.UiDevice;
31 import android.support.test.uiautomator.UiObject2;
32 import android.support.test.uiautomator.Until;
33 import android.util.Log;
34 
35 import junit.framework.Assert;
36 
37 import java.io.BufferedReader;
38 import java.io.FileInputStream;
39 import java.io.IOException;
40 import java.io.InputStreamReader;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Hashtable;
44 import java.util.List;
45 
46 public class PermissionHelper {
47     public static final String TEST_TAG = "PermissionTest";
48     public static final String SETTINGS_PACKAGE = "com.android.settings";
49     public final int TIMEOUT = 500;
50     public static PermissionHelper mInstance = null;
51     private UiDevice mDevice;
52     private Context mContext;
53     private static UiAutomation mUiAutomation;
54     public static Hashtable<String, List<String>> mPermissionGroupInfo = null;
55     ILauncherStrategy mLauncherStrategy;
56 
PermissionHelper(UiDevice device, Context context, UiAutomation uiAutomation)57     private PermissionHelper(UiDevice device, Context context, UiAutomation uiAutomation) {
58         mDevice = device;
59         mContext = context;
60         mUiAutomation = uiAutomation;
61         mLauncherStrategy = LauncherStrategyFactory.getInstance(mDevice).getLauncherStrategy();
62     }
63 
getInstance(UiDevice device, Context context, UiAutomation uiAutomation)64     public static PermissionHelper getInstance(UiDevice device, Context context,
65             UiAutomation uiAutomation) {
66         if (mInstance == null) {
67             mInstance = new PermissionHelper(device, context, uiAutomation);
68             PermissionHelper.populateDangerousPermissionGroupInfo();
69         }
70         return mInstance;
71     }
72 
73     /**
74      * Returns list of all dangerous permission of the system
75      */
populateDangerousPermissionGroupInfo()76     private static void populateDangerousPermissionGroupInfo() {
77         ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand("pm list permissions -g -d");
78         try (BufferedReader reader = new BufferedReader(
79                 new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) {
80             String line;
81             List<String> permissions = new ArrayList<String>();
82             String groupName = null;
83             while ((line = reader.readLine()) != null) {
84                 if (line.isEmpty()) {
85                     // Do nothing
86                 } else if (line.startsWith("group")) {
87                     if (mPermissionGroupInfo == null) {
88                         mPermissionGroupInfo = new Hashtable<String, List<String>>();
89                     } else {
90                         mPermissionGroupInfo.put(groupName, permissions);
91                         permissions = new ArrayList<String>();
92                     }
93                     groupName = line.split(":")[1];
94                 } else if (line.startsWith("  permission:")) {
95                     permissions.add(line.split(":")[1]);
96                 }
97             }
98             mPermissionGroupInfo.put(groupName, permissions);
99         } catch (IOException e) {
100             Log.e(TEST_TAG, e.getMessage());
101         }
102     }
103 
104     /**
105      * Returns list of permission asked by package
106      * @param packageName : PackageName for which permission list to be returned
107      * @param permitted : set 'true' for normal and default granted dangerous permissions, 'false'
108      * otherwise
109      * @return
110      */
getPermissionByPackage(String packageName, Boolean permitted)111     public List<String> getPermissionByPackage(String packageName, Boolean permitted) {
112         List<String> selectedPermissions = new ArrayList<String>();
113         String[] requestedPermissions = null;
114         int[] requestedPermissionFlags = null;
115         PackageInfo packageInfo = null;
116         try {
117             packageInfo = getPackageManager().getPackageInfo(packageName,
118                     PackageManager.GET_PERMISSIONS);
119         } catch (NameNotFoundException e) {
120             throw new RuntimeException(String.format("%s package isn't found", packageName));
121         }
122 
123         requestedPermissions = packageInfo.requestedPermissions;
124         requestedPermissionFlags = packageInfo.requestedPermissionsFlags;
125         for (int i = 0; i < requestedPermissions.length; ++i) {
126             // requestedPermissionFlags 1 = Denied, 3 = Granted
127             if (permitted && requestedPermissionFlags[i] == 3) {
128                 selectedPermissions.add(requestedPermissions[i]);
129             } else if (!permitted && requestedPermissionFlags[i] == 1) {
130                 selectedPermissions.add(requestedPermissions[i]);
131             }
132         }
133         return selectedPermissions;
134     }
135 
136     /**
137      * Verify any dangerous permission not mentioned in manifest aren't granted
138      * @param packageName
139      * @param permittedGroups
140      */
verifyExtraDangerousPermissionNotGranted(String packageName, String[] permittedGroups)141     public void verifyExtraDangerousPermissionNotGranted(String packageName,
142             String[] permittedGroups) {
143         List<String> allPermittedDangerousPermsList = getAllDangerousPermissionsByPermGrpNames(
144                 permittedGroups);
145         List<String> allPermissionsForPackageList = getPermissionByPackage(packageName,
146                 Boolean.TRUE);
147         List<String> allPlatformDangerousPermissionList = getPlatformDangerousPermissionGroupNames();
148         allPermissionsForPackageList.retainAll(allPlatformDangerousPermissionList);
149         allPermissionsForPackageList.removeAll(allPermittedDangerousPermsList);
150         Assert.assertTrue(
151                 String.format("For package %s some extra dangerous permissions have been granted",
152                         packageName),
153                 allPermissionsForPackageList.isEmpty());
154     }
155 
156     /**
157      * Verify any dangerous permission mentioned in manifest that is not default for privileged app
158      * isn't granted. Example: Location permission for Camera app
159      * @param packageName
160      * @param notPermittedGroups
161      */
verifyNotPermittedDangerousPermissionDenied(String packageName, String[] notPermittedGroups)162     public void verifyNotPermittedDangerousPermissionDenied(String packageName,
163             String[] notPermittedGroups) {
164         List<String> allNotPermittedDangerousPermsList = getAllDangerousPermissionsByPermGrpNames(
165                 notPermittedGroups);
166         List<String> allPermissionsForPackageList = getPermissionByPackage(packageName,
167                 Boolean.TRUE);
168         int allNotPermittedDangerousPermsCount = allNotPermittedDangerousPermsList.size();
169         allNotPermittedDangerousPermsList.removeAll(allPermissionsForPackageList);
170         Assert.assertTrue(
171                 String.format("For package %s not permissible dangerous permissions been granted",
172                         packageName),
173                 allNotPermittedDangerousPermsList.size() == allNotPermittedDangerousPermsCount);
174     }
175 
176     /**
177      * Verify any normal permission mentioned in manifest is auto granted
178      * @param packageName
179      */
verifyNormalPermissionsAutoGranted(String packageName)180     public void verifyNormalPermissionsAutoGranted(String packageName) {
181         List<String> allDeniedPermissionsForPackageList = getPermissionByPackage(packageName,
182                 Boolean.FALSE);
183         List<String> allPlatformDangerousPermissionList = getPlatformDangerousPermissionGroupNames();
184         allDeniedPermissionsForPackageList.removeAll(allPlatformDangerousPermissionList);
185         if (!allDeniedPermissionsForPackageList.isEmpty()) {
186             for (int i = 0; i < allDeniedPermissionsForPackageList.size(); ++i) {
187                 Log.d(TEST_TAG, String.format("%s should have been auto granted",
188                         allDeniedPermissionsForPackageList.get(i)));
189             }
190         }
191         Assert.assertTrue(
192                 String.format("For package %s few normal permission have been denied", packageName),
193                 allDeniedPermissionsForPackageList.isEmpty());
194     }
195 
196     /**
197      * Verifies via UI that a permission is set/unset for an app
198      * @param appName
199      * @param permission
200      * @param expected : 'ON' or 'OFF'
201      * @return
202      */
verifyPermissionSettingStatus(String appName, String permission, PermissionStatus expected)203     public Boolean verifyPermissionSettingStatus(String appName, String permission,
204             PermissionStatus expected) {
205         if (!expected.equals(PermissionStatus.ON) && !expected.equals(PermissionStatus.OFF)) {
206             throw new RuntimeException(String.format("%s isn't valid permission status", expected));
207         }
208         openAppPermissionView(appName);
209         UiObject2 permissionView = mDevice
210                 .wait(Until.findObject(By.res("android:id/list_container")), TIMEOUT);
211         List<UiObject2> permissionsList = permissionView.getChildren().get(0).getChildren();
212         for (UiObject2 permDesc : permissionsList) {
213             if (permDesc.getChildren().get(1).getChildren().get(0).getText().equals(permission)) {
214                 String status = permDesc.getChildren().get(2).getChildren().get(0).getText();
215                 return status.equals(expected.toString().toUpperCase());
216             }
217         }
218         Assert.fail("Permission is not found");
219         return Boolean.FALSE;
220     }
221 
222     /**
223      * Verify default dangerous permission mentioned in manifest for system privileged apps are auto
224      * permitted Example: Camera permission for Camera app
225      * @param packageName
226      * @param permittedGroups
227      */
verifyDefaultDangerousPermissionGranted(String packageName, String[] permittedGroups, Boolean byGroup)228     public void verifyDefaultDangerousPermissionGranted(String packageName,
229             String[] permittedGroups,
230             Boolean byGroup) {
231         List<String> allPermittedDangerousPermsList = new ArrayList<String>();
232         if (byGroup) {
233             allPermittedDangerousPermsList = getAllDangerousPermissionsByPermGrpNames(
234                     permittedGroups);
235         } else {
236             allPermittedDangerousPermsList.addAll(Arrays.asList(permittedGroups));
237         }
238 
239         List<String> allPermissionsForPackageList = getPermissionByPackage(packageName,
240                 Boolean.TRUE);
241         allPermittedDangerousPermsList.removeAll(allPermissionsForPackageList);
242         for (String permission : allPermittedDangerousPermsList) {
243             Log.d(TEST_TAG,
244                     String.format("%s - > %s hasn't been granted yet", packageName, permission));
245         }
246         Assert.assertTrue(String.format("For %s some Permissions aren't granted yet", packageName),
247                 allPermittedDangerousPermsList.isEmpty());
248     }
249 
250     /**
251      * For a given app, opens the permission settings window settings -> apps -> permissions
252      * @param appName
253      */
openAppPermissionView(String appName)254     public void openAppPermissionView(String appName) {
255         mDevice.pressHome();
256         launchApp(SETTINGS_PACKAGE, "Settings");
257         UiObject2 app = null;
258         UiObject2 view = null;
259         int maxAttempt = 5;
260         while ((maxAttempt-- > 0)
261                 && ((app = mDevice.wait(Until.findObject(By.res("android:id/title").text("Apps")),
262                         TIMEOUT)) == null)) {
263             view = mDevice.wait(Until.findObject(By.res(SETTINGS_PACKAGE, "main_content")),
264                     TIMEOUT);
265             // todo scroll may be different for device and build
266             view.scroll(Direction.DOWN, 1.0f);
267         }
268 
269         mDevice.wait(Until.findObject(By.res("android:id/title").text("Apps")),
270                 TIMEOUT)
271                 .clickAndWait(Until.newWindow(), TIMEOUT);
272         app = null;
273         view = null;
274         maxAttempt = 10;
275         while ((maxAttempt-- > 0)
276                 && ((app = mDevice.wait(Until.findObject(By.res("android:id/title").text(appName)),
277                         TIMEOUT)) == null)) {
278             view = mDevice.wait(Until.findObject(By.res("com.android.settings:id/main_content")),
279                     TIMEOUT);
280             // todo scroll may be different for device and build
281             view.scroll(Direction.DOWN, 1.0f);
282         }
283         app.clickAndWait(Until.newWindow(), TIMEOUT);
284         mDevice.wait(Until.findObject(By.res("android:id/title").text("Permissions")),
285                 TIMEOUT).clickAndWait(Until.newWindow(), TIMEOUT);
286     }
287 
288     /**
289      * Toggles permission for an app via UI
290      * @param appName
291      * @param permission
292      * @param toBeSet
293      */
togglePermissionSetting(String appName, String permission, Boolean toBeSet)294     public void togglePermissionSetting(String appName, String permission, Boolean toBeSet) {
295         openAppPermissionView(appName);
296         UiObject2 permissionView = mDevice
297                 .wait(Until.findObject(By.res("android:id/list_container")), TIMEOUT);
298         List<UiObject2> permissionsList = permissionView.getChildren().get(0).getChildren();
299         for (UiObject2 obj : permissionsList) {
300             if (obj.getChildren().get(1).getChildren().get(0).getText().equals(permission)) {
301                 String status = obj.getChildren().get(2).getChildren().get(0).getText();
302                 if ((toBeSet && !status.equals(PermissionStatus.ON.toString()))
303                         || (!toBeSet && status.equals(PermissionStatus.ON.toString()))) {
304                     obj.getChildren().get(2).getChildren().get(0).click();
305                     mDevice.waitForIdle();
306                 }
307                 break;
308             }
309         }
310     }
311 
312     /**
313      * Grant or revoke permission via adb command
314      * @param packageName
315      * @param permissionName
316      * @param permissionOp : Accepted values are 'grant' and 'revoke'
317      */
grantOrRevokePermissionViaAdb(String packageName, String permissionName, PermissionOp permissionOp)318     public void grantOrRevokePermissionViaAdb(String packageName, String permissionName,
319             PermissionOp permissionOp) {
320         if (permissionOp == null) {
321             throw new RuntimeException("null operation can't be executed");
322         }
323         String command = String.format("pm %s %s %s", permissionOp.toString().toLowerCase(),
324                 packageName, permissionName);
325         Log.d(TEST_TAG, String.format("executing - %s", command));
326         mUiAutomation.executeShellCommand(command);
327         mDevice.waitForIdle();
328     }
329 
330     /**
331      * returns list of specific permissions in a dangerous permission group
332      * @param permissionGroupsToCheck
333      * @return
334      */
getAllDangerousPermissionsByPermGrpNames(String[] permissionGroupsToCheck)335     public List<String> getAllDangerousPermissionsByPermGrpNames(String[] permissionGroupsToCheck) {
336         List<String> allDangerousPermissions = new ArrayList<String>();
337         for (String s : permissionGroupsToCheck) {
338             String grpName = String.format("android.permission-group.%s", s.toUpperCase());
339             if (PermissionHelper.mPermissionGroupInfo.keySet().contains(grpName)) {
340                 allDangerousPermissions.addAll(PermissionHelper.mPermissionGroupInfo.get(grpName));
341             }
342         }
343 
344         return allDangerousPermissions;
345     }
346 
347     /**
348      * Returns platform dangerous permission group names
349      * @return
350      */
getPlatformDangerousPermissionGroupNames()351     public List<String> getPlatformDangerousPermissionGroupNames() {
352         List<String> allDangerousPermissions = new ArrayList<String>();
353         for (List<String> prmsList : PermissionHelper.mPermissionGroupInfo.values()) {
354             allDangerousPermissions.addAll(prmsList);
355         }
356         return allDangerousPermissions;
357     }
358 
359     /**
360      * To ensure that all default dangerous permissions mentioned in manifest are granted for any
361      * privileged app
362      * @param packageName
363      * @param granted
364      * @param denied
365      */
ensureAppHasDefaultPermissions(String packageName, String[] granted, String[] denied)366     public void ensureAppHasDefaultPermissions(String packageName, String[] granted,
367             String[] denied) {
368         List<String> defaultGranted = getAllDangerousPermissionsByPermGrpNames(granted);
369         List<String> currentGranted = getPermissionByPackage(packageName, Boolean.TRUE);
370         List<String> defaultDenied = getAllDangerousPermissionsByPermGrpNames(denied);
371         List<String> currentDenied = getPermissionByPackage(packageName, Boolean.FALSE);
372         defaultGranted.removeAll(currentGranted);
373         for (String permission : defaultGranted) {
374             grantOrRevokePermissionViaAdb(packageName, permission, PermissionOp.GRANT);
375         }
376         defaultDenied.removeAll(currentDenied);
377         for (String permission : defaultDenied) {
378             grantOrRevokePermissionViaAdb(packageName, permission, PermissionOp.REVOKE);
379         }
380     }
381 
382     /**
383      * Get permission description via UI
384      * @param appName
385      * @return
386      */
getPermissionDescGroupNames(String appName)387     public List<String> getPermissionDescGroupNames(String appName) {
388         List<String> groupNames = new ArrayList<String>();
389         openAppPermissionView(appName);
390         mDevice.wait(Until.findObject(By.desc("More options")), TIMEOUT).click();
391         mDevice.wait(Until.findObject(By.text("All permissions")), TIMEOUT).click();
392         UiObject2 permissionsListView = mDevice
393                 .wait(Until.findObject(By.res("android:id/list_container")), TIMEOUT);
394         List<UiObject2> permissionList = permissionsListView
395                 .findObjects(By.clazz("android.widget.TextView"));
396         for (UiObject2 obj : permissionList) {
397             if (obj.getText() != null && obj.getText() != "" && obj.getVisibleBounds().left == 0) {
398                 if (obj.getText().equals("Other app capabilities"))
399                     break;
400                 groupNames.add(obj.getText().toUpperCase());
401             }
402         }
403         return groupNames;
404     }
405 
getPackageManager()406     public PackageManager getPackageManager() {
407         return mContext.getPackageManager();
408     }
409 
launchApp(String packageName, String appName)410     public void launchApp(String packageName, String appName) {
411         if (!mDevice.hasObject(By.pkg(packageName).depth(0))) {
412             mLauncherStrategy.launch(appName, packageName);
413         }
414     }
415 
cleanPackage(String packageName)416     public void cleanPackage(String packageName) {
417             mUiAutomation.executeShellCommand(String.format("pm clear %s", packageName));
418             SystemClock.sleep(2 * TIMEOUT);
419     }
420 
421     /** Supported operations on permission */
422     public enum PermissionOp {
423         GRANT, REVOKE;
424     }
425 
426     /** Available permission status */
427     public enum PermissionStatus {
428         ON, OFF;
429     }
430 }
431