/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.car.watchdog; import android.annotation.Nullable; import android.automotive.watchdog.internal.ApplicationCategoryType; import android.automotive.watchdog.internal.ComponentType; import android.automotive.watchdog.internal.PackageIdentifier; import android.automotive.watchdog.internal.PackageInfo; import android.automotive.watchdog.internal.UidType; import android.car.builtin.content.pm.PackageManagerHelper; import android.car.builtin.util.Slogf; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Process; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.car.CarLog; import com.android.car.internal.util.IntArray; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** Handles package info resolving */ public final class PackageInfoHandler { public static final String SHARED_PACKAGE_PREFIX = "shared:"; private static final String TAG = CarLog.tagFor(PackageInfoHandler.class); private final PackageManager mPackageManager; private final Object mLock = new Object(); @GuardedBy("mLock") private final SparseArray mGenericPackageNameByUid = new SparseArray<>(); @GuardedBy("mLock") private final SparseArray> mPackagesBySharedUid = new SparseArray<>(); @GuardedBy("mLock") private final ArrayMap mGenericPackageNameByPackage = new ArrayMap<>(); @GuardedBy("mLock") private final SparseArray> mGenericPackageNamesByComponentType = new SparseArray<>(); @GuardedBy("mLock") private List mVendorPackagePrefixes = new ArrayList<>(); public PackageInfoHandler(PackageManager packageManager) { mPackageManager = packageManager; } /** * Returns the generic package names for the given UIDs. * *

Some UIDs may not have names. This may occur when a UID is being removed and the * internal data structures are not up-to-date. The caller should handle it. */ public SparseArray getNamesForUids(int[] uids) { IntArray unmappedUids = new IntArray(uids.length); SparseArray genericPackageNameByUid = new SparseArray<>(); synchronized (mLock) { for (int uid : uids) { String genericPackageName = mGenericPackageNameByUid.get(uid, null); if (genericPackageName != null) { genericPackageNameByUid.append(uid, genericPackageName); } else { unmappedUids.add(uid); } } } if (unmappedUids.size() == 0) { return genericPackageNameByUid; } String[] genericPackageNames = PackageManagerHelper.getNamesForUids(mPackageManager, unmappedUids.toArray()); synchronized (mLock) { for (int i = 0; i < unmappedUids.size(); ++i) { if (genericPackageNames[i] == null || genericPackageNames[i].isEmpty()) { continue; } int uid = unmappedUids.get(i); String genericPackageName = genericPackageNames[i]; mGenericPackageNameByUid.append(uid, genericPackageName); genericPackageNameByUid.append(uid, genericPackageName); mGenericPackageNameByPackage.put(genericPackageName, genericPackageName); if (!genericPackageName.startsWith(SHARED_PACKAGE_PREFIX)) { continue; } populateSharedPackagesLocked(uid, genericPackageName); } } return genericPackageNameByUid; } /** * Returns the generic package name for the user package. * *

Returns null when no generic package name is found. */ @Nullable public String getNameForUserPackage(String packageName, int userId) { synchronized (mLock) { String genericPackageName = mGenericPackageNameByPackage.get(packageName); if (genericPackageName != null) { return genericPackageName; } } try { return getNameForPackage(PackageManagerHelper.getPackageInfoAsUser( mPackageManager, packageName, /* packageInfoFlags= */ 0, userId)); } catch (PackageManager.NameNotFoundException e) { Slogf.e(TAG, "Package '%s' not found for user %d: %s", packageName, userId, e); } return null; } /** Returns the packages owned by the shared UID */ public List getPackagesForUid(int uid, String genericPackageName) { synchronized (mLock) { /* When fetching the packages under a shared UID update the internal DS. This will help * capture any recently installed packages. */ populateSharedPackagesLocked(uid, genericPackageName); return mPackagesBySharedUid.get(uid); } } /** Returns the generic package name for the given package info. */ public String getNameForPackage(android.content.pm.PackageInfo packageInfo) { synchronized (mLock) { String genericPackageName = mGenericPackageNameByPackage.get(packageInfo.packageName); if (genericPackageName != null) { return genericPackageName; } if (packageInfo.sharedUserId != null) { populateSharedPackagesLocked(packageInfo.applicationInfo.uid, SHARED_PACKAGE_PREFIX + packageInfo.sharedUserId); return SHARED_PACKAGE_PREFIX + packageInfo.sharedUserId; } mGenericPackageNameByPackage.put(packageInfo.packageName, packageInfo.packageName); return packageInfo.packageName; } } /** * Returns the internal package infos for the given UIDs. * *

Some UIDs may not have package infos. This may occur when a UID is being removed and the * internal data structures are not up-to-date. The caller should handle it. */ public List getPackageInfosForUids(int[] uids, List vendorPackagePrefixes) { setVendorPackagePrefixes(vendorPackagePrefixes); SparseArray genericPackageNameByUid = getNamesForUids(uids); ArrayList packageInfos = new ArrayList<>(genericPackageNameByUid.size()); for (int i = 0; i < genericPackageNameByUid.size(); ++i) { packageInfos.add(getPackageInfo(genericPackageNameByUid.keyAt(i), genericPackageNameByUid.valueAt(i))); } return packageInfos; } /** Returns component type for the given uid and package name. */ public @ComponentType int getComponentType(int uid, String genericPackageName) { synchronized (mLock) { for (int i = 0; i < mGenericPackageNamesByComponentType.size(); ++i) { if (mGenericPackageNamesByComponentType.valueAt(i).contains(genericPackageName)) { return mGenericPackageNamesByComponentType.keyAt(i); } } } int componentType = ComponentType.UNKNOWN; if (genericPackageName.startsWith(SHARED_PACKAGE_PREFIX)) { synchronized (mLock) { if (!mPackagesBySharedUid.contains(uid)) { populateSharedPackagesLocked(uid, genericPackageName); } List packages = mPackagesBySharedUid.get(uid); if (packages != null) { componentType = getSharedComponentTypeInternal( UserHandle.getUserHandleForUid(uid), packages, genericPackageName); } } } else { componentType = getUserPackageComponentType( UserHandle.getUserHandleForUid(uid), genericPackageName); } if (componentType != ComponentType.UNKNOWN) { cachePackageComponentType(genericPackageName, componentType); } return componentType; } @GuardedBy("mLock") private void populateSharedPackagesLocked(int uid, String genericPackageName) { String[] packages = mPackageManager.getPackagesForUid(uid); for (String pkg : packages) { mGenericPackageNameByPackage.put(pkg, genericPackageName); } mPackagesBySharedUid.put(uid, Arrays.asList(packages)); } private PackageInfo getPackageInfo(int uid, String genericPackageName) { PackageInfo packageInfo = new PackageInfo(); packageInfo.packageIdentifier = new PackageIdentifier(); packageInfo.packageIdentifier.uid = uid; packageInfo.packageIdentifier.name = genericPackageName; packageInfo.sharedUidPackages = new ArrayList<>(); packageInfo.componentType = getComponentType(uid, genericPackageName); /* Application category type mapping is handled on the daemon side. */ packageInfo.appCategoryType = ApplicationCategoryType.OTHERS; int appId = UserHandle.getAppId(uid); packageInfo.uidType = appId >= Process.FIRST_APPLICATION_UID ? UidType.APPLICATION : UidType.NATIVE; if (genericPackageName.startsWith(SHARED_PACKAGE_PREFIX)) { synchronized (mLock) { List packages = mPackagesBySharedUid.get(uid); if (packages != null) { packageInfo.sharedUidPackages = new ArrayList<>(packages); } } } return packageInfo; } /** * Returns the most restrictive component type shared by the given application infos. * * A shared UID has multiple packages associated with it and these packages may be * mapped to different component types. Thus map the shared UID to the most restrictive * component type. */ public @ComponentType int getSharedComponentType( List applicationInfos, String genericPackageName) { SparseBooleanArray seenComponents = new SparseBooleanArray(); for (int i = 0; i < applicationInfos.size(); ++i) { int type = getComponentType(applicationInfos.get(i)); seenComponents.put(type, true); } if (seenComponents.get(ComponentType.VENDOR)) { return ComponentType.VENDOR; } else if (seenComponents.get(ComponentType.SYSTEM)) { synchronized (mLock) { for (int i = 0; i < mVendorPackagePrefixes.size(); ++i) { if (genericPackageName.startsWith(mVendorPackagePrefixes.get(i))) { return ComponentType.VENDOR; } } } return ComponentType.SYSTEM; } else if (seenComponents.get(ComponentType.THIRD_PARTY)) { return ComponentType.THIRD_PARTY; } return ComponentType.UNKNOWN; } private @ComponentType int getUserPackageComponentType( UserHandle userHandle, String packageName) { try { ApplicationInfo info = mPackageManager.getApplicationInfoAsUser( packageName, /* flags= */ 0, userHandle); return getComponentType(info); } catch (PackageManager.NameNotFoundException e) { Slogf.e(TAG, e, "Package '%s' not found for user %d", packageName, userHandle.getIdentifier()); } return ComponentType.UNKNOWN; } private @ComponentType int getSharedComponentTypeInternal( UserHandle userHandle, List packages, String genericPackageName) { List applicationInfos = new ArrayList<>(); for (int i = 0; i < packages.size(); ++i) { try { applicationInfos.add(mPackageManager.getApplicationInfoAsUser( packages.get(i), /* flags= */ 0, userHandle)); } catch (PackageManager.NameNotFoundException e) { Slogf.w(TAG, "Package '%s' not found for user %d", packages.get(i), userHandle.getIdentifier()); } } return getSharedComponentType(applicationInfos, genericPackageName); } /** Returns the component type for the given application info. */ public int getComponentType(ApplicationInfo applicationInfo) { if (PackageManagerHelper.isOemApp(applicationInfo) || PackageManagerHelper.isVendorApp(applicationInfo) || PackageManagerHelper.isOdmApp(applicationInfo)) { return ComponentType.VENDOR; } if (PackageManagerHelper.isSystemApp(applicationInfo) || PackageManagerHelper.isUpdatedSystemApp(applicationInfo) || PackageManagerHelper.isProductApp(applicationInfo) || PackageManagerHelper.isSystemExtApp(applicationInfo)) { synchronized (mLock) { for (int i = 0; i < mVendorPackagePrefixes.size(); ++i) { if (applicationInfo.packageName.startsWith(mVendorPackagePrefixes.get(i))) { return ComponentType.VENDOR; } } } return ComponentType.SYSTEM; } return ComponentType.THIRD_PARTY; } void setVendorPackagePrefixes(List vendorPackagePrefixes) { synchronized (mLock) { if (mVendorPackagePrefixes.equals(vendorPackagePrefixes)) { return; } mVendorPackagePrefixes = vendorPackagePrefixes; // When the vendor package prefixes change due to config update, the component types // for these packages also change. Ergo, clear the component type cache, so the // component types can be inferred again. mGenericPackageNamesByComponentType.clear(); } } private void cachePackageComponentType(String genericPackageName, @ComponentType int componentType) { synchronized (mLock) { ArraySet packages = mGenericPackageNamesByComponentType.get(componentType); if (packages == null) { packages = new ArraySet<>(); } packages.add(genericPackageName); mGenericPackageNamesByComponentType.append(componentType, packages); } } }