1 /* 2 * Copyright (C) 2022 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.server.healthconnect.permission; 18 19 import static android.content.pm.PackageManager.GET_PERMISSIONS; 20 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.content.pm.PackageInfo; 24 import android.content.pm.PackageManager; 25 import android.health.connect.HealthConnectManager; 26 import android.os.UserHandle; 27 import android.util.Log; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Set; 32 33 /** 34 * Utility class with PackageInfo-related methods for {@link FirstGrantTimeManager} 35 * 36 * @hide 37 */ 38 public final class PackageInfoUtils { 39 private static final String TAG = "HCPackageInfoUtils"; 40 PackageInfoUtils()41 public PackageInfoUtils() {} 42 getPackagesHoldingHealthPermissions(UserHandle user, Context context)43 public List<PackageInfo> getPackagesHoldingHealthPermissions(UserHandle user, Context context) { 44 // TODO(b/260707328): replace with getPackagesHoldingPermissions 45 List<PackageInfo> allInfos = 46 getPackageManagerAsUser(context, user) 47 .getInstalledPackages(PackageManager.PackageInfoFlags.of(GET_PERMISSIONS)); 48 List<PackageInfo> healthAppsInfos = new ArrayList<>(); 49 50 for (PackageInfo info : allInfos) { 51 if (anyRequestedHealthPermissionGranted(context, info)) { 52 healthAppsInfos.add(info); 53 } 54 } 55 return healthAppsInfos; 56 } 57 getPackagesCompatibleWithHealthConnect( Context context, UserHandle user)58 public List<PackageInfo> getPackagesCompatibleWithHealthConnect( 59 Context context, UserHandle user) { 60 List<PackageInfo> allInfos = 61 getPackageManagerAsUser(context, user) 62 .getInstalledPackages(PackageManager.PackageInfoFlags.of(GET_PERMISSIONS)); 63 List<PackageInfo> healthAppsInfos = new ArrayList<>(); 64 65 for (PackageInfo info : allInfos) { 66 if (hasRequestedHealthPermission(context, info)) { 67 healthAppsInfos.add(info); 68 } 69 } 70 return healthAppsInfos; 71 } 72 hasGrantedHealthPermissions(String[] packageNames, UserHandle user, Context context)73 boolean hasGrantedHealthPermissions(String[] packageNames, UserHandle user, Context context) { 74 for (String packageName : packageNames) { 75 PackageInfo info = getPackageInfoWithPermissionsAsUser(packageName, user, context); 76 if (info != null && anyRequestedHealthPermissionGranted(context, info)) { 77 return true; 78 } 79 } 80 return false; 81 } 82 83 @Nullable getPackagesForUid(Context context, UserHandle user, int packageUid)84 String[] getPackagesForUid(Context context, UserHandle user, int packageUid) { 85 return getPackageManagerAsUser(context, user).getPackagesForUid(packageUid); 86 } 87 getPackagesForUidNonNull(Context context, UserHandle user, int packageUid)88 String[] getPackagesForUidNonNull(Context context, UserHandle user, int packageUid) { 89 String[] packages = getPackagesForUid(context, user, packageUid); 90 return packages != null ? packages : new String[] {}; 91 } 92 93 /** 94 * Checks if the given package had any read/write permissions to Health Connect. 95 * 96 * @param context Context 97 * @param packageInfo Package to check 98 * @return If the given package is connected to Health Connect. 99 */ anyRequestedHealthPermissionGranted( Context context, PackageInfo packageInfo)100 private static boolean anyRequestedHealthPermissionGranted( 101 Context context, PackageInfo packageInfo) { 102 if (packageInfo.requestedPermissions == null) { 103 return false; 104 } 105 Set<String> healthPermissions = HealthConnectManager.getHealthPermissions(context); 106 107 for (int i = 0; i < packageInfo.requestedPermissions.length; i++) { 108 String currPerm = packageInfo.requestedPermissions[i]; 109 if (healthPermissions.contains(currPerm) 110 && ((packageInfo.requestedPermissionsFlags[i] 111 & PackageInfo.REQUESTED_PERMISSION_GRANTED) 112 != 0)) { 113 return true; 114 } 115 } 116 return false; 117 } 118 119 @Nullable getPackageInfoWithPermissionsAsUser( String packageName, UserHandle user, Context context)120 public PackageInfo getPackageInfoWithPermissionsAsUser( 121 String packageName, UserHandle user, Context context) { 122 try { 123 return getPackageManagerAsUser(context, user) 124 .getPackageInfo( 125 packageName, PackageManager.PackageInfoFlags.of(GET_PERMISSIONS)); 126 } catch (PackageManager.NameNotFoundException e) { 127 // App not found. 128 Log.e(TAG, "NameNotFoundException for " + packageName); 129 return null; 130 } 131 } 132 133 @Nullable getSharedUserNameFromUid(int uid, Context context)134 String getSharedUserNameFromUid(int uid, Context context) { 135 UserHandle user = UserHandle.getUserHandleForUid(uid); 136 PackageManager packageManager = getPackageManagerAsUser(context, user); 137 String[] packages = packageManager.getPackagesForUid(uid); 138 if (packages == null || packages.length == 0) { 139 Log.e(TAG, "Can't get package names for UID: " + uid); 140 return null; 141 } 142 try { 143 PackageInfo info = 144 packageManager.getPackageInfo( 145 packages[0], PackageManager.PackageInfoFlags.of(0)); 146 return info.sharedUserId; 147 } catch (PackageManager.NameNotFoundException e) { 148 Log.e(TAG, "Package " + packages[0] + " not found."); 149 return null; 150 } 151 } 152 153 @Nullable getPackageUid(String packageName, UserHandle user, Context context)154 Integer getPackageUid(String packageName, UserHandle user, Context context) { 155 Integer uid = null; 156 try { 157 uid = 158 getPackageManagerAsUser(context, user) 159 .getPackageUid( 160 packageName, 161 PackageManager.PackageInfoFlags.of(/* flags= */ 0)); 162 } catch (PackageManager.NameNotFoundException e) { 163 Log.e(TAG, "NameNotFound exception for " + packageName); 164 } 165 return uid; 166 } 167 168 /** 169 * Returns the list of health permissions granted to a given package name. It does not check if 170 * the given package name is valid. 171 */ getGrantedHealthPermissions( Context context, String packageName, UserHandle user)172 public static List<String> getGrantedHealthPermissions( 173 Context context, String packageName, UserHandle user) { 174 PackageInfo packageInfo = 175 getPackageInfoUnchecked( 176 packageName, 177 user, 178 PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS), 179 context); 180 181 return getGrantedHealthPermissions(context, packageInfo); 182 } 183 184 /** Returns the list of health permissions granted to the given {@link PackageInfo}. */ getGrantedHealthPermissions( Context context, PackageInfo packageInfo)185 public static List<String> getGrantedHealthPermissions( 186 Context context, PackageInfo packageInfo) { 187 Set<String> healthPermissions = HealthConnectManager.getHealthPermissions(context); 188 189 if (packageInfo.requestedPermissions == null) { 190 return List.of(); 191 } 192 193 List<String> grantedHealthPerms = new ArrayList<>(packageInfo.requestedPermissions.length); 194 for (int i = 0; i < packageInfo.requestedPermissions.length; i++) { 195 String currPerm = packageInfo.requestedPermissions[i]; 196 if (packageInfo.requestedPermissionsFlags != null 197 && healthPermissions.contains(currPerm) 198 && ((packageInfo.requestedPermissionsFlags[i] 199 & PackageInfo.REQUESTED_PERMISSION_GRANTED) 200 != 0)) { 201 grantedHealthPerms.add(currPerm); 202 } 203 } 204 return grantedHealthPerms; 205 } 206 207 /** 208 * Returns the list of {@link PackageInfo} for a given package. It does not check if the given 209 * package name is valid. 210 */ getPackageInfoUnchecked( String packageName, UserHandle user, PackageManager.PackageInfoFlags flags, Context context)211 public static PackageInfo getPackageInfoUnchecked( 212 String packageName, 213 UserHandle user, 214 PackageManager.PackageInfoFlags flags, 215 Context context) { 216 try { 217 return getPackageManagerAsUser(context, user).getPackageInfo(packageName, flags); 218 } catch (PackageManager.NameNotFoundException e) { 219 throw new IllegalArgumentException("invalid package", e); 220 } 221 } 222 getPackageManagerAsUser(Context context, UserHandle user)223 private static PackageManager getPackageManagerAsUser(Context context, UserHandle user) { 224 return context.createContextAsUser(user, /* flags */ 0).getPackageManager(); 225 } 226 hasRequestedHealthPermission(Context context, PackageInfo packageInfo)227 private boolean hasRequestedHealthPermission(Context context, PackageInfo packageInfo) { 228 if (packageInfo == null || packageInfo.requestedPermissions == null) { 229 return false; 230 } 231 232 Set<String> healthPermissions = HealthConnectManager.getHealthPermissions(context); 233 for (int i = 0; i < packageInfo.requestedPermissions.length; i++) { 234 if (healthPermissions.contains(packageInfo.requestedPermissions[i])) { 235 return true; 236 } 237 } 238 return false; 239 } 240 } 241