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