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