blob: ab8cc5374ec93037b55b6dc32b1f728cd12f9369 [file] [log] [blame]
Patrick Baumann6c1c8092019-06-27 14:55:44 -07001/*
2 * Copyright (C) 2019 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
17package com.android.server.pm;
18
19import android.Manifest;
20import android.annotation.Nullable;
21import android.app.AppOpsManager;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.content.pm.PackageParser;
26import android.os.Build;
27import android.os.Process;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.permission.IPermissionManager;
31import android.provider.DeviceConfig;
32import android.util.ArraySet;
33import android.util.Slog;
34import android.util.SparseArray;
35
36import com.android.internal.R;
37
38import java.util.ArrayList;
39import java.util.Collections;
40import java.util.HashMap;
41import java.util.HashSet;
42import java.util.List;
43import java.util.Map;
44import java.util.Set;
45
46/**
47 * The entity responsible for filtering visibility between apps based on declarations in their
48 * manifests.
49 */
50class AppsFilter {
51
52 private static final String TAG = PackageManagerService.TAG;
53 /**
54 * This contains a list of packages that are implicitly queryable because another app explicitly
55 * interacted with it. For example, if application A starts a service in application B,
56 * application B is implicitly allowed to query for application A; regardless of any manifest
57 * entries.
58 */
59 private final SparseArray<HashMap<String, ArrayList<String>>> mImplicitlyQueryable =
60 new SparseArray<>();
61
62 /**
63 * A mapping from the set of packages that query other packages via package name to the
64 * list of packages that they can see.
65 */
66 private final HashMap<String, List<String>> mQueriesViaPackage = new HashMap<>();
67
68 /**
69 * A mapping from the set of packages that query others via intent to the list
70 * of packages that the intents resolve to.
71 */
72 private final HashMap<String, List<String>> mQueriesViaIntent = new HashMap<>();
73
74 /**
75 * A set of packages that are always queryable by any package, regardless of their manifest
76 * content.
77 */
78 private final HashSet<String> mForceQueryable;
79 /**
80 * A set of packages that are always queryable by any package, regardless of their manifest
81 * content.
82 */
83 private final Set<String> mForceQueryableByDevice;
84
85 /** True if all system apps should be made queryable by default. */
86 private final boolean mSystemAppsQueryable;
87
88 private final IPermissionManager mPermissionManager;
89
90 private final AppOpsManager mAppOpsManager;
91 private final ConfigProvider mConfigProvider;
92
93 AppsFilter(ConfigProvider configProvider, IPermissionManager permissionManager,
94 AppOpsManager appOpsManager, String[] forceQueryableWhitelist,
95 boolean systemAppsQueryable) {
96 mConfigProvider = configProvider;
97 mAppOpsManager = appOpsManager;
98 final HashSet<String> forceQueryableByDeviceSet = new HashSet<>();
99 Collections.addAll(forceQueryableByDeviceSet, forceQueryableWhitelist);
100 this.mForceQueryableByDevice = Collections.unmodifiableSet(forceQueryableByDeviceSet);
101 this.mForceQueryable = new HashSet<>();
102 mPermissionManager = permissionManager;
103 mSystemAppsQueryable = systemAppsQueryable;
104 }
105
106 public static AppsFilter create(Context context) {
107 final boolean forceSystemAppsQueryable =
108 context.getResources().getBoolean(R.bool.config_forceSystemPackagesQueryable);
109 final ConfigProvider configProvider = () -> DeviceConfig.getBoolean(
110 DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
111 "package_query_filtering_enabled",
112 false);
113 final String[] forcedQueryablePackageNames;
114 if (forceSystemAppsQueryable) {
115 // all system apps already queryable, no need to read and parse individual exceptions
116 forcedQueryablePackageNames = new String[]{};
117 } else {
118 forcedQueryablePackageNames =
119 context.getResources().getStringArray(R.array.config_forceQueryablePackages);
120 for (int i = 0; i < forcedQueryablePackageNames.length; i++) {
121 forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
122 }
123 }
124 IPermissionManager permissionmgr =
125 (IPermissionManager) ServiceManager.getService("permissionmgr");
126 return new AppsFilter(configProvider, permissionmgr,
127 context.getSystemService(AppOpsManager.class), forcedQueryablePackageNames,
128 forceSystemAppsQueryable);
129 }
130
131 /** Returns true if the querying package may query for the potential target package */
132 private static boolean canQuery(PackageParser.Package querying,
133 PackageParser.Package potentialTarget) {
134 if (querying.mQueriesIntents == null) {
135 return false;
136 }
137 for (Intent intent : querying.mQueriesIntents) {
138 for (PackageParser.Activity activity : potentialTarget.activities) {
139 if (activity.intents != null) {
140 for (PackageParser.ActivityIntentInfo filter : activity.intents) {
141 if (matches(intent, filter)) {
142 return true;
143 }
144 }
145 }
146 }
147 }
148 return false;
149 }
150
151 /** Returns true if the given intent matches the given filter. */
152 private static boolean matches(Intent intent, PackageParser.ActivityIntentInfo filter) {
153 return filter.match(intent.getAction(), intent.getType(), intent.getScheme(),
154 intent.getData(), intent.getCategories(), "AppsFilter") > 0;
155 }
156
157 /**
158 * Marks that a package initiated an interaction with another package, granting visibility of
159 * the prior from the former.
160 *
161 * @param initiatingPackage the package initiating the interaction
162 * @param targetPackage the package being interacted with and thus gaining visibility of the
163 * initiating package.
164 * @param userId the user in which this interaction was taking place
165 */
166 private void markAppInteraction(
167 PackageSetting initiatingPackage, PackageSetting targetPackage, int userId) {
168 HashMap<String, ArrayList<String>> currentUser = mImplicitlyQueryable.get(userId);
169 if (currentUser == null) {
170 currentUser = new HashMap<>();
171 mImplicitlyQueryable.put(userId, currentUser);
172 }
173 if (!currentUser.containsKey(targetPackage.pkg.packageName)) {
174 currentUser.put(targetPackage.pkg.packageName, new ArrayList<>());
175 }
176 currentUser.get(targetPackage.pkg.packageName).add(initiatingPackage.pkg.packageName);
177 }
178
179 /**
180 * Adds a package that should be considered when filtering visibility between apps.
181 *
182 * @param newPkg the new package being added
183 * @param existing all other packages currently on the device.
184 */
185 public void addPackage(PackageParser.Package newPkg,
186 Map<String, PackageParser.Package> existing) {
187 // let's re-evaluate the ability of already added packages to see this new package
188 if (newPkg.mForceQueryable
189 || (mSystemAppsQueryable && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) {
190 mForceQueryable.add(newPkg.packageName);
191 } else {
192 for (String packageName : mQueriesViaIntent.keySet()) {
193 if (packageName == newPkg.packageName) {
194 continue;
195 }
196 final PackageParser.Package existingPackage = existing.get(packageName);
197 if (canQuery(existingPackage, newPkg)) {
198 mQueriesViaIntent.get(packageName).add(newPkg.packageName);
199 }
200 }
201 }
202 // if the new package declares them, let's evaluate its ability to see existing packages
203 mQueriesViaIntent.put(newPkg.packageName, new ArrayList<>());
204 for (PackageParser.Package existingPackage : existing.values()) {
205 if (existingPackage.packageName == newPkg.packageName) {
206 continue;
207 }
208 if (existingPackage.mForceQueryable
209 || (mSystemAppsQueryable
210 && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) {
211 continue;
212 }
213 if (canQuery(newPkg, existingPackage)) {
214 mQueriesViaIntent.get(newPkg.packageName).add(existingPackage.packageName);
215 }
216 }
217 final ArrayList<String> queriesPackages = new ArrayList<>(
218 newPkg.mQueriesPackages == null ? 0 : newPkg.mQueriesPackages.size());
219 if (newPkg.mQueriesPackages != null) {
220 queriesPackages.addAll(newPkg.mQueriesPackages);
221 }
222 mQueriesViaPackage.put(newPkg.packageName, queriesPackages);
223 }
224
225 /**
226 * Removes a package for consideration when filtering visibility between apps.
227 *
228 * @param packageName the name of the package being removed.
229 */
230 public void removePackage(String packageName) {
231 mForceQueryable.remove(packageName);
232
233 for (int i = 0; i < mImplicitlyQueryable.size(); i++) {
234 mImplicitlyQueryable.valueAt(i).remove(packageName);
235 for (ArrayList<String> initiators : mImplicitlyQueryable.valueAt(i).values()) {
236 initiators.remove(packageName);
237 }
238 }
239
240 mQueriesViaIntent.remove(packageName);
241 for (List<String> declarators : mQueriesViaIntent.values()) {
242 declarators.remove(packageName);
243 }
244
245 mQueriesViaPackage.remove(packageName);
246 }
247
248 /**
249 * Returns true if the calling package should not be able to see the target package, false if no
250 * filtering should be done.
251 *
252 * @param callingUid the uid of the caller attempting to access a package
253 * @param callingSetting the setting attempting to access a package or null if it could not be
254 * found
255 * @param targetPkgSetting the package being accessed
256 * @param userId the user in which this access is being attempted
257 */
258 public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
259 PackageSetting targetPkgSetting, int userId) {
260 if (callingUid < Process.FIRST_APPLICATION_UID) {
261 return false;
262 }
263 if (callingSetting == null) {
264 Slog.wtf(TAG, "No setting found for non system uid " + callingUid);
265 return true;
266 }
267 PackageSetting callingPkgSetting = null;
268 if (callingSetting instanceof PackageSetting) {
269 callingPkgSetting = (PackageSetting) callingSetting;
270 if (!shouldFilterApplicationInternal(callingPkgSetting, targetPkgSetting,
271 userId)) {
272 // TODO: actually base this on a start / launch (not just a query)
273 markAppInteraction(callingPkgSetting, targetPkgSetting, userId);
274 return false;
275 }
276 } else if (callingSetting instanceof SharedUserSetting) {
277 final ArraySet<PackageSetting> packageSettings =
278 ((SharedUserSetting) callingSetting).packages;
279 if (packageSettings != null && packageSettings.size() > 0) {
280 for (PackageSetting packageSetting : packageSettings) {
281 if (!shouldFilterApplicationInternal(packageSetting, targetPkgSetting,
282 userId)) {
283 // TODO: actually base this on a start / launch (not just a query)
284 markAppInteraction(packageSetting, targetPkgSetting, userId);
285 return false;
286 }
287 if (callingPkgSetting == null && packageSetting.pkg != null) {
288 callingPkgSetting = packageSetting;
289 }
290 }
291 } else {
292 return true;
293 }
294 }
295 if (callingPkgSetting == null) {
296 Slog.wtf(TAG, "What... " + callingSetting);
297 return true;
298 }
299 final int mode = mAppOpsManager
300 .checkOpNoThrow(AppOpsManager.OP_QUERY_ALL_PACKAGES, callingUid,
301 callingPkgSetting.pkg.packageName);
302 switch (mode) {
303 case AppOpsManager.MODE_DEFAULT:
304 // if default, let's rely on remote feature toggle to determine whether to
305 // actually filter
306 return mConfigProvider.isEnabled();
307 case AppOpsManager.MODE_ALLOWED:
308 // explicitly allowed to see all packages, don't filter
309 return false;
310 case AppOpsManager.MODE_ERRORED:
311 // deny / error: let's log so developer can audit usages
312 Slog.i(TAG, callingPkgSetting.pkg.packageName
313 + " blocked from accessing " + targetPkgSetting.pkg.packageName);
314 case AppOpsManager.MODE_IGNORED:
315 // fall through
316 default:
317 return true;
318 }
319 }
320
321 private boolean shouldFilterApplicationInternal(
322 PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, int userId) {
323 final String callingName = callingPkgSetting.pkg.packageName;
324 final String targetName = targetPkgSetting.pkg.packageName;
325 if (callingPkgSetting.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) {
326 return false;
327 }
328 if (isImplicitlyQueryableSystemApp(targetPkgSetting)) {
329 return false;
330 }
331 if (targetPkgSetting.pkg.mForceQueryable) {
332 return false;
333 }
334 if (mForceQueryable.contains(targetName)) {
335 return false;
336 }
337 if (mQueriesViaPackage.containsKey(callingName)
338 && mQueriesViaPackage.get(callingName).contains(
339 targetName)) {
340 // the calling package has explicitly declared the target package; allow
341 return false;
342 } else if (mQueriesViaIntent.containsKey(callingName)
343 && mQueriesViaIntent.get(callingName).contains(targetName)) {
344 return false;
345 }
346 if (mImplicitlyQueryable.get(userId) != null
347 && mImplicitlyQueryable.get(userId).containsKey(callingName)
348 && mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) {
349 return false;
350 }
351 try {
352 if (mPermissionManager.checkPermission(
353 Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId)
354 == PackageManager.PERMISSION_GRANTED) {
355 return false;
356 }
357 } catch (RemoteException e) {
358 return true;
359 }
360 return true;
361 }
362
363 private boolean isImplicitlyQueryableSystemApp(PackageSetting targetPkgSetting) {
364 return targetPkgSetting.isSystem() && (mSystemAppsQueryable
365 || mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName));
366 }
367
368 public interface ConfigProvider {
369 boolean isEnabled();
370 }
371
372}