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