• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.compatibility.common.deviceinfo;
17 
18 import android.Manifest;
19 import android.annotation.TargetApi;
20 import android.app.admin.DevicePolicyManager;
21 import android.app.role.RoleManager;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PermissionInfo;
28 import android.os.Build;
29 import android.os.Process;
30 
31 import com.android.compatibility.common.util.DeviceInfoStore;
32 import com.android.compatibility.common.util.PackageUtil;
33 
34 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
35 
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Set;
43 
44 /**
45  * PackageDeviceInfo collector.
46  */
47 @TargetApi(Build.VERSION_CODES.N)
48 public class PackageDeviceInfo extends DeviceInfo {
49 
50     private static final String PLATFORM = "android";
51     private static final String PLATFORM_ANDROID_PERMISSION_PREFIX = "android.permission.";
52     private static final String PLATFORM_MANIFEST_PERMISSION_PREFIX = "android.Manifest.permission.";
53 
54     private static final String PACKAGE = "package";
55     private static final String NAME = "name";
56     private static final String VERSION_NAME = "version_name";
57     private static final String DIR = "dir";
58     private static final String SYSTEM_PRIV = "system_priv";
59     private static final String MIN_SDK = "min_sdk";
60     private static final String TARGET_SDK = "target_sdk";
61 
62     private static final String REQUESTED_PERMISSIONS = "requested_permissions";
63     private static final String DEFINED_PERMISSIONS = "defined_permissions";
64     private static final String PERMISSION_NAME = "name";
65     private static final String PERMISSION_FLAGS = "flags";
66     private static final String PERMISSION_GROUP = "permission_group";
67     private static final String PERMISSION_PROTECTION = "protection_level";
68     private static final String PERMISSION_PROTECTION_FLAGS = "protection_level_flags";
69     private static final String PERMISSION_IS_GRANTED = "is_granted";
70 
71 
72     private static final String PERMISSION_TYPE = "type";
73     private static final int PERMISSION_TYPE_SYSTEM = 1;
74     private static final int PERMISSION_TYPE_OEM = 2;
75     private static final int PERMISSION_TYPE_CUSTOM = 3;
76 
77     private static final String REQUESTED_ROLES = "requested_roles";
78     private static final String ROLE_NAME = "name";
79 
80     private static final String HAS_SYSTEM_UID = "has_system_uid";
81 
82     private static final String SHARES_INSTALL_PERMISSION = "shares_install_packages_permission";
83     private static final String INSTALL_PACKAGES_PERMISSION = "android.permission.INSTALL_PACKAGES";
84 
85     private static final String SHA256_CERT = "sha256_cert";
86 
87     private static final String SHA256_FILE = "sha256_file";
88 
89     private static final String CONFIG_NOTIFICATION_ACCESS = "config_defaultListenerAccessPackages";
90     private static final String HAS_DEFAULT_NOTIFICATION_ACCESS = "has_default_notification_access";
91 
92     private static final String UID = "uid";
93     private static final String IS_ACTIVE_ADMIN = "is_active_admin";
94 
95     private static final String CONFIG_ACCESSIBILITY_SERVICE = "config_defaultAccessibilityService";
96     private static final String DEFAULT_ACCESSIBILITY_SERVICE = "is_default_accessibility_service";
97 
98     private static final HashSet<String> ADDITIONAL_ANDROID_PERMISSIONS = new HashSet<>(Arrays.asList(new String[] {
99         "com.android.voicemail.permission.ADD_VOICEMAIL",
100         "com.android.voicemail.permission.WRITE_VOICEMAIL",
101         "com.android.voicemail.permission.READ_VOICEMAIL",
102         "com.android.browser.permission.READ_HISTORY_BOOKMARKS",
103         "com.android.browser.permission.WRITE_HISTORY_BOOKMARKS",
104         "com.android.alarm.permission.SET_ALARM",
105         "com.android.launcher.permission.INSTALL_SHORTCUT",
106         "com.android.launcher.permission.UNINSTALL_SHORTCUT",
107         "com.android.permission.INSTALL_EXISTING_PACKAGES",
108         "com.android.permission.USE_INSTALLER_V2",
109         "com.android.permission.USE_SYSTEM_DATA_LOADERS",
110         "android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"
111     }));
112 
113 
114     @Override
collectDeviceInfo(DeviceInfoStore store)115     protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
116         final PackageManager pm = getContext().getPackageManager();
117 
118         final List<PackageInfo> allPackages =
119                 pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
120         final Set<String> defaultNotificationListeners =
121                 getColonSeparatedPackageList(CONFIG_NOTIFICATION_ACCESS);
122 
123         final Set<String> deviceAdminPackages = getActiveDeviceAdminPackages();
124 
125         final ComponentName defaultAccessibilityComponent = getDefaultAccessibilityComponent();
126 
127         final HashMap<String, List<String>> packageRolesData = getPackageRolesData();
128 
129         // Platform permission data used to tag permissions information with sourcing information
130         final PackageInfo platformInfo = pm.getPackageInfo(PLATFORM , PackageManager.GET_PERMISSIONS);
131         final Set<String> platformPermissions = new HashSet<String>();
132         for (PermissionInfo permission : platformInfo.permissions) {
133           platformPermissions.add(permission.name);
134         }
135 
136         store.startArray(PACKAGE);
137         for (PackageInfo pkg : allPackages) {
138             store.startGroup();
139             store.addResult(NAME, pkg.packageName);
140             store.addResult(VERSION_NAME, pkg.versionName);
141 
142             collectRequestedPermissions(store, pm, platformPermissions, pkg);
143             collectDefinedPermissions(store, platformPermissions, pkg);
144 
145             collectionApplicationInfo(store, pm, pkg);
146 
147             store.addResult(HAS_DEFAULT_NOTIFICATION_ACCESS,
148                     defaultNotificationListeners.contains(pkg.packageName));
149 
150             store.addResult(IS_ACTIVE_ADMIN, deviceAdminPackages.contains(pkg.packageName));
151 
152             boolean isDefaultAccessibilityComponent = false;
153             if (defaultAccessibilityComponent != null) {
154               isDefaultAccessibilityComponent = pkg.packageName.equals(
155                       defaultAccessibilityComponent.getPackageName()
156               );
157             }
158             store.addResult(DEFAULT_ACCESSIBILITY_SERVICE, isDefaultAccessibilityComponent);
159 
160             String sha256_cert = PackageUtil.computePackageSignatureDigest(pkg.packageName);
161             store.addResult(SHA256_CERT, sha256_cert);
162 
163             String sha256_file = PackageUtil.computePackageFileDigest(pkg);
164             store.addResult(SHA256_FILE, sha256_file);
165 
166             collectRoles(store, packageRolesData, pkg);
167 
168             store.endGroup();
169         }
170         store.endArray(); // "package"
171     }
172 
collectRequestedPermissions(DeviceInfoStore store, PackageManager pm, Set<String> systemPermissions, PackageInfo pkg)173     private static void collectRequestedPermissions(DeviceInfoStore store,
174                                            PackageManager pm,
175                                            Set<String> systemPermissions,
176                                            PackageInfo pkg) throws IOException
177     {
178         store.startArray(REQUESTED_PERMISSIONS);
179         if (pkg.requestedPermissions != null && pkg.requestedPermissions.length > 0) {
180             for (String permission : pkg.requestedPermissions) {
181                 if (permission == null) continue;
182 
183                 try {
184                     final PermissionInfo pi = pm.getPermissionInfo(permission, 0);
185 
186                     store.startGroup();
187                     writePermissionsDetails(pi, store, systemPermissions);
188 
189                     boolean isGranted = pm.checkPermission(
190                             permission, pkg.packageName) == pm.PERMISSION_GRANTED;
191                     store.addResult(PERMISSION_IS_GRANTED, isGranted);
192 
193                     store.endGroup();
194                 } catch (PackageManager.NameNotFoundException e) {
195                     // ignore unrecognized permission and continue
196                 }
197             }
198         }
199         store.endArray();
200     }
201 
collectDefinedPermissions(DeviceInfoStore store, Set<String> systemPermissions, PackageInfo pkg)202     private static void collectDefinedPermissions(DeviceInfoStore store,
203                                                   Set<String> systemPermissions,
204                                                   PackageInfo pkg) throws IOException {
205         if (pkg.permissions != null && pkg.permissions.length > 0) {
206             store.startArray(DEFINED_PERMISSIONS);
207             for (PermissionInfo permission : pkg.permissions) {
208                 if (permission == null) continue;
209                 // Ignore "android" package defined AOSP permissions.
210                 if (pkg.packageName.equals(PLATFORM)
211                         && isAndroidPermission(permission.name))
212                     continue;
213 
214                 store.startGroup();
215                 writePermissionsDetails(permission, store, systemPermissions);
216                 store.endGroup();
217 
218             }
219             store.endArray();
220         }
221     }
222 
collectionApplicationInfo(DeviceInfoStore store, PackageManager pm, PackageInfo pkg)223     private static void collectionApplicationInfo(DeviceInfoStore store,
224                                                   PackageManager pm,
225                                                   PackageInfo pkg) throws IOException {
226         final ApplicationInfo appInfo = pkg.applicationInfo;
227         if (appInfo != null) {
228             store.addResult(DIR, appInfo.sourceDir);
229             store.addResult(SYSTEM_PRIV, appInfo.isPrivilegedApp());
230 
231             store.addResult(MIN_SDK, appInfo.minSdkVersion);
232             store.addResult(TARGET_SDK, appInfo.targetSdkVersion);
233 
234             store.addResult(HAS_SYSTEM_UID, appInfo.uid < Process.FIRST_APPLICATION_UID);
235 
236             final boolean canInstall = sharesUidWithInstallerPackage(pm, appInfo.uid);
237             store.addResult(SHARES_INSTALL_PERMISSION, canInstall);
238 
239             store.addResult(UID, appInfo.uid);
240         }
241     }
242 
sharesUidWithInstallerPackage(PackageManager pm, int uid)243     private static boolean sharesUidWithInstallerPackage(PackageManager pm, int uid) {
244         final String[] sharesUidWith = pm.getPackagesForUid(uid);
245 
246         if (sharesUidWith == null) {
247             return false;
248         }
249 
250         // Approx 20 permissions per package for rough estimate of sizing
251         final int capacity = sharesUidWith.length * 20;
252         final List<String> sharedPermissions = new ArrayList<>(capacity);
253         for (String pkg :sharesUidWith){
254             try {
255                 final PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
256 
257                 if (info.requestedPermissions == null) {
258                     continue;
259                 }
260 
261                 for (String p : info.requestedPermissions) {
262                     if (p != null) {
263                         sharedPermissions.add(p);
264                     }
265                 }
266             } catch (PackageManager.NameNotFoundException e) {
267                 // ignore, continue
268             }
269         }
270 
271         return sharedPermissions.contains(PackageDeviceInfo.INSTALL_PACKAGES_PERMISSION);
272     }
273 
writePermissionsDetails(PermissionInfo pi, DeviceInfoStore store, Set<String> systemPermissions)274     private static void writePermissionsDetails(PermissionInfo pi,
275                                                 DeviceInfoStore store,
276                                                 Set<String> systemPermissions) throws IOException {
277         final String permissionName = pi.name;
278         store.addResult(PERMISSION_NAME, permissionName);
279 
280         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
281             store.addResult(PERMISSION_FLAGS, pi.flags);
282         } else {
283             store.addResult(PERMISSION_FLAGS, 0);
284         }
285 
286         store.addResult(PERMISSION_GROUP, pi.group);
287 
288         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
289             store.addResult(PERMISSION_PROTECTION, pi.getProtection());
290             store.addResult(PERMISSION_PROTECTION_FLAGS, pi.getProtectionFlags());
291         } else {
292             store.addResult(PERMISSION_PROTECTION,
293                     pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE);
294             store.addResult(PERMISSION_PROTECTION_FLAGS,
295                     pi.protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE);
296         }
297 
298         final boolean isPlatformPermission = systemPermissions.contains(permissionName);
299         if (isPlatformPermission) {
300             final boolean isAndroidPermission = isAndroidPermission(permissionName);
301             if (isAndroidPermission) {
302             store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_SYSTEM);
303             } else {
304             store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_OEM);
305             }
306         } else {
307             store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_CUSTOM);
308         }
309     }
310 
getActiveDeviceAdminPackages()311     private Set<String> getActiveDeviceAdminPackages() {
312         final DevicePolicyManager dpm = (DevicePolicyManager)
313                 getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
314 
315         final List<ComponentName> components = dpm.getActiveAdmins();
316         if (components == null) {
317             return new HashSet<>(0);
318         }
319 
320         final HashSet<String> packages = new HashSet<>(components.size());
321         for (ComponentName component : components) {
322             packages.add(component.getPackageName());
323         }
324 
325         return packages;
326     }
327 
getDefaultAccessibilityComponent()328     private ComponentName getDefaultAccessibilityComponent() {
329         final String defaultAccessibilityServiceComponent =
330                 getRawDeviceConfig(CONFIG_ACCESSIBILITY_SERVICE);
331         return ComponentName.unflattenFromString(defaultAccessibilityServiceComponent);
332     }
333 
334     /**
335      * Parses and returns a set of package ids from a configuration value
336      * e.g config_defaultListenerAccessPackages
337      **/
getColonSeparatedPackageList(String name)338     private Set<String> getColonSeparatedPackageList(String name) {
339         String raw = getRawDeviceConfig(name);
340         String[] packages = raw.split(":");
341         return new HashSet<>(Arrays.asList(packages));
342     }
343 
344     /** Returns the value of a device configuration setting available in android.internal.R.* **/
getRawDeviceConfig(String name)345     private String getRawDeviceConfig(String name) {
346         return getContext()
347                 .getResources()
348                 .getString(getDeviceResourcesIdentifier(name, "string"));
349     }
350 
getDeviceResourcesIdentifier(String name, String type)351     private int getDeviceResourcesIdentifier(String name, String type) {
352         return getContext()
353                 .getResources()
354                 .getIdentifier(name, type, "android");
355     }
356 
357     /** Return a boolean value to whether the permission is an android permission defined by android package */
isAndroidPermission(String permissionName)358     private static boolean isAndroidPermission(String permissionName) {
359         if(permissionName.startsWith(PLATFORM_ANDROID_PERMISSION_PREFIX)
360             || permissionName.startsWith(PLATFORM_MANIFEST_PERMISSION_PREFIX)
361             || ADDITIONAL_ANDROID_PERMISSIONS.contains(permissionName))
362             return true;
363         return false;
364     }
365 
collectRoles(DeviceInfoStore store, HashMap<String, List<String>> packageRolesData, PackageInfo pkg)366     private static void collectRoles(DeviceInfoStore store,
367                                      HashMap<String, List<String>> packageRolesData,
368                                      PackageInfo pkg) throws IOException {
369         String packageName = pkg.packageName;
370         if(packageRolesData.containsKey(packageName)) {
371             List<String> roleNames = packageRolesData.get(packageName);
372 
373             store.startArray(REQUESTED_ROLES);
374             for(String roleName: roleNames) {
375                 store.startGroup();
376                 store.addResult(ROLE_NAME, roleName);
377                 store.endGroup();
378             }
379             store.endArray();
380         }
381     }
382 
383     /*
384         Return a map of PackageName -> List of RoleNames held by that package
385     */
getPackageRolesData()386     private HashMap<String, List<String>> getPackageRolesData() throws Exception {
387         final RoleManager roleManager = getContext().getSystemService(RoleManager.class);
388         HashMap<String, List<String>> packageRolesData = new HashMap<>();
389 
390         for(String roleName: RolesUtil.ROLE_NAMES) {
391             List<String> packageNames = getRoleHolders(roleName, roleManager);
392 
393             for(String packageName: packageNames) {
394                 packageRolesData.putIfAbsent(packageName, new ArrayList<>());
395                 packageRolesData.get(packageName).add(roleName);
396             }
397         }
398         return packageRolesData;
399     }
400 
getRoleHolders(String roleName, RoleManager roleManager)401     public static List<String> getRoleHolders(String roleName, RoleManager roleManager) throws Exception {
402         return callWithShellPermissionIdentity(
403                 () -> roleManager.getRoleHolders(roleName),
404                         Manifest.permission.MANAGE_ROLE_HOLDERS);
405     }
406 }
407 
408