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