1 /* 2 * Copyright (C) 2021 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.car.watchdog; 18 19 import android.annotation.Nullable; 20 import android.automotive.watchdog.internal.ApplicationCategoryType; 21 import android.automotive.watchdog.internal.ComponentType; 22 import android.automotive.watchdog.internal.PackageIdentifier; 23 import android.automotive.watchdog.internal.PackageInfo; 24 import android.automotive.watchdog.internal.UidType; 25 import android.car.builtin.content.pm.PackageManagerHelper; 26 import android.car.builtin.util.Slogf; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageManager; 29 import android.os.Process; 30 import android.os.UserHandle; 31 import android.util.ArrayMap; 32 import android.util.ArraySet; 33 import android.util.SparseArray; 34 import android.util.SparseBooleanArray; 35 36 import com.android.car.CarLog; 37 import com.android.car.internal.util.IntArray; 38 import com.android.internal.annotations.GuardedBy; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.List; 43 44 /** Handles package info resolving */ 45 public final class PackageInfoHandler { 46 public static final String SHARED_PACKAGE_PREFIX = "shared:"; 47 48 private static final String TAG = CarLog.tagFor(PackageInfoHandler.class); 49 50 private final PackageManager mPackageManager; 51 private final Object mLock = new Object(); 52 @GuardedBy("mLock") 53 private final SparseArray<String> mGenericPackageNameByUid = new SparseArray<>(); 54 @GuardedBy("mLock") 55 private final SparseArray<List<String>> mPackagesBySharedUid = new SparseArray<>(); 56 @GuardedBy("mLock") 57 private final ArrayMap<String, String> mGenericPackageNameByPackage = new ArrayMap<>(); 58 @GuardedBy("mLock") 59 private final SparseArray<ArraySet<String>> mGenericPackageNamesByComponentType = 60 new SparseArray<>(); 61 @GuardedBy("mLock") 62 private List<String> mVendorPackagePrefixes = new ArrayList<>(); 63 PackageInfoHandler(PackageManager packageManager)64 public PackageInfoHandler(PackageManager packageManager) { 65 mPackageManager = packageManager; 66 } 67 68 /** 69 * Returns the generic package names for the given UIDs. 70 * 71 * <p>Some UIDs may not have names. This may occur when a UID is being removed and the 72 * internal data structures are not up-to-date. The caller should handle it. 73 */ getNamesForUids(int[] uids)74 public SparseArray<String> getNamesForUids(int[] uids) { 75 IntArray unmappedUids = new IntArray(uids.length); 76 SparseArray<String> genericPackageNameByUid = new SparseArray<>(); 77 synchronized (mLock) { 78 for (int uid : uids) { 79 String genericPackageName = mGenericPackageNameByUid.get(uid, null); 80 if (genericPackageName != null) { 81 genericPackageNameByUid.append(uid, genericPackageName); 82 } else { 83 unmappedUids.add(uid); 84 } 85 } 86 } 87 if (unmappedUids.size() == 0) { 88 return genericPackageNameByUid; 89 } 90 String[] genericPackageNames = PackageManagerHelper.getNamesForUids(mPackageManager, 91 unmappedUids.toArray()); 92 synchronized (mLock) { 93 for (int i = 0; i < unmappedUids.size(); ++i) { 94 if (genericPackageNames[i] == null || genericPackageNames[i].isEmpty()) { 95 continue; 96 } 97 int uid = unmappedUids.get(i); 98 String genericPackageName = genericPackageNames[i]; 99 mGenericPackageNameByUid.append(uid, genericPackageName); 100 genericPackageNameByUid.append(uid, genericPackageName); 101 mGenericPackageNameByPackage.put(genericPackageName, genericPackageName); 102 if (!genericPackageName.startsWith(SHARED_PACKAGE_PREFIX)) { 103 continue; 104 } 105 populateSharedPackagesLocked(uid, genericPackageName); 106 } 107 } 108 return genericPackageNameByUid; 109 } 110 111 /** 112 * Returns the generic package name for the user package. 113 * 114 * <p>Returns null when no generic package name is found. 115 */ 116 @Nullable getNameForUserPackage(String packageName, int userId)117 public String getNameForUserPackage(String packageName, int userId) { 118 synchronized (mLock) { 119 String genericPackageName = mGenericPackageNameByPackage.get(packageName); 120 if (genericPackageName != null) { 121 return genericPackageName; 122 } 123 } 124 try { 125 return getNameForPackage(PackageManagerHelper.getPackageInfoAsUser( 126 mPackageManager, packageName, /* packageInfoFlags= */ 0, userId)); 127 } catch (PackageManager.NameNotFoundException e) { 128 Slogf.e(TAG, "Package '%s' not found for user %d: %s", packageName, userId, e); 129 } 130 return null; 131 } 132 133 /** Returns the packages owned by the shared UID */ getPackagesForUid(int uid, String genericPackageName)134 public List<String> getPackagesForUid(int uid, String genericPackageName) { 135 synchronized (mLock) { 136 /* When fetching the packages under a shared UID update the internal DS. This will help 137 * capture any recently installed packages. 138 */ 139 populateSharedPackagesLocked(uid, genericPackageName); 140 return mPackagesBySharedUid.get(uid); 141 } 142 } 143 144 /** Returns the generic package name for the given package info. */ getNameForPackage(android.content.pm.PackageInfo packageInfo)145 public String getNameForPackage(android.content.pm.PackageInfo packageInfo) { 146 synchronized (mLock) { 147 String genericPackageName = mGenericPackageNameByPackage.get(packageInfo.packageName); 148 if (genericPackageName != null) { 149 return genericPackageName; 150 } 151 if (packageInfo.sharedUserId != null) { 152 populateSharedPackagesLocked(packageInfo.applicationInfo.uid, 153 SHARED_PACKAGE_PREFIX + packageInfo.sharedUserId); 154 return SHARED_PACKAGE_PREFIX + packageInfo.sharedUserId; 155 } 156 mGenericPackageNameByPackage.put(packageInfo.packageName, packageInfo.packageName); 157 return packageInfo.packageName; 158 } 159 } 160 161 /** 162 * Returns the internal package infos for the given UIDs. 163 * 164 * <p>Some UIDs may not have package infos. This may occur when a UID is being removed and the 165 * internal data structures are not up-to-date. The caller should handle it. 166 */ getPackageInfosForUids(int[] uids, List<String> vendorPackagePrefixes)167 public List<PackageInfo> getPackageInfosForUids(int[] uids, 168 List<String> vendorPackagePrefixes) { 169 setVendorPackagePrefixes(vendorPackagePrefixes); 170 SparseArray<String> genericPackageNameByUid = getNamesForUids(uids); 171 ArrayList<PackageInfo> packageInfos = new ArrayList<>(genericPackageNameByUid.size()); 172 for (int i = 0; i < genericPackageNameByUid.size(); ++i) { 173 packageInfos.add(getPackageInfo(genericPackageNameByUid.keyAt(i), 174 genericPackageNameByUid.valueAt(i))); 175 } 176 return packageInfos; 177 } 178 179 /** Returns component type for the given uid and package name. */ getComponentType(int uid, String genericPackageName)180 public @ComponentType int getComponentType(int uid, String genericPackageName) { 181 synchronized (mLock) { 182 for (int i = 0; i < mGenericPackageNamesByComponentType.size(); ++i) { 183 if (mGenericPackageNamesByComponentType.valueAt(i).contains(genericPackageName)) { 184 return mGenericPackageNamesByComponentType.keyAt(i); 185 } 186 } 187 } 188 int componentType = ComponentType.UNKNOWN; 189 if (genericPackageName.startsWith(SHARED_PACKAGE_PREFIX)) { 190 synchronized (mLock) { 191 if (!mPackagesBySharedUid.contains(uid)) { 192 populateSharedPackagesLocked(uid, genericPackageName); 193 } 194 List<String> packages = mPackagesBySharedUid.get(uid); 195 if (packages != null) { 196 componentType = getSharedComponentTypeInternal( 197 UserHandle.getUserHandleForUid(uid), packages, genericPackageName); 198 } 199 } 200 } else { 201 componentType = getUserPackageComponentType( 202 UserHandle.getUserHandleForUid(uid), genericPackageName); 203 } 204 if (componentType != ComponentType.UNKNOWN) { 205 cachePackageComponentType(genericPackageName, componentType); 206 } 207 return componentType; 208 } 209 210 @GuardedBy("mLock") populateSharedPackagesLocked(int uid, String genericPackageName)211 private void populateSharedPackagesLocked(int uid, String genericPackageName) { 212 String[] packages = mPackageManager.getPackagesForUid(uid); 213 for (String pkg : packages) { 214 mGenericPackageNameByPackage.put(pkg, genericPackageName); 215 } 216 mPackagesBySharedUid.put(uid, Arrays.asList(packages)); 217 } 218 getPackageInfo(int uid, String genericPackageName)219 private PackageInfo getPackageInfo(int uid, String genericPackageName) { 220 PackageInfo packageInfo = new PackageInfo(); 221 packageInfo.packageIdentifier = new PackageIdentifier(); 222 packageInfo.packageIdentifier.uid = uid; 223 packageInfo.packageIdentifier.name = genericPackageName; 224 packageInfo.sharedUidPackages = new ArrayList<>(); 225 packageInfo.componentType = getComponentType(uid, genericPackageName); 226 /* Application category type mapping is handled on the daemon side. */ 227 packageInfo.appCategoryType = ApplicationCategoryType.OTHERS; 228 int appId = UserHandle.getAppId(uid); 229 packageInfo.uidType = appId >= Process.FIRST_APPLICATION_UID ? UidType.APPLICATION : 230 UidType.NATIVE; 231 232 if (genericPackageName.startsWith(SHARED_PACKAGE_PREFIX)) { 233 synchronized (mLock) { 234 List<String> packages = mPackagesBySharedUid.get(uid); 235 if (packages != null) { 236 packageInfo.sharedUidPackages = new ArrayList<>(packages); 237 } 238 } 239 } 240 return packageInfo; 241 } 242 243 /** 244 * Returns the most restrictive component type shared by the given application infos. 245 * 246 * A shared UID has multiple packages associated with it and these packages may be 247 * mapped to different component types. Thus map the shared UID to the most restrictive 248 * component type. 249 */ getSharedComponentType( List<ApplicationInfo> applicationInfos, String genericPackageName)250 public @ComponentType int getSharedComponentType( 251 List<ApplicationInfo> applicationInfos, String genericPackageName) { 252 SparseBooleanArray seenComponents = new SparseBooleanArray(); 253 for (int i = 0; i < applicationInfos.size(); ++i) { 254 int type = getComponentType(applicationInfos.get(i)); 255 seenComponents.put(type, true); 256 } 257 if (seenComponents.get(ComponentType.VENDOR)) { 258 return ComponentType.VENDOR; 259 } else if (seenComponents.get(ComponentType.SYSTEM)) { 260 synchronized (mLock) { 261 for (int i = 0; i < mVendorPackagePrefixes.size(); ++i) { 262 if (genericPackageName.startsWith(mVendorPackagePrefixes.get(i))) { 263 return ComponentType.VENDOR; 264 } 265 } 266 } 267 return ComponentType.SYSTEM; 268 } else if (seenComponents.get(ComponentType.THIRD_PARTY)) { 269 return ComponentType.THIRD_PARTY; 270 } 271 return ComponentType.UNKNOWN; 272 } 273 getUserPackageComponentType( UserHandle userHandle, String packageName)274 private @ComponentType int getUserPackageComponentType( 275 UserHandle userHandle, String packageName) { 276 try { 277 ApplicationInfo info = mPackageManager.getApplicationInfoAsUser( 278 packageName, /* flags= */ 0, userHandle); 279 return getComponentType(info); 280 } catch (PackageManager.NameNotFoundException e) { 281 Slogf.e(TAG, e, "Package '%s' not found for user %d", packageName, 282 userHandle.getIdentifier()); 283 } 284 return ComponentType.UNKNOWN; 285 } 286 getSharedComponentTypeInternal( UserHandle userHandle, List<String> packages, String genericPackageName)287 private @ComponentType int getSharedComponentTypeInternal( 288 UserHandle userHandle, List<String> packages, String genericPackageName) { 289 List<ApplicationInfo> applicationInfos = new ArrayList<>(); 290 for (int i = 0; i < packages.size(); ++i) { 291 try { 292 applicationInfos.add(mPackageManager.getApplicationInfoAsUser( 293 packages.get(i), /* flags= */ 0, userHandle)); 294 } catch (PackageManager.NameNotFoundException e) { 295 Slogf.w(TAG, "Package '%s' not found for user %d", packages.get(i), 296 userHandle.getIdentifier()); 297 } 298 } 299 return getSharedComponentType(applicationInfos, genericPackageName); 300 } 301 302 /** Returns the component type for the given application info. */ getComponentType(ApplicationInfo applicationInfo)303 public int getComponentType(ApplicationInfo applicationInfo) { 304 if (PackageManagerHelper.isOemApp(applicationInfo) 305 || PackageManagerHelper.isVendorApp(applicationInfo) 306 || PackageManagerHelper.isOdmApp(applicationInfo)) { 307 return ComponentType.VENDOR; 308 } 309 if (PackageManagerHelper.isSystemApp(applicationInfo) 310 || PackageManagerHelper.isUpdatedSystemApp(applicationInfo) 311 || PackageManagerHelper.isProductApp(applicationInfo) 312 || PackageManagerHelper.isSystemExtApp(applicationInfo)) { 313 synchronized (mLock) { 314 for (int i = 0; i < mVendorPackagePrefixes.size(); ++i) { 315 if (applicationInfo.packageName.startsWith(mVendorPackagePrefixes.get(i))) { 316 return ComponentType.VENDOR; 317 } 318 } 319 } 320 return ComponentType.SYSTEM; 321 } 322 return ComponentType.THIRD_PARTY; 323 } 324 setVendorPackagePrefixes(List<String> vendorPackagePrefixes)325 void setVendorPackagePrefixes(List<String> vendorPackagePrefixes) { 326 synchronized (mLock) { 327 if (mVendorPackagePrefixes.equals(vendorPackagePrefixes)) { 328 return; 329 } 330 mVendorPackagePrefixes = vendorPackagePrefixes; 331 // When the vendor package prefixes change due to config update, the component types 332 // for these packages also change. Ergo, clear the component type cache, so the 333 // component types can be inferred again. 334 mGenericPackageNamesByComponentType.clear(); 335 } 336 } 337 cachePackageComponentType(String genericPackageName, @ComponentType int componentType)338 private void cachePackageComponentType(String genericPackageName, 339 @ComponentType int componentType) { 340 synchronized (mLock) { 341 ArraySet<String> packages = mGenericPackageNamesByComponentType.get(componentType); 342 if (packages == null) { 343 packages = new ArraySet<>(); 344 } 345 packages.add(genericPackageName); 346 mGenericPackageNamesByComponentType.append(componentType, packages); 347 } 348 } 349 } 350