blob: 80fda19aa10c9048b899ce9e1abb97b7df0828a1 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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;
18
Eugene Susla4ab95112018-12-17 14:45:11 -080019import android.content.Intent;
20import android.content.IntentFilter;
21import android.net.Uri;
22import android.util.ArrayMap;
23import android.util.ArraySet;
24import android.util.FastImmutableArraySet;
25import android.util.Log;
26import android.util.LogPrinter;
27import android.util.MutableInt;
28import android.util.PrintWriterPrinter;
29import android.util.Printer;
30import android.util.Slog;
31import android.util.proto.ProtoOutputStream;
32
33import com.android.internal.util.FastPrintWriter;
34
Dianne Hackborn1d442e02009-04-20 18:14:05 -070035import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import java.util.ArrayList;
Dianne Hackborn38ba6e92013-09-23 11:08:52 -070037import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import java.util.Collections;
39import java.util.Comparator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import java.util.Iterator;
41import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import java.util.Set;
43
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044/**
45 * {@hide}
46 */
Dianne Hackborn6c418d52011-06-29 14:05:33 -070047public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 final private static String TAG = "IntentResolver";
49 final private static boolean DEBUG = false;
Joe Onorato43a17652011-04-06 19:22:23 -070050 final private static boolean localLOGV = DEBUG || false;
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -080051 final private static boolean localVerificationLOGV = DEBUG || false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53 public void addFilter(F f) {
54 if (localLOGV) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080055 Slog.v(TAG, "Adding filter: " + f);
56 f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
57 Slog.v(TAG, " Building Lookup Maps:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 }
59
60 mFilters.add(f);
61 int numS = register_intent_filter(f, f.schemesIterator(),
62 mSchemeToFilter, " Scheme: ");
63 int numT = register_mime_types(f, " Type: ");
64 if (numS == 0 && numT == 0) {
65 register_intent_filter(f, f.actionsIterator(),
66 mActionToFilter, " Action: ");
67 }
68 if (numT != 0) {
69 register_intent_filter(f, f.actionsIterator(),
70 mTypedActionToFilter, " TypedAction: ");
71 }
72 }
73
Suprabh Shuklaff784952018-02-28 20:55:40 -080074 public static boolean filterEquals(IntentFilter f1, IntentFilter f2) {
Dianne Hackbornf2ac2762014-08-16 11:44:40 -070075 int s1 = f1.countActions();
76 int s2 = f2.countActions();
77 if (s1 != s2) {
78 return false;
79 }
80 for (int i=0; i<s1; i++) {
81 if (!f2.hasAction(f1.getAction(i))) {
82 return false;
83 }
84 }
85 s1 = f1.countCategories();
86 s2 = f2.countCategories();
87 if (s1 != s2) {
88 return false;
89 }
90 for (int i=0; i<s1; i++) {
91 if (!f2.hasCategory(f1.getCategory(i))) {
92 return false;
93 }
94 }
95 s1 = f1.countDataTypes();
96 s2 = f2.countDataTypes();
97 if (s1 != s2) {
98 return false;
99 }
100 for (int i=0; i<s1; i++) {
101 if (!f2.hasExactDataType(f1.getDataType(i))) {
102 return false;
103 }
104 }
105 s1 = f1.countDataSchemes();
106 s2 = f2.countDataSchemes();
107 if (s1 != s2) {
108 return false;
109 }
110 for (int i=0; i<s1; i++) {
111 if (!f2.hasDataScheme(f1.getDataScheme(i))) {
112 return false;
113 }
114 }
115 s1 = f1.countDataAuthorities();
116 s2 = f2.countDataAuthorities();
117 if (s1 != s2) {
118 return false;
119 }
120 for (int i=0; i<s1; i++) {
121 if (!f2.hasDataAuthority(f1.getDataAuthority(i))) {
122 return false;
123 }
124 }
125 s1 = f1.countDataPaths();
126 s2 = f2.countDataPaths();
127 if (s1 != s2) {
128 return false;
129 }
130 for (int i=0; i<s1; i++) {
131 if (!f2.hasDataPath(f1.getDataPath(i))) {
132 return false;
133 }
134 }
135 s1 = f1.countDataSchemeSpecificParts();
136 s2 = f2.countDataSchemeSpecificParts();
137 if (s1 != s2) {
138 return false;
139 }
140 for (int i=0; i<s1; i++) {
141 if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) {
142 return false;
143 }
144 }
145 return true;
146 }
147
148 private ArrayList<F> collectFilters(F[] array, IntentFilter matching) {
149 ArrayList<F> res = null;
150 if (array != null) {
151 for (int i=0; i<array.length; i++) {
152 F cur = array[i];
153 if (cur == null) {
154 break;
155 }
156 if (filterEquals(cur, matching)) {
157 if (res == null) {
158 res = new ArrayList<>();
159 }
160 res.add(cur);
161 }
162 }
163 }
164 return res;
165 }
166
167 public ArrayList<F> findFilters(IntentFilter matching) {
168 if (matching.countDataSchemes() == 1) {
169 // Fast case.
170 return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching);
171 } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) {
172 // Another fast case.
173 return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching);
174 } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0
175 && matching.countActions() == 1) {
176 // Last fast case.
177 return collectFilters(mActionToFilter.get(matching.getAction(0)), matching);
178 } else {
179 ArrayList<F> res = null;
180 for (F cur : mFilters) {
181 if (filterEquals(cur, matching)) {
182 if (res == null) {
183 res = new ArrayList<>();
184 }
185 res.add(cur);
186 }
187 }
188 return res;
189 }
190 }
191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 public void removeFilter(F f) {
193 removeFilterInternal(f);
194 mFilters.remove(f);
195 }
196
197 void removeFilterInternal(F f) {
198 if (localLOGV) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800199 Slog.v(TAG, "Removing filter: " + f);
200 f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
201 Slog.v(TAG, " Cleaning Lookup Maps:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203
204 int numS = unregister_intent_filter(f, f.schemesIterator(),
205 mSchemeToFilter, " Scheme: ");
206 int numT = unregister_mime_types(f, " Type: ");
207 if (numS == 0 && numT == 0) {
208 unregister_intent_filter(f, f.actionsIterator(),
209 mActionToFilter, " Action: ");
210 }
211 if (numT != 0) {
212 unregister_intent_filter(f, f.actionsIterator(),
213 mTypedActionToFilter, " TypedAction: ");
214 }
215 }
216
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700217 boolean dumpMap(PrintWriter out, String titlePrefix, String title,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800218 String prefix, ArrayMap<String, F[]> map, String packageName,
219 boolean printFilter, boolean collapseDuplicates) {
220 final String eprefix = prefix + " ";
221 final String fprefix = prefix + " ";
222 final ArrayMap<Object, MutableInt> found = new ArrayMap<>();
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700223 boolean printedSomething = false;
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700224 Printer printer = null;
Dianne Hackbornd052a942014-11-21 15:23:13 -0800225 for (int mapi=0; mapi<map.size(); mapi++) {
226 F[] a = map.valueAt(mapi);
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700227 final int N = a.length;
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700228 boolean printedHeader = false;
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700229 F filter;
Dianne Hackborn3cdb56e2015-11-11 12:45:44 -0800230 if (collapseDuplicates && !printFilter) {
Dianne Hackbornd052a942014-11-21 15:23:13 -0800231 found.clear();
232 for (int i=0; i<N && (filter=a[i]) != null; i++) {
233 if (packageName != null && !isPackageForFilter(packageName, filter)) {
234 continue;
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700235 }
Dianne Hackbornd052a942014-11-21 15:23:13 -0800236 Object label = filterToLabel(filter);
237 int index = found.indexOfKey(label);
238 if (index < 0) {
239 found.put(label, new MutableInt(1));
240 } else {
241 found.valueAt(index).value++;
242 }
243 }
244 for (int i=0; i<found.size(); i++) {
245 if (title != null) {
246 out.print(titlePrefix); out.println(title);
247 title = null;
248 }
249 if (!printedHeader) {
250 out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
251 printedHeader = true;
252 }
253 printedSomething = true;
254 dumpFilterLabel(out, fprefix, found.keyAt(i), found.valueAt(i).value);
255 }
256 } else {
257 for (int i=0; i<N && (filter=a[i]) != null; i++) {
258 if (packageName != null && !isPackageForFilter(packageName, filter)) {
259 continue;
260 }
261 if (title != null) {
262 out.print(titlePrefix); out.println(title);
263 title = null;
264 }
265 if (!printedHeader) {
266 out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
267 printedHeader = true;
268 }
269 printedSomething = true;
270 dumpFilter(out, fprefix, filter);
271 if (printFilter) {
272 if (printer == null) {
273 printer = new PrintWriterPrinter(out);
274 }
275 filter.dump(printer, fprefix + " ");
276 }
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700277 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 }
279 }
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700280 return printedSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
282
Yi Jin129fc6c2017-09-28 15:48:38 -0700283 void writeProtoMap(ProtoOutputStream proto, long fieldId, ArrayMap<String, F[]> map) {
284 int N = map.size();
285 for (int mapi = 0; mapi < N; mapi++) {
286 long token = proto.start(fieldId);
287 proto.write(IntentResolverProto.ArrayMapEntry.KEY, map.keyAt(mapi));
288 for (F f : map.valueAt(mapi)) {
289 if (f != null) {
290 proto.write(IntentResolverProto.ArrayMapEntry.VALUES, f.toString());
291 }
292 }
293 proto.end(token);
294 }
295 }
296
297 public void writeToProto(ProtoOutputStream proto, long fieldId) {
298 long token = proto.start(fieldId);
299 writeProtoMap(proto, IntentResolverProto.FULL_MIME_TYPES, mTypeToFilter);
300 writeProtoMap(proto, IntentResolverProto.BASE_MIME_TYPES, mBaseTypeToFilter);
301 writeProtoMap(proto, IntentResolverProto.WILD_MIME_TYPES, mWildTypeToFilter);
302 writeProtoMap(proto, IntentResolverProto.SCHEMES, mSchemeToFilter);
303 writeProtoMap(proto, IntentResolverProto.NON_DATA_ACTIONS, mActionToFilter);
304 writeProtoMap(proto, IntentResolverProto.MIME_TYPED_ACTIONS, mTypedActionToFilter);
305 proto.end(token);
306 }
307
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700308 public boolean dump(PrintWriter out, String title, String prefix, String packageName,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800309 boolean printFilter, boolean collapseDuplicates) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700310 String innerPrefix = prefix + " ";
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700311 String sepPrefix = "\n" + prefix;
312 String curPrefix = title + "\n" + prefix;
313 if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800314 mTypeToFilter, packageName, printFilter, collapseDuplicates)) {
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700315 curPrefix = sepPrefix;
316 }
317 if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800318 mBaseTypeToFilter, packageName, printFilter, collapseDuplicates)) {
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700319 curPrefix = sepPrefix;
320 }
321 if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800322 mWildTypeToFilter, packageName, printFilter, collapseDuplicates)) {
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700323 curPrefix = sepPrefix;
324 }
325 if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800326 mSchemeToFilter, packageName, printFilter, collapseDuplicates)) {
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700327 curPrefix = sepPrefix;
328 }
329 if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800330 mActionToFilter, packageName, printFilter, collapseDuplicates)) {
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700331 curPrefix = sepPrefix;
332 }
333 if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
Dianne Hackbornd052a942014-11-21 15:23:13 -0800334 mTypedActionToFilter, packageName, printFilter, collapseDuplicates)) {
Dianne Hackbornd4310ac2010-03-16 22:55:08 -0700335 curPrefix = sepPrefix;
336 }
337 return curPrefix == sepPrefix;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 }
339
340 private class IteratorWrapper implements Iterator<F> {
341 private final Iterator<F> mI;
342 private F mCur;
343
344 IteratorWrapper(Iterator<F> it) {
345 mI = it;
346 }
347
348 public boolean hasNext() {
349 return mI.hasNext();
350 }
351
352 public F next() {
353 return (mCur = mI.next());
354 }
355
356 public void remove() {
357 if (mCur != null) {
358 removeFilterInternal(mCur);
359 }
360 mI.remove();
361 }
362
363 }
364
365 /**
366 * Returns an iterator allowing filters to be removed.
367 */
368 public Iterator<F> filterIterator() {
369 return new IteratorWrapper(mFilters.iterator());
370 }
371
372 /**
373 * Returns a read-only set of the filters.
374 */
375 public Set<F> filterSet() {
376 return Collections.unmodifiableSet(mFilters);
377 }
378
Todd Kennedy7bc3a702016-12-08 14:54:48 -0800379 public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,
Todd Kennedybe0b8892017-02-15 14:13:52 -0800380 ArrayList<F[]> listCut, int userId) {
Mihai Predaeae850c2009-05-13 10:13:48 +0200381 ArrayList<R> resultList = new ArrayList<R>();
382
383 final boolean debug = localLOGV ||
384 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
385
Jeff Brown2c376fc2011-01-28 17:34:01 -0800386 FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
Mihai Predaeae850c2009-05-13 10:13:48 +0200387 final String scheme = intent.getScheme();
388 int N = listCut.size();
389 for (int i = 0; i < N; ++i) {
Todd Kennedybe0b8892017-02-15 14:13:52 -0800390 buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme,
391 listCut.get(i), resultList, userId);
Mihai Predaeae850c2009-05-13 10:13:48 +0200392 }
Todd Kennedyc2e96d42016-09-08 15:34:03 -0700393 filterResults(resultList);
Mihai Predaeae850c2009-05-13 10:13:48 +0200394 sortResults(resultList);
395 return resultList;
396 }
397
Amith Yamasani483f3b02012-03-13 16:08:00 -0700398 public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
Todd Kennedybe0b8892017-02-15 14:13:52 -0800399 int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 String scheme = intent.getScheme();
401
402 ArrayList<R> finalList = new ArrayList<R>();
403
404 final boolean debug = localLOGV ||
405 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
406
Joe Onorato8a9b2202010-02-26 18:56:32 -0800407 if (debug) Slog.v(
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700408 TAG, "Resolving type=" + resolvedType + " scheme=" + scheme
409 + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700411 F[] firstTypeCut = null;
412 F[] secondTypeCut = null;
413 F[] thirdTypeCut = null;
414 F[] schemeCut = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415
416 // If the intent includes a MIME type, then we want to collect all of
417 // the filters that match that MIME type.
418 if (resolvedType != null) {
419 int slashpos = resolvedType.indexOf('/');
420 if (slashpos > 0) {
421 final String baseType = resolvedType.substring(0, slashpos);
422 if (!baseType.equals("*")) {
423 if (resolvedType.length() != slashpos+2
424 || resolvedType.charAt(slashpos+1) != '*') {
425 // Not a wild card, so we can just look for all filters that
426 // completely match or wildcards whose base type matches.
427 firstTypeCut = mTypeToFilter.get(resolvedType);
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700428 if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 secondTypeCut = mWildTypeToFilter.get(baseType);
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700430 if (debug) Slog.v(TAG, "Second type cut: "
431 + Arrays.toString(secondTypeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 } else {
433 // We can match anything with our base type.
434 firstTypeCut = mBaseTypeToFilter.get(baseType);
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700435 if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 secondTypeCut = mWildTypeToFilter.get(baseType);
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700437 if (debug) Slog.v(TAG, "Second type cut: "
438 + Arrays.toString(secondTypeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 }
440 // Any */* types always apply, but we only need to do this
441 // if the intent type was not already */*.
442 thirdTypeCut = mWildTypeToFilter.get("*");
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700443 if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 } else if (intent.getAction() != null) {
445 // The intent specified any type ({@literal *}/*). This
446 // can be a whole heck of a lot of things, so as a first
447 // cut let's use the action instead.
448 firstTypeCut = mTypedActionToFilter.get(intent.getAction());
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700449 if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 }
451 }
452 }
453
454 // If the intent includes a data URI, then we want to collect all of
455 // the filters that match its scheme (we will further refine matches
456 // on the authority and path by directly matching each resulting filter).
457 if (scheme != null) {
458 schemeCut = mSchemeToFilter.get(scheme);
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700459 if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
461
462 // If the intent does not specify any data -- either a MIME type or
463 // a URI -- then we will only be looking for matches against empty
464 // data.
465 if (resolvedType == null && scheme == null && intent.getAction() != null) {
466 firstTypeCut = mActionToFilter.get(intent.getAction());
Dianne Hackborn38ba6e92013-09-23 11:08:52 -0700467 if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 }
469
Jeff Brown2c376fc2011-01-28 17:34:01 -0800470 FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 if (firstTypeCut != null) {
Todd Kennedybe0b8892017-02-15 14:13:52 -0800472 buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
473 scheme, firstTypeCut, finalList, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 }
475 if (secondTypeCut != null) {
Todd Kennedybe0b8892017-02-15 14:13:52 -0800476 buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
477 scheme, secondTypeCut, finalList, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 }
479 if (thirdTypeCut != null) {
Todd Kennedybe0b8892017-02-15 14:13:52 -0800480 buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
481 scheme, thirdTypeCut, finalList, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 }
483 if (schemeCut != null) {
Todd Kennedybe0b8892017-02-15 14:13:52 -0800484 buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
485 scheme, schemeCut, finalList, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
Todd Kennedyc2e96d42016-09-08 15:34:03 -0700487 filterResults(finalList);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 sortResults(finalList);
489
490 if (debug) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800491 Slog.v(TAG, "Final result list:");
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700492 for (int i=0; i<finalList.size(); i++) {
493 Slog.v(TAG, " " + finalList.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 }
495 }
496 return finalList;
497 }
498
499 /**
500 * Control whether the given filter is allowed to go into the result
501 * list. Mainly intended to prevent adding multiple filters for the
502 * same target object.
503 */
504 protected boolean allowFilterResult(F filter, List<R> dest) {
505 return true;
506 }
507
Dianne Hackborne7f97212011-02-24 14:40:20 -0800508 /**
509 * Returns whether the object associated with the given filter is
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800510 * "stopped", that is whether it should not be included in the result
Dianne Hackborne7f97212011-02-24 14:40:20 -0800511 * if the intent requests to excluded stopped objects.
512 */
Amith Yamasani483f3b02012-03-13 16:08:00 -0700513 protected boolean isFilterStopped(F filter, int userId) {
Dianne Hackborne7f97212011-02-24 14:40:20 -0800514 return false;
515 }
516
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700517 /**
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800518 * Returns whether the given filter is "verified" that is whether it has been verified against
519 * its data URIs.
520 *
521 * The verification would happen only and only if the Intent action is
522 * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
523 * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
524 * is "http" or "https".
525 *
526 * @see android.content.IntentFilter#setAutoVerify(boolean)
527 * @see android.content.IntentFilter#getAutoVerify()
528 */
529 protected boolean isFilterVerified(F filter) {
530 return filter.isVerified();
531 }
532
533 /**
Ben Gruver4efe9402013-04-02 21:18:41 -0700534 * Returns whether this filter is owned by this package. This must be
535 * implemented to provide correct filtering of Intents that have
536 * specified a package name they are to be delivered to.
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700537 */
Ben Gruver4efe9402013-04-02 21:18:41 -0700538 protected abstract boolean isPackageForFilter(String packageName, F filter);
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700539
540 protected abstract F[] newArray(int size);
541
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700542 @SuppressWarnings("unchecked")
Amith Yamasani483f3b02012-03-13 16:08:00 -0700543 protected R newResult(F filter, int match, int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 return (R)filter;
545 }
546
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700547 @SuppressWarnings("unchecked")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 protected void sortResults(List<R> results) {
549 Collections.sort(results, mResolvePrioritySorter);
550 }
551
Todd Kennedyc2e96d42016-09-08 15:34:03 -0700552 /**
553 * Apply filtering to the results. This happens before the results are sorted.
554 */
555 protected void filterResults(List<R> results) {
556 }
557
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700558 protected void dumpFilter(PrintWriter out, String prefix, F filter) {
559 out.print(prefix); out.println(filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 }
561
Dianne Hackbornd052a942014-11-21 15:23:13 -0800562 protected Object filterToLabel(F filter) {
563 return "IntentFilter";
564 }
565
566 protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
567 out.print(prefix); out.print(label); out.print(": "); out.println(count);
568 }
569
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700570 private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) {
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700571 F[] array = map.get(name);
572 if (array == null) {
573 array = newArray(2);
574 map.put(name, array);
575 array[0] = filter;
576 } else {
577 final int N = array.length;
578 int i = N;
579 while (i > 0 && array[i-1] == null) {
580 i--;
581 }
582 if (i < N) {
583 array[i] = filter;
584 } else {
585 F[] newa = newArray((N*3)/2);
586 System.arraycopy(array, 0, newa, 0, N);
587 newa[N] = filter;
588 map.put(name, newa);
589 }
590 }
591 }
592
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 private final int register_mime_types(F filter, String prefix) {
594 final Iterator<String> i = filter.typesIterator();
595 if (i == null) {
596 return 0;
597 }
598
599 int num = 0;
600 while (i.hasNext()) {
Kenny Root502e9a42011-01-10 13:48:15 -0800601 String name = i.next();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 num++;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800603 if (localLOGV) Slog.v(TAG, prefix + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 String baseName = name;
605 final int slashpos = name.indexOf('/');
606 if (slashpos > 0) {
607 baseName = name.substring(0, slashpos).intern();
608 } else {
609 name = name + "/*";
610 }
611
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700612 addFilter(mTypeToFilter, name, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613
614 if (slashpos > 0) {
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700615 addFilter(mBaseTypeToFilter, baseName, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 } else {
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700617 addFilter(mWildTypeToFilter, baseName, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 }
619 }
620
621 return num;
622 }
623
624 private final int unregister_mime_types(F filter, String prefix) {
625 final Iterator<String> i = filter.typesIterator();
626 if (i == null) {
627 return 0;
628 }
629
630 int num = 0;
631 while (i.hasNext()) {
Kenny Root502e9a42011-01-10 13:48:15 -0800632 String name = i.next();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 num++;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800634 if (localLOGV) Slog.v(TAG, prefix + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 String baseName = name;
636 final int slashpos = name.indexOf('/');
637 if (slashpos > 0) {
638 baseName = name.substring(0, slashpos).intern();
639 } else {
640 name = name + "/*";
641 }
642
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700643 remove_all_objects(mTypeToFilter, name, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644
645 if (slashpos > 0) {
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700646 remove_all_objects(mBaseTypeToFilter, baseName, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 } else {
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700648 remove_all_objects(mWildTypeToFilter, baseName, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 }
650 }
651 return num;
652 }
653
654 private final int register_intent_filter(F filter, Iterator<String> i,
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700655 ArrayMap<String, F[]> dest, String prefix) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 if (i == null) {
657 return 0;
658 }
659
660 int num = 0;
661 while (i.hasNext()) {
662 String name = i.next();
663 num++;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800664 if (localLOGV) Slog.v(TAG, prefix + name);
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700665 addFilter(dest, name, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 }
667 return num;
668 }
669
670 private final int unregister_intent_filter(F filter, Iterator<String> i,
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700671 ArrayMap<String, F[]> dest, String prefix) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 if (i == null) {
673 return 0;
674 }
675
676 int num = 0;
677 while (i.hasNext()) {
678 String name = i.next();
679 num++;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800680 if (localLOGV) Slog.v(TAG, prefix + name);
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700681 remove_all_objects(dest, name, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 }
683 return num;
684 }
685
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700686 private final void remove_all_objects(ArrayMap<String, F[]> map, String name,
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700687 Object object) {
688 F[] array = map.get(name);
689 if (array != null) {
690 int LAST = array.length-1;
691 while (LAST >= 0 && array[LAST] == null) {
692 LAST--;
693 }
694 for (int idx=LAST; idx>=0; idx--) {
695 if (array[idx] == object) {
696 final int remain = LAST - idx;
697 if (remain > 0) {
698 System.arraycopy(array, idx+1, array, idx, remain);
699 }
700 array[LAST] = null;
701 LAST--;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 }
703 }
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700704 if (LAST < 0) {
705 map.remove(name);
706 } else if (LAST < (array.length/2)) {
707 F[] newa = newArray(LAST+2);
708 System.arraycopy(array, 0, newa, 0, LAST+1);
709 map.put(name, newa);
710 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 }
713
Jeff Brown2c376fc2011-01-28 17:34:01 -0800714 private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
715 final Set<String> categories = intent.getCategories();
716 if (categories == null) {
717 return null;
718 }
719 return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
720 }
721
722 private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
Todd Kennedybe0b8892017-02-15 14:13:52 -0800723 boolean debug, boolean defaultOnly, String resolvedType, String scheme,
724 F[] src, List<R> dest, int userId) {
Jeff Brown2c376fc2011-01-28 17:34:01 -0800725 final String action = intent.getAction();
726 final Uri data = intent.getData();
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700727 final String packageName = intent.getPackage();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728
Dianne Hackborne7f97212011-02-24 14:40:20 -0800729 final boolean excludingStopped = intent.isExcludingStopped();
730
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700731 final Printer logPrinter;
732 final PrintWriter logPrintWriter;
733 if (debug) {
734 logPrinter = new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM);
735 logPrintWriter = new FastPrintWriter(logPrinter);
736 } else {
737 logPrinter = null;
738 logPrintWriter = null;
739 }
740
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700741 final int N = src != null ? src.length : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 boolean hasNonDefaults = false;
743 int i;
Dianne Hackborn9ec6cdd2012-05-31 10:57:54 -0700744 F filter;
745 for (i=0; i<N && (filter=src[i]) != null; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 int match;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800747 if (debug) Slog.v(TAG, "Matching against filter " + filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748
Amith Yamasani483f3b02012-03-13 16:08:00 -0700749 if (excludingStopped && isFilterStopped(filter, userId)) {
Dianne Hackborne7f97212011-02-24 14:40:20 -0800750 if (debug) {
751 Slog.v(TAG, " Filter's target is stopped; skipping");
752 }
753 continue;
754 }
755
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700756 // Is delivery being limited to filters owned by a particular package?
Ben Gruver4efe9402013-04-02 21:18:41 -0700757 if (packageName != null && !isPackageForFilter(packageName, filter)) {
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700758 if (debug) {
759 Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
760 }
761 continue;
762 }
763
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800764 // Are we verified ?
765 if (filter.getAutoVerify()) {
766 if (localVerificationLOGV || debug) {
767 Slog.v(TAG, " Filter verified: " + isFilterVerified(filter));
Christopher Tate72c10a22015-06-12 18:31:24 -0700768 int authorities = filter.countDataAuthorities();
769 for (int z = 0; z < authorities; z++) {
770 Slog.v(TAG, " " + filter.getDataAuthority(z).getHost());
771 }
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800772 }
773 }
774
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 // Do we already have this one?
776 if (!allowFilterResult(filter, dest)) {
777 if (debug) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800778 Slog.v(TAG, " Filter's target already added");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 }
780 continue;
781 }
782
Jeff Brown2c376fc2011-01-28 17:34:01 -0800783 match = filter.match(action, resolvedType, scheme, data, categories, TAG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 if (match >= 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800785 if (debug) Slog.v(TAG, " Filter matched! match=0x" +
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700786 Integer.toHexString(match) + " hasDefault="
787 + filter.hasCategory(Intent.CATEGORY_DEFAULT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
Amith Yamasani483f3b02012-03-13 16:08:00 -0700789 final R oneResult = newResult(filter, match, userId);
Eugene Susla4ab95112018-12-17 14:45:11 -0800790 if (debug) Slog.v(TAG, " Created result: " + oneResult);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 if (oneResult != null) {
792 dest.add(oneResult);
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700793 if (debug) {
794 dumpFilter(logPrintWriter, " ", filter);
795 logPrintWriter.flush();
796 filter.dump(logPrinter, " ");
797 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 }
799 } else {
800 hasNonDefaults = true;
801 }
802 } else {
803 if (debug) {
804 String reason;
805 switch (match) {
806 case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
807 case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
808 case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
809 case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
810 default: reason = "unknown reason"; break;
811 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800812 Slog.v(TAG, " Filter did not match: " + reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 }
814 }
815 }
816
Todd Kennedy5cb3b7a2016-03-25 06:58:12 -0700817 if (debug && hasNonDefaults) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700818 if (dest.size() == 0) {
Todd Kennedy5cb3b7a2016-03-25 06:58:12 -0700819 Slog.v(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700820 } else if (dest.size() > 1) {
Todd Kennedy5cb3b7a2016-03-25 06:58:12 -0700821 Slog.v(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700822 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 }
824 }
825
826 // Sorts a List of IntentFilter objects into descending priority order.
Dianne Hackborn6c418d52011-06-29 14:05:33 -0700827 @SuppressWarnings("rawtypes")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 private static final Comparator mResolvePrioritySorter = new Comparator() {
829 public int compare(Object o1, Object o2) {
Kenny Root502e9a42011-01-10 13:48:15 -0800830 final int q1 = ((IntentFilter) o1).getPriority();
831 final int q2 = ((IntentFilter) o2).getPriority();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);
833 }
834 };
835
836 /**
837 * All filters that have been registered.
838 */
Jeff Sharkey9f837a92014-10-24 12:07:24 -0700839 private final ArraySet<F> mFilters = new ArraySet<F>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840
841 /**
842 * All of the MIME types that have been registered, such as "image/jpeg",
843 * "image/*", or "{@literal *}/*".
844 */
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700845 private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846
847 /**
848 * The base names of all of all fully qualified MIME types that have been
849 * registered, such as "image" or "*". Wild card MIME types such as
850 * "image/*" will not be here.
851 */
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700852 private final ArrayMap<String, F[]> mBaseTypeToFilter = new ArrayMap<String, F[]>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853
854 /**
855 * The base names of all of the MIME types with a sub-type wildcard that
856 * have been registered. For example, a filter with "image/*" will be
857 * included here as "image" but one with "image/jpeg" will not be
858 * included here. This also includes the "*" for the "{@literal *}/*"
859 * MIME type.
860 */
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700861 private final ArrayMap<String, F[]> mWildTypeToFilter = new ArrayMap<String, F[]>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862
863 /**
864 * All of the URI schemes (such as http) that have been registered.
865 */
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700866 private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867
868 /**
869 * All of the actions that have been registered, but only those that did
870 * not specify data.
871 */
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700872 private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873
874 /**
875 * All of the actions that have been registered and specified a MIME type.
876 */
Dianne Hackbornf4bf0ae2013-05-20 18:42:16 -0700877 private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878}