1 /*
2  * Copyright (C) 2015 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.commands.monkey;
18 
19 import android.Manifest;
20 import android.content.pm.ApplicationInfo;
21 import android.content.pm.IPackageManager;
22 import android.content.pm.PackageInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.PermissionInfo;
25 import android.os.Build;
26 import android.os.RemoteException;
27 import android.os.ServiceManager;
28 import android.os.UserHandle;
29 
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Random;
35 
36 /**
37  * Utility class that encapsulates runtime permission related methods for monkey
38  *
39  */
40 public class MonkeyPermissionUtil {
41 
42     private static final String PERMISSION_PREFIX = "android.permission.";
43     private static final String PERMISSION_GROUP_PREFIX = "android.permission-group.";
44 
45     // from com.android.packageinstaller.permission.utils
46     private static final String[] MODERN_PERMISSION_GROUPS = {
47             Manifest.permission_group.CALENDAR, Manifest.permission_group.CAMERA,
48             Manifest.permission_group.CONTACTS, Manifest.permission_group.LOCATION,
49             Manifest.permission_group.SENSORS, Manifest.permission_group.SMS,
50             Manifest.permission_group.PHONE, Manifest.permission_group.MICROPHONE,
51             Manifest.permission_group.STORAGE
52     };
53 
54     // from com.android.packageinstaller.permission.utils
isModernPermissionGroup(String name)55     private static boolean isModernPermissionGroup(String name) {
56         for (String modernGroup : MODERN_PERMISSION_GROUPS) {
57             if (modernGroup.equals(name)) {
58                 return true;
59             }
60         }
61         return false;
62     }
63 
64     /**
65      * actual list of packages to target, with invalid packages excluded, and may optionally include
66      * system packages
67      */
68     private List<String> mTargetedPackages;
69     /** if we should target system packages regardless if they are listed */
70     private boolean mTargetSystemPackages;
71     private IPackageManager mPm;
72 
73     /** keep track of runtime permissions requested for each package targeted */
74     private Map<String, List<PermissionInfo>> mPermissionMap;
75 
MonkeyPermissionUtil()76     public MonkeyPermissionUtil() {
77         mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
78     }
79 
setTargetSystemPackages(boolean targetSystemPackages)80     public void setTargetSystemPackages(boolean targetSystemPackages) {
81         mTargetSystemPackages = targetSystemPackages;
82     }
83 
84     /**
85      * Decide if a package should be targeted by permission monkey
86      * @param info
87      * @return
88      */
shouldTargetPackage(PackageInfo info)89     private boolean shouldTargetPackage(PackageInfo info) {
90         // target if permitted by white listing / black listing rules
91         if (MonkeyUtils.getPackageFilter().checkEnteringPackage(info.packageName)) {
92             return true;
93         }
94         if (mTargetSystemPackages
95                 // not explicitly black listed
96                 && !MonkeyUtils.getPackageFilter().isPackageInvalid(info.packageName)
97                 // is a system app
98                 && (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
99             return true;
100         }
101         return false;
102     }
103 
shouldTargetPermission(String pkg, PermissionInfo pi)104     private boolean shouldTargetPermission(String pkg, PermissionInfo pi) throws RemoteException {
105         int flags = mPm.getPermissionFlags(pi.name, pkg, UserHandle.myUserId());
106         int fixedPermFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
107                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
108         return pi.group != null && pi.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS
109                 && ((flags & fixedPermFlags) == 0)
110                 && isModernPermissionGroup(pi.group);
111     }
112 
populatePermissionsMapping()113     public boolean populatePermissionsMapping() {
114         mPermissionMap = new HashMap<>();
115         try {
116             List<?> pkgInfos = mPm.getInstalledPackages(
117                     PackageManager.GET_PERMISSIONS, UserHandle.myUserId()).getList();
118             for (Object o : pkgInfos) {
119                 PackageInfo info = (PackageInfo)o;
120                 if (!shouldTargetPackage(info)) {
121                     continue;
122                 }
123                 List<PermissionInfo> permissions = new ArrayList<>();
124                 if (info.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
125                     // skip apps targetting lower API level
126                     continue;
127                 }
128                 if (info.requestedPermissions == null) {
129                     continue;
130                 }
131                 for (String perm : info.requestedPermissions) {
132                     PermissionInfo pi = mPm.getPermissionInfo(perm, "shell", 0);
133                     if (pi != null && shouldTargetPermission(info.packageName, pi)) {
134                         permissions.add(pi);
135                     }
136                 }
137                 if (!permissions.isEmpty()) {
138                     mPermissionMap.put(info.packageName, permissions);
139                 }
140             }
141         } catch (RemoteException re) {
142             Logger.err.println("** Failed talking with package manager!");
143             return false;
144         }
145         if (!mPermissionMap.isEmpty()) {
146             mTargetedPackages = new ArrayList<>(mPermissionMap.keySet());
147         }
148         return true;
149     }
150 
dump()151     public void dump() {
152         Logger.out.println("// Targeted packages and permissions:");
153         for (Map.Entry<String, List<PermissionInfo>> e : mPermissionMap.entrySet()) {
154             Logger.out.println(String.format("//  + Using %s", e.getKey()));
155             for (PermissionInfo pi : e.getValue()) {
156                 String name = pi.name;
157                 if (name != null) {
158                     if (name.startsWith(PERMISSION_PREFIX)) {
159                         name = name.substring(PERMISSION_PREFIX.length());
160                     }
161                 }
162                 String group = pi.group;
163                 if (group != null) {
164                     if (group.startsWith(PERMISSION_GROUP_PREFIX)) {
165                         group = group.substring(PERMISSION_GROUP_PREFIX.length());
166                     }
167                 }
168                 Logger.out.println(String.format("//    Permission: %s [%s]", name, group));
169             }
170         }
171     }
172 
generateRandomPermissionEvent(Random random)173     public MonkeyPermissionEvent generateRandomPermissionEvent(Random random) {
174         String pkg = mTargetedPackages.get(random.nextInt(mTargetedPackages.size()));
175         List<PermissionInfo> infos = mPermissionMap.get(pkg);
176         return new MonkeyPermissionEvent(pkg, infos.get(random.nextInt(infos.size())));
177     }
178 }
179