1 /*
2  * Copyright (C) 2022 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.server.pm;
18 
19 import static android.os.Process.THREAD_PRIORITY_DEFAULT;
20 
21 import android.Manifest;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.util.ArrayMap;
27 import android.util.ArraySet;
28 import android.util.Pair;
29 
30 import com.android.internal.util.ArrayUtils;
31 import com.android.internal.util.ConcurrentUtils;
32 import com.android.server.pm.pkg.AndroidPackage;
33 import com.android.server.pm.pkg.PackageState;
34 import com.android.server.pm.pkg.PackageStateInternal;
35 import com.android.server.pm.pkg.component.ParsedComponent;
36 import com.android.server.pm.pkg.component.ParsedIntentInfo;
37 import com.android.server.pm.pkg.component.ParsedMainComponent;
38 import com.android.server.pm.pkg.component.ParsedProvider;
39 import com.android.server.utils.WatchedArraySet;
40 import com.android.server.utils.WatchedSparseSetArray;
41 
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Set;
45 import java.util.StringTokenizer;
46 import java.util.concurrent.ExecutionException;
47 import java.util.concurrent.ExecutorService;
48 import java.util.concurrent.Future;
49 
50 final class AppsFilterUtils {
requestsQueryAllPackages(@onNull AndroidPackage pkg)51     public static boolean requestsQueryAllPackages(@NonNull AndroidPackage pkg) {
52         // we're not guaranteed to have permissions yet analyzed at package add, so we inspect the
53         // package directly
54         return pkg.getRequestedPermissions().contains(
55                 Manifest.permission.QUERY_ALL_PACKAGES);
56     }
57 
58     /** Returns true if the querying package may query for the potential target package */
canQueryViaComponents(AndroidPackage querying, AndroidPackage potentialTarget, WatchedArraySet<String> protectedBroadcasts)59     public static boolean canQueryViaComponents(AndroidPackage querying,
60             AndroidPackage potentialTarget, WatchedArraySet<String> protectedBroadcasts) {
61         if (!querying.getQueriesIntents().isEmpty()) {
62             for (Intent intent : querying.getQueriesIntents()) {
63                 if (matchesPackage(intent, potentialTarget, protectedBroadcasts)) {
64                     return true;
65                 }
66             }
67         }
68         if (!querying.getQueriesProviders().isEmpty()
69                 && matchesProviders(querying.getQueriesProviders(), potentialTarget)) {
70             return true;
71         }
72         return false;
73     }
74 
canQueryViaPackage(AndroidPackage querying, AndroidPackage potentialTarget)75     public static boolean canQueryViaPackage(AndroidPackage querying,
76             AndroidPackage potentialTarget) {
77         return !querying.getQueriesPackages().isEmpty()
78                 && querying.getQueriesPackages().contains(potentialTarget.getPackageName());
79     }
80 
canQueryAsInstaller(PackageStateInternal querying, AndroidPackage potentialTarget)81     public static boolean canQueryAsInstaller(PackageStateInternal querying,
82             AndroidPackage potentialTarget) {
83         final InstallSource installSource = querying.getInstallSource();
84         if (potentialTarget.getPackageName().equals(installSource.mInstallerPackageName)) {
85             return true;
86         }
87         if (!installSource.mIsInitiatingPackageUninstalled
88                 && potentialTarget.getPackageName().equals(installSource.mInitiatingPackageName)) {
89             return true;
90         }
91         return false;
92     }
93 
canQueryAsUpdateOwner(PackageStateInternal querying, AndroidPackage potentialTarget)94     public static boolean canQueryAsUpdateOwner(PackageStateInternal querying,
95             AndroidPackage potentialTarget) {
96         final InstallSource installSource = querying.getInstallSource();
97         if (potentialTarget.getPackageName().equals(installSource.mUpdateOwnerPackageName)) {
98             return true;
99         }
100         return false;
101     }
102 
canQueryViaUsesLibrary(AndroidPackage querying, AndroidPackage potentialTarget)103     public static boolean canQueryViaUsesLibrary(AndroidPackage querying,
104             AndroidPackage potentialTarget) {
105         if (potentialTarget.getLibraryNames().isEmpty()) {
106             return false;
107         }
108         final List<String> libNames = potentialTarget.getLibraryNames();
109         for (int i = 0, size = libNames.size(); i < size; i++) {
110             final String libName = libNames.get(i);
111             if (querying.getUsesLibraries().contains(libName)
112                     || querying.getUsesOptionalLibraries().contains(libName)) {
113                 return true;
114             }
115         }
116         return false;
117     }
118 
matchesProviders( Set<String> queriesAuthorities, AndroidPackage potentialTarget)119     private static boolean matchesProviders(
120             Set<String> queriesAuthorities, AndroidPackage potentialTarget) {
121         for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) {
122             ParsedProvider provider = potentialTarget.getProviders().get(p);
123             if (!provider.isExported()) {
124                 continue;
125             }
126             if (provider.getAuthority() == null) {
127                 continue;
128             }
129             StringTokenizer authorities = new StringTokenizer(provider.getAuthority(), ";",
130                     false);
131             while (authorities.hasMoreElements()) {
132                 if (queriesAuthorities.contains(authorities.nextToken())) {
133                     return true;
134                 }
135             }
136         }
137         return false;
138     }
139 
matchesPackage(Intent intent, AndroidPackage potentialTarget, WatchedArraySet<String> protectedBroadcasts)140     private static boolean matchesPackage(Intent intent, AndroidPackage potentialTarget,
141             WatchedArraySet<String> protectedBroadcasts) {
142         if (matchesAnyComponents(
143                 intent, potentialTarget.getServices(), null /*protectedBroadcasts*/)) {
144             return true;
145         }
146         if (matchesAnyComponents(
147                 intent, potentialTarget.getActivities(), null /*protectedBroadcasts*/)) {
148             return true;
149         }
150         if (matchesAnyComponents(intent, potentialTarget.getReceivers(), protectedBroadcasts)) {
151             return true;
152         }
153         if (matchesAnyComponents(
154                 intent, potentialTarget.getProviders(), null /*protectedBroadcasts*/)) {
155             return true;
156         }
157         return false;
158     }
159 
matchesAnyComponents(Intent intent, List<? extends ParsedMainComponent> components, WatchedArraySet<String> protectedBroadcasts)160     private static boolean matchesAnyComponents(Intent intent,
161             List<? extends ParsedMainComponent> components,
162             WatchedArraySet<String> protectedBroadcasts) {
163         for (int i = ArrayUtils.size(components) - 1; i >= 0; i--) {
164             ParsedMainComponent component = components.get(i);
165             if (!component.isExported()) {
166                 continue;
167             }
168             if (matchesAnyFilter(intent, component, protectedBroadcasts)) {
169                 return true;
170             }
171         }
172         return false;
173     }
174 
matchesAnyFilter(Intent intent, ParsedComponent component, WatchedArraySet<String> protectedBroadcasts)175     private static boolean matchesAnyFilter(Intent intent, ParsedComponent component,
176             WatchedArraySet<String> protectedBroadcasts) {
177         List<ParsedIntentInfo> intents = component.getIntents();
178         for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) {
179             IntentFilter intentFilter = intents.get(i).getIntentFilter();
180             if (matchesIntentFilter(intent, intentFilter, protectedBroadcasts)) {
181                 return true;
182             }
183         }
184         return false;
185     }
186 
matchesIntentFilter(Intent intent, IntentFilter intentFilter, @Nullable WatchedArraySet<String> protectedBroadcasts)187     private static boolean matchesIntentFilter(Intent intent, IntentFilter intentFilter,
188             @Nullable WatchedArraySet<String> protectedBroadcasts) {
189         return intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
190                 intent.getData(), intent.getCategories(), "AppsFilter", true,
191                 protectedBroadcasts != null ? protectedBroadcasts.untrackedStorage() : null) > 0;
192     }
193 
194     /**
195      * A helper class for parallel computing of component visibility of all packages on the device.
196      */
197     public static final class ParallelComputeComponentVisibility {
198         private static final int MAX_THREADS = 4;
199 
200         private final ArrayMap<String, ? extends PackageStateInternal> mExistingSettings;
201         private final WatchedArraySet<Integer> mForceQueryable;
202         private final WatchedArraySet<String> mProtectedBroadcasts;
203 
ParallelComputeComponentVisibility( @onNull ArrayMap<String, ? extends PackageStateInternal> existingSettings, @NonNull WatchedArraySet<Integer> forceQueryable, @NonNull WatchedArraySet<String> protectedBroadcasts)204         ParallelComputeComponentVisibility(
205                 @NonNull ArrayMap<String, ? extends PackageStateInternal> existingSettings,
206                 @NonNull WatchedArraySet<Integer> forceQueryable,
207                 @NonNull WatchedArraySet<String> protectedBroadcasts) {
208             mExistingSettings = existingSettings;
209             mForceQueryable = forceQueryable;
210             mProtectedBroadcasts = protectedBroadcasts;
211         }
212 
213         /**
214          * Computes component visibility of all packages in parallel from a thread pool.
215          */
execute(@onNull WatchedSparseSetArray<Integer> outQueriesViaComponent)216         void execute(@NonNull WatchedSparseSetArray<Integer> outQueriesViaComponent) {
217             final ExecutorService pool = ConcurrentUtils.newFixedThreadPool(
218                     MAX_THREADS, ParallelComputeComponentVisibility.class.getSimpleName(),
219                     THREAD_PRIORITY_DEFAULT);
220             try {
221                 final List<Pair<PackageState, Future<ArraySet<Integer>>>> futures =
222                         new ArrayList<>();
223                 for (int i = mExistingSettings.size() - 1; i >= 0; i--) {
224                     final PackageStateInternal setting = mExistingSettings.valueAt(i);
225                     final AndroidPackage pkg = setting.getPkg();
226                     if (pkg == null || requestsQueryAllPackages(pkg)) {
227                         continue;
228                     }
229                     if (pkg.getQueriesIntents().isEmpty()
230                             && pkg.getQueriesProviders().isEmpty()) {
231                         continue;
232                     }
233                     futures.add(new Pair(setting,
234                             pool.submit(() -> getVisibleListOfQueryViaComponents(setting))));
235                 }
236                 for (int i = 0; i < futures.size(); i++) {
237                     final int appId = futures.get(i).first.getAppId();
238                     final Future<ArraySet<Integer>> future = futures.get(i).second;
239                     try {
240                         final ArraySet<Integer> visibleList = future.get();
241                         if (visibleList.size() != 0) {
242                             outQueriesViaComponent.addAll(appId, visibleList);
243                         }
244                     } catch (InterruptedException | ExecutionException e) {
245                         throw new IllegalStateException(e);
246                     }
247                 }
248             } finally {
249                 pool.shutdownNow();
250             }
251         }
252 
253         /**
254          * Returns a set of app IDs that contains components resolved by the queries intent
255          * or provider that declared in the manifest of the querying package.
256          *
257          * @param setting The package to query.
258          * @return A set of app IDs.
259          */
260         @NonNull
getVisibleListOfQueryViaComponents( @onNull PackageStateInternal setting)261         private ArraySet<Integer> getVisibleListOfQueryViaComponents(
262                 @NonNull PackageStateInternal setting) {
263             final ArraySet<Integer> result = new ArraySet();
264             for (int i = mExistingSettings.size() - 1; i >= 0; i--) {
265                 final PackageStateInternal otherSetting = mExistingSettings.valueAt(i);
266                 if (setting.getAppId() == otherSetting.getAppId()) {
267                     continue;
268                 }
269                 if (otherSetting.getPkg() == null || mForceQueryable.contains(
270                         otherSetting.getAppId())) {
271                     continue;
272                 }
273                 final boolean canQuery = canQueryViaComponents(
274                         setting.getPkg(), otherSetting.getPkg(), mProtectedBroadcasts);
275                 if (canQuery) {
276                     result.add(otherSetting.getAppId());
277                 }
278             }
279             return result;
280         }
281     }
282 }
283