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 static java.util.Objects.requireNonNull; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.content.pm.PackageInfo; 27 import android.content.pm.PackageManager; 28 import android.health.connect.HealthConnectManager; 29 import android.os.UserHandle; 30 import android.util.ArrayMap; 31 import android.util.ArraySet; 32 import android.util.Log; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Optional; 40 import java.util.Set; 41 42 /** 43 * Utility class with PackageInfo-related methods for {@link FirstGrantTimeManager} 44 * 45 * @hide 46 */ 47 public final class PackageInfoUtils { 48 private static final String TAG = "HealthConnectPackageInfoUtils"; 49 50 @Nullable private static volatile PackageInfoUtils sPackageInfoUtils; 51 52 /** 53 * Store PackageManager for each user. Keys are users, values are PackageManagers which get from 54 * each user. 55 */ 56 private final Map<UserHandle, PackageManager> mUsersPackageManager = new ArrayMap<>(); 57 PackageInfoUtils()58 private PackageInfoUtils() {} 59 60 @NonNull getInstance()61 public static synchronized PackageInfoUtils getInstance() { 62 if (sPackageInfoUtils == null) { 63 sPackageInfoUtils = new PackageInfoUtils(); 64 } 65 66 return requireNonNull(sPackageInfoUtils); 67 } 68 69 /** Reset singleton instance {@link PackageInfoUtils}. */ clearInstance()70 public static void clearInstance() { 71 sPackageInfoUtils = null; 72 } 73 74 /** Set an instance of {@link PackageInfoUtils} for testing purposes, such as a mock. */ 75 @VisibleForTesting setInstanceForTest(@onNull PackageInfoUtils instance)76 static synchronized void setInstanceForTest(@NonNull PackageInfoUtils instance) { 77 sPackageInfoUtils = instance; 78 } 79 80 @NonNull collectSharedUserNameToUidsMappingForUser( @onNull List<PackageInfo> packageInfos, UserHandle user)81 Map<String, Set<Integer>> collectSharedUserNameToUidsMappingForUser( 82 @NonNull List<PackageInfo> packageInfos, UserHandle user) { 83 Map<String, Set<Integer>> sharedUserNameToUids = new ArrayMap<>(); 84 for (PackageInfo info : packageInfos) { 85 if (info.sharedUserId != null) { 86 if (sharedUserNameToUids.get(info.sharedUserId) == null) { 87 sharedUserNameToUids.put(info.sharedUserId, new ArraySet<>()); 88 } 89 sharedUserNameToUids.get(info.sharedUserId).add(info.applicationInfo.uid); 90 } 91 } 92 return sharedUserNameToUids; 93 } 94 95 @NonNull getPackagesHoldingHealthPermissions(UserHandle user, Context context)96 public List<PackageInfo> getPackagesHoldingHealthPermissions(UserHandle user, Context context) { 97 // TODO(b/260707328): replace with getPackagesHoldingPermissions 98 List<PackageInfo> allInfos = 99 getPackageManagerAsUser(user, context) 100 .getInstalledPackages(PackageManager.PackageInfoFlags.of(GET_PERMISSIONS)); 101 List<PackageInfo> healthAppsInfos = new ArrayList<>(); 102 103 for (PackageInfo info : allInfos) { 104 if (anyRequestedHealthPermissionGranted(context, info)) { 105 healthAppsInfos.add(info); 106 } 107 } 108 return healthAppsInfos; 109 } 110 111 @SuppressWarnings("NullAway") 112 // TODO(b/317029272): fix this suppression hasGrantedHealthPermissions( @onNull String[] packageNames, @NonNull UserHandle user, @NonNull Context context)113 boolean hasGrantedHealthPermissions( 114 @NonNull String[] packageNames, @NonNull UserHandle user, @NonNull Context context) { 115 for (String packageName : packageNames) { 116 PackageInfo info = getPackageInfoWithPermissionsAsUser(packageName, user, context); 117 if (anyRequestedHealthPermissionGranted(context, info)) { 118 return true; 119 } 120 } 121 return false; 122 } 123 124 @Nullable getPackagesForUid(int packageUid, @NonNull UserHandle user, @NonNull Context context)125 String[] getPackagesForUid(int packageUid, @NonNull UserHandle user, @NonNull Context context) { 126 return getPackageManagerAsUser(user, context).getPackagesForUid(packageUid); 127 } 128 129 /** 130 * Checks if the given package had any read/write permissions to Health Connect. 131 * 132 * @param context Context 133 * @param packageInfo Package to check 134 * @return If the given package is connected to Health Connect. 135 */ anyRequestedHealthPermissionGranted( @ullable Context context, @Nullable PackageInfo packageInfo)136 public static boolean anyRequestedHealthPermissionGranted( 137 @Nullable Context context, @Nullable PackageInfo packageInfo) { 138 if (context == null || packageInfo == null || packageInfo.requestedPermissions == null) { 139 Log.w(TAG, "Can't extract requested permissions from the package info."); 140 return false; 141 } 142 143 for (int i = 0; i < packageInfo.requestedPermissions.length; i++) { 144 String currPerm = packageInfo.requestedPermissions[i]; 145 if (HealthConnectManager.isHealthPermission(context, currPerm) 146 && ((packageInfo.requestedPermissionsFlags[i] 147 & PackageInfo.REQUESTED_PERMISSION_GRANTED) 148 != 0)) { 149 return true; 150 } 151 } 152 return false; 153 } 154 155 @Nullable getPackageInfoWithPermissionsAsUser( @onNull String packageName, @NonNull UserHandle user, @NonNull Context context)156 public PackageInfo getPackageInfoWithPermissionsAsUser( 157 @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) { 158 try { 159 return getPackageManagerAsUser(user, context) 160 .getPackageInfo( 161 packageName, PackageManager.PackageInfoFlags.of(GET_PERMISSIONS)); 162 } catch (PackageManager.NameNotFoundException e) { 163 // App not found. 164 Log.e(TAG, "NameNotFoundException for " + packageName); 165 return null; 166 } 167 } 168 169 @Nullable getSharedUserNameFromUid(int uid, Context context)170 String getSharedUserNameFromUid(int uid, Context context) { 171 UserHandle user = UserHandle.getUserHandleForUid(uid); 172 PackageManager packageManager = getPackageManagerAsUser(user, context); 173 String[] packages = packageManager.getPackagesForUid(uid); 174 if (packages == null || packages.length == 0) { 175 Log.e(TAG, "Can't get package names for UID: " + uid); 176 return null; 177 } 178 try { 179 PackageInfo info = 180 packageManager.getPackageInfo( 181 packages[0], PackageManager.PackageInfoFlags.of(0)); 182 return info.sharedUserId; 183 } catch (PackageManager.NameNotFoundException e) { 184 Log.e(TAG, "Package " + packages[0] + " not found."); 185 return null; 186 } 187 } 188 getPackageNameFromUid(int uid)189 Optional<String> getPackageNameFromUid(int uid) { 190 String[] packages = getPackageNamesForUid(uid); 191 if (packages.length != 1) { 192 Log.w(TAG, "Can't get one package name for UID: " + uid); 193 return Optional.empty(); 194 } 195 return Optional.of(packages[0]); 196 } 197 getPackageNamesForUid(int uid)198 String[] getPackageNamesForUid(int uid) { 199 PackageManager packageManager = 200 mUsersPackageManager.get(UserHandle.getUserHandleForUid(uid)); 201 if (packageManager == null) { 202 return new String[] {}; 203 } 204 String[] packages = packageManager.getPackagesForUid(uid); 205 return packages != null ? packages : new String[] {}; 206 } 207 208 @Nullable getPackageUid( @onNull String packageName, @NonNull UserHandle user, @NonNull Context context)209 Integer getPackageUid( 210 @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) { 211 Integer uid = null; 212 try { 213 uid = 214 getPackageManagerAsUser(user, context) 215 .getPackageUid( 216 packageName, 217 PackageManager.PackageInfoFlags.of(/* flags= */ 0)); 218 } catch (PackageManager.NameNotFoundException e) { 219 Log.e(TAG, "NameNotFound exception for " + packageName); 220 } 221 return uid; 222 } 223 224 @NonNull getPackageManagerAsUser( @onNull UserHandle user, @NonNull Context context)225 private PackageManager getPackageManagerAsUser( 226 @NonNull UserHandle user, @NonNull Context context) { 227 PackageManager packageManager = mUsersPackageManager.get(user); 228 if (packageManager == null) { 229 packageManager = context.getPackageManager(); 230 mUsersPackageManager.put(user, packageManager); 231 } 232 return packageManager; 233 } 234 } 235