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