blob: e3f4bb869451c6315ea55a0ba866eea3a8c6596b [file] [log] [blame]
/*
* 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.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.car.CarLog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.utils.Slogf;
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<String> mGenericPackageNameByUid = new SparseArray<>();
@GuardedBy("mLock")
private final SparseArray<List<String>> mPackagesBySharedUid = new SparseArray<>();
@GuardedBy("mLock")
private final ArrayMap<String, String> mGenericPackageNameByPackage = new ArrayMap<>();
@GuardedBy("mLock")
private List<String> 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<String> getNamesForUids(int[] uids) {
IntArray unmappedUids = new IntArray(uids.length);
SparseArray<String> 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 = mPackageManager.getNamesForUids(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(
mPackageManager.getPackageInfoAsUser(packageName, /* flags= */ 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<String> 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<PackageInfo> getPackageInfosForUids(int[] uids,
List<String> vendorPackagePrefixes) {
synchronized (mLock) {
/*
* Vendor package prefixes don't change frequently because it changes only when the
* vendor configuration is updated. Thus caching this locally during this call should
* keep the cache up-to-date because the daemon issues this call frequently.
*/
mVendorPackagePrefixes = vendorPackagePrefixes;
}
SparseArray<String> genericPackageNameByUid = getNamesForUids(uids);
ArrayList<PackageInfo> packageInfos = new ArrayList<>(genericPackageNameByUid.size());
for (int i = 0; i < genericPackageNameByUid.size(); ++i) {
packageInfos.add(getPackageInfo(genericPackageNameByUid.keyAt(i),
genericPackageNameByUid.valueAt(i)));
}
return packageInfos;
}
@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 = ComponentType.UNKNOWN;
/* Application category type mapping is handled on the daemon side. */
packageInfo.appCategoryType = ApplicationCategoryType.OTHERS;
int userId = UserHandle.getUserId(uid);
int appId = UserHandle.getAppId(uid);
packageInfo.uidType = appId >= Process.FIRST_APPLICATION_UID ? UidType.APPLICATION :
UidType.NATIVE;
if (genericPackageName.startsWith(SHARED_PACKAGE_PREFIX)) {
List<String> packages = null;
synchronized (mLock) {
packages = mPackagesBySharedUid.get(uid);
if (packages == null) {
return packageInfo;
}
}
List<ApplicationInfo> applicationInfos = new ArrayList<>();
for (int i = 0; i < packages.size(); ++i) {
try {
applicationInfos.add(mPackageManager.getApplicationInfoAsUser(packages.get(i),
/* flags= */ 0, userId));
} catch (PackageManager.NameNotFoundException e) {
Slogf.e(TAG, "Package '%s' not found for user %d: %s", packages.get(i), userId,
e);
}
}
packageInfo.componentType = getSharedComponentType(
applicationInfos, genericPackageName);
packageInfo.sharedUidPackages = new ArrayList<>(packages);
} else {
packageInfo.componentType = getUserPackageComponentType(
userId, genericPackageName);
}
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 int getSharedComponentType(List<ApplicationInfo> 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 int getUserPackageComponentType(int userId, String packageName) {
try {
ApplicationInfo info = mPackageManager.getApplicationInfoAsUser(packageName,
/* flags= */ 0, userId);
return getComponentType(info);
} catch (PackageManager.NameNotFoundException e) {
Slogf.e(TAG, "Package '%s' not found for user %d: %s", packageName, userId, e);
}
return ComponentType.UNKNOWN;
}
/** Returns the component type for the given application info. */
public int getComponentType(ApplicationInfo applicationInfo) {
if ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0
|| (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0
|| (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
return ComponentType.VENDOR;
}
if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|| (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
|| (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0
|| (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
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<String> vendorPackagePrefixes) {
synchronized (mLock) {
mVendorPackagePrefixes = vendorPackagePrefixes;
}
}
}