1 /*
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 
17 package com.android.server;
18 
19 import java.io.PrintWriter;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Set;
27 
28 import android.net.Uri;
29 import android.util.FastImmutableArraySet;
30 import android.util.ArrayMap;
31 import android.util.ArraySet;
32 import android.util.Log;
33 import android.util.MutableInt;
34 import android.util.PrintWriterPrinter;
35 import android.util.Slog;
36 import android.util.LogPrinter;
37 import android.util.Printer;
38 
39 import android.content.Intent;
40 import android.content.IntentFilter;
41 import android.util.proto.ProtoOutputStream;
42 
43 import com.android.internal.util.FastPrintWriter;
44 
45 /**
46  * {@hide}
47  */
48 public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
49     final private static String TAG = "IntentResolver";
50     final private static boolean DEBUG = false;
51     final private static boolean localLOGV = DEBUG || false;
52     final private static boolean localVerificationLOGV = DEBUG || false;
53 
addFilter(F f)54     public void addFilter(F f) {
55         if (localLOGV) {
56             Slog.v(TAG, "Adding filter: " + f);
57             f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
58             Slog.v(TAG, "    Building Lookup Maps:");
59         }
60 
61         mFilters.add(f);
62         int numS = register_intent_filter(f, f.schemesIterator(),
63                 mSchemeToFilter, "      Scheme: ");
64         int numT = register_mime_types(f, "      Type: ");
65         if (numS == 0 && numT == 0) {
66             register_intent_filter(f, f.actionsIterator(),
67                     mActionToFilter, "      Action: ");
68         }
69         if (numT != 0) {
70             register_intent_filter(f, f.actionsIterator(),
71                     mTypedActionToFilter, "      TypedAction: ");
72         }
73     }
74 
filterEquals(IntentFilter f1, IntentFilter f2)75     public static boolean filterEquals(IntentFilter f1, IntentFilter f2) {
76         int s1 = f1.countActions();
77         int s2 = f2.countActions();
78         if (s1 != s2) {
79             return false;
80         }
81         for (int i=0; i<s1; i++) {
82             if (!f2.hasAction(f1.getAction(i))) {
83                 return false;
84             }
85         }
86         s1 = f1.countCategories();
87         s2 = f2.countCategories();
88         if (s1 != s2) {
89             return false;
90         }
91         for (int i=0; i<s1; i++) {
92             if (!f2.hasCategory(f1.getCategory(i))) {
93                 return false;
94             }
95         }
96         s1 = f1.countDataTypes();
97         s2 = f2.countDataTypes();
98         if (s1 != s2) {
99             return false;
100         }
101         for (int i=0; i<s1; i++) {
102             if (!f2.hasExactDataType(f1.getDataType(i))) {
103                 return false;
104             }
105         }
106         s1 = f1.countDataSchemes();
107         s2 = f2.countDataSchemes();
108         if (s1 != s2) {
109             return false;
110         }
111         for (int i=0; i<s1; i++) {
112             if (!f2.hasDataScheme(f1.getDataScheme(i))) {
113                 return false;
114             }
115         }
116         s1 = f1.countDataAuthorities();
117         s2 = f2.countDataAuthorities();
118         if (s1 != s2) {
119             return false;
120         }
121         for (int i=0; i<s1; i++) {
122             if (!f2.hasDataAuthority(f1.getDataAuthority(i))) {
123                 return false;
124             }
125         }
126         s1 = f1.countDataPaths();
127         s2 = f2.countDataPaths();
128         if (s1 != s2) {
129             return false;
130         }
131         for (int i=0; i<s1; i++) {
132             if (!f2.hasDataPath(f1.getDataPath(i))) {
133                 return false;
134             }
135         }
136         s1 = f1.countDataSchemeSpecificParts();
137         s2 = f2.countDataSchemeSpecificParts();
138         if (s1 != s2) {
139             return false;
140         }
141         for (int i=0; i<s1; i++) {
142             if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) {
143                 return false;
144             }
145         }
146         return true;
147     }
148 
collectFilters(F[] array, IntentFilter matching)149     private ArrayList<F> collectFilters(F[] array, IntentFilter matching) {
150         ArrayList<F> res = null;
151         if (array != null) {
152             for (int i=0; i<array.length; i++) {
153                 F cur = array[i];
154                 if (cur == null) {
155                     break;
156                 }
157                 if (filterEquals(cur, matching)) {
158                     if (res == null) {
159                         res = new ArrayList<>();
160                     }
161                     res.add(cur);
162                 }
163             }
164         }
165         return res;
166     }
167 
findFilters(IntentFilter matching)168     public ArrayList<F> findFilters(IntentFilter matching) {
169         if (matching.countDataSchemes() == 1) {
170             // Fast case.
171             return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching);
172         } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) {
173             // Another fast case.
174             return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching);
175         } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0
176                 && matching.countActions() == 1) {
177             // Last fast case.
178             return collectFilters(mActionToFilter.get(matching.getAction(0)), matching);
179         } else {
180             ArrayList<F> res = null;
181             for (F cur : mFilters) {
182                 if (filterEquals(cur, matching)) {
183                     if (res == null) {
184                         res = new ArrayList<>();
185                     }
186                     res.add(cur);
187                 }
188             }
189             return res;
190         }
191     }
192 
removeFilter(F f)193     public void removeFilter(F f) {
194         removeFilterInternal(f);
195         mFilters.remove(f);
196     }
197 
removeFilterInternal(F f)198     void removeFilterInternal(F f) {
199         if (localLOGV) {
200             Slog.v(TAG, "Removing filter: " + f);
201             f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
202             Slog.v(TAG, "    Cleaning Lookup Maps:");
203         }
204 
205         int numS = unregister_intent_filter(f, f.schemesIterator(),
206                 mSchemeToFilter, "      Scheme: ");
207         int numT = unregister_mime_types(f, "      Type: ");
208         if (numS == 0 && numT == 0) {
209             unregister_intent_filter(f, f.actionsIterator(),
210                     mActionToFilter, "      Action: ");
211         }
212         if (numT != 0) {
213             unregister_intent_filter(f, f.actionsIterator(),
214                     mTypedActionToFilter, "      TypedAction: ");
215         }
216     }
217 
dumpMap(PrintWriter out, String titlePrefix, String title, String prefix, ArrayMap<String, F[]> map, String packageName, boolean printFilter, boolean collapseDuplicates)218     boolean dumpMap(PrintWriter out, String titlePrefix, String title,
219             String prefix, ArrayMap<String, F[]> map, String packageName,
220             boolean printFilter, boolean collapseDuplicates) {
221         final String eprefix = prefix + "  ";
222         final String fprefix = prefix + "    ";
223         final ArrayMap<Object, MutableInt> found = new ArrayMap<>();
224         boolean printedSomething = false;
225         Printer printer = null;
226         for (int mapi=0; mapi<map.size(); mapi++) {
227             F[] a = map.valueAt(mapi);
228             final int N = a.length;
229             boolean printedHeader = false;
230             F filter;
231             if (collapseDuplicates && !printFilter) {
232                 found.clear();
233                 for (int i=0; i<N && (filter=a[i]) != null; i++) {
234                     if (packageName != null && !isPackageForFilter(packageName, filter)) {
235                         continue;
236                     }
237                     Object label = filterToLabel(filter);
238                     int index = found.indexOfKey(label);
239                     if (index < 0) {
240                         found.put(label, new MutableInt(1));
241                     } else {
242                         found.valueAt(index).value++;
243                     }
244                 }
245                 for (int i=0; i<found.size(); i++) {
246                     if (title != null) {
247                         out.print(titlePrefix); out.println(title);
248                         title = null;
249                     }
250                     if (!printedHeader) {
251                         out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
252                         printedHeader = true;
253                     }
254                     printedSomething = true;
255                     dumpFilterLabel(out, fprefix, found.keyAt(i), found.valueAt(i).value);
256                 }
257             } else {
258                 for (int i=0; i<N && (filter=a[i]) != null; i++) {
259                     if (packageName != null && !isPackageForFilter(packageName, filter)) {
260                         continue;
261                     }
262                     if (title != null) {
263                         out.print(titlePrefix); out.println(title);
264                         title = null;
265                     }
266                     if (!printedHeader) {
267                         out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
268                         printedHeader = true;
269                     }
270                     printedSomething = true;
271                     dumpFilter(out, fprefix, filter);
272                     if (printFilter) {
273                         if (printer == null) {
274                             printer = new PrintWriterPrinter(out);
275                         }
276                         filter.dump(printer, fprefix + "  ");
277                     }
278                 }
279             }
280         }
281         return printedSomething;
282     }
283 
writeProtoMap(ProtoOutputStream proto, long fieldId, ArrayMap<String, F[]> map)284     void writeProtoMap(ProtoOutputStream proto, long fieldId, ArrayMap<String, F[]> map) {
285         int N = map.size();
286         for (int mapi = 0; mapi < N; mapi++) {
287             long token = proto.start(fieldId);
288             proto.write(IntentResolverProto.ArrayMapEntry.KEY, map.keyAt(mapi));
289             for (F f : map.valueAt(mapi)) {
290                 if (f != null) {
291                     proto.write(IntentResolverProto.ArrayMapEntry.VALUES, f.toString());
292                 }
293             }
294             proto.end(token);
295         }
296     }
297 
writeToProto(ProtoOutputStream proto, long fieldId)298     public void writeToProto(ProtoOutputStream proto, long fieldId) {
299         long token = proto.start(fieldId);
300         writeProtoMap(proto, IntentResolverProto.FULL_MIME_TYPES, mTypeToFilter);
301         writeProtoMap(proto, IntentResolverProto.BASE_MIME_TYPES, mBaseTypeToFilter);
302         writeProtoMap(proto, IntentResolverProto.WILD_MIME_TYPES, mWildTypeToFilter);
303         writeProtoMap(proto, IntentResolverProto.SCHEMES, mSchemeToFilter);
304         writeProtoMap(proto, IntentResolverProto.NON_DATA_ACTIONS, mActionToFilter);
305         writeProtoMap(proto, IntentResolverProto.MIME_TYPED_ACTIONS, mTypedActionToFilter);
306         proto.end(token);
307     }
308 
dump(PrintWriter out, String title, String prefix, String packageName, boolean printFilter, boolean collapseDuplicates)309     public boolean dump(PrintWriter out, String title, String prefix, String packageName,
310             boolean printFilter, boolean collapseDuplicates) {
311         String innerPrefix = prefix + "  ";
312         String sepPrefix = "\n" + prefix;
313         String curPrefix = title + "\n" + prefix;
314         if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
315                 mTypeToFilter, packageName, printFilter, collapseDuplicates)) {
316             curPrefix = sepPrefix;
317         }
318         if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
319                 mBaseTypeToFilter, packageName, printFilter, collapseDuplicates)) {
320             curPrefix = sepPrefix;
321         }
322         if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
323                 mWildTypeToFilter, packageName, printFilter, collapseDuplicates)) {
324             curPrefix = sepPrefix;
325         }
326         if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
327                 mSchemeToFilter, packageName, printFilter, collapseDuplicates)) {
328             curPrefix = sepPrefix;
329         }
330         if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
331                 mActionToFilter, packageName, printFilter, collapseDuplicates)) {
332             curPrefix = sepPrefix;
333         }
334         if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
335                 mTypedActionToFilter, packageName, printFilter, collapseDuplicates)) {
336             curPrefix = sepPrefix;
337         }
338         return curPrefix == sepPrefix;
339     }
340 
341     private class IteratorWrapper implements Iterator<F> {
342         private final Iterator<F> mI;
343         private F mCur;
344 
IteratorWrapper(Iterator<F> it)345         IteratorWrapper(Iterator<F> it) {
346             mI = it;
347         }
348 
hasNext()349         public boolean hasNext() {
350             return mI.hasNext();
351         }
352 
next()353         public F next() {
354             return (mCur = mI.next());
355         }
356 
remove()357         public void remove() {
358             if (mCur != null) {
359                 removeFilterInternal(mCur);
360             }
361             mI.remove();
362         }
363 
364     }
365 
366     /**
367      * Returns an iterator allowing filters to be removed.
368      */
filterIterator()369     public Iterator<F> filterIterator() {
370         return new IteratorWrapper(mFilters.iterator());
371     }
372 
373     /**
374      * Returns a read-only set of the filters.
375      */
filterSet()376     public Set<F> filterSet() {
377         return Collections.unmodifiableSet(mFilters);
378     }
379 
queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId)380     public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,
381             ArrayList<F[]> listCut, int userId) {
382         ArrayList<R> resultList = new ArrayList<R>();
383 
384         final boolean debug = localLOGV ||
385                 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
386 
387         FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
388         final String scheme = intent.getScheme();
389         int N = listCut.size();
390         for (int i = 0; i < N; ++i) {
391             buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme,
392                     listCut.get(i), resultList, userId);
393         }
394         filterResults(resultList);
395         sortResults(resultList);
396         return resultList;
397     }
398 
queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId)399     public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
400             int userId) {
401         String scheme = intent.getScheme();
402 
403         ArrayList<R> finalList = new ArrayList<R>();
404 
405         final boolean debug = localLOGV ||
406                 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
407 
408         if (debug) Slog.v(
409             TAG, "Resolving type=" + resolvedType + " scheme=" + scheme
410             + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent);
411 
412         F[] firstTypeCut = null;
413         F[] secondTypeCut = null;
414         F[] thirdTypeCut = null;
415         F[] schemeCut = null;
416 
417         // If the intent includes a MIME type, then we want to collect all of
418         // the filters that match that MIME type.
419         if (resolvedType != null) {
420             int slashpos = resolvedType.indexOf('/');
421             if (slashpos > 0) {
422                 final String baseType = resolvedType.substring(0, slashpos);
423                 if (!baseType.equals("*")) {
424                     if (resolvedType.length() != slashpos+2
425                             || resolvedType.charAt(slashpos+1) != '*') {
426                         // Not a wild card, so we can just look for all filters that
427                         // completely match or wildcards whose base type matches.
428                         firstTypeCut = mTypeToFilter.get(resolvedType);
429                         if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
430                         secondTypeCut = mWildTypeToFilter.get(baseType);
431                         if (debug) Slog.v(TAG, "Second type cut: "
432                                 + Arrays.toString(secondTypeCut));
433                     } else {
434                         // We can match anything with our base type.
435                         firstTypeCut = mBaseTypeToFilter.get(baseType);
436                         if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
437                         secondTypeCut = mWildTypeToFilter.get(baseType);
438                         if (debug) Slog.v(TAG, "Second type cut: "
439                                 + Arrays.toString(secondTypeCut));
440                     }
441                     // Any */* types always apply, but we only need to do this
442                     // if the intent type was not already */*.
443                     thirdTypeCut = mWildTypeToFilter.get("*");
444                     if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut));
445                 } else if (intent.getAction() != null) {
446                     // The intent specified any type ({@literal *}/*).  This
447                     // can be a whole heck of a lot of things, so as a first
448                     // cut let's use the action instead.
449                     firstTypeCut = mTypedActionToFilter.get(intent.getAction());
450                     if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut));
451                 }
452             }
453         }
454 
455         // If the intent includes a data URI, then we want to collect all of
456         // the filters that match its scheme (we will further refine matches
457         // on the authority and path by directly matching each resulting filter).
458         if (scheme != null) {
459             schemeCut = mSchemeToFilter.get(scheme);
460             if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut));
461         }
462 
463         // If the intent does not specify any data -- either a MIME type or
464         // a URI -- then we will only be looking for matches against empty
465         // data.
466         if (resolvedType == null && scheme == null && intent.getAction() != null) {
467             firstTypeCut = mActionToFilter.get(intent.getAction());
468             if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut));
469         }
470 
471         FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
472         if (firstTypeCut != null) {
473             buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
474                     scheme, firstTypeCut, finalList, userId);
475         }
476         if (secondTypeCut != null) {
477             buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
478                     scheme, secondTypeCut, finalList, userId);
479         }
480         if (thirdTypeCut != null) {
481             buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
482                     scheme, thirdTypeCut, finalList, userId);
483         }
484         if (schemeCut != null) {
485             buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
486                     scheme, schemeCut, finalList, userId);
487         }
488         filterResults(finalList);
489         sortResults(finalList);
490 
491         if (debug) {
492             Slog.v(TAG, "Final result list:");
493             for (int i=0; i<finalList.size(); i++) {
494                 Slog.v(TAG, "  " + finalList.get(i));
495             }
496         }
497         return finalList;
498     }
499 
500     /**
501      * Control whether the given filter is allowed to go into the result
502      * list.  Mainly intended to prevent adding multiple filters for the
503      * same target object.
504      */
allowFilterResult(F filter, List<R> dest)505     protected boolean allowFilterResult(F filter, List<R> dest) {
506         return true;
507     }
508 
509     /**
510      * Returns whether the object associated with the given filter is
511      * "stopped", that is whether it should not be included in the result
512      * if the intent requests to excluded stopped objects.
513      */
isFilterStopped(F filter, int userId)514     protected boolean isFilterStopped(F filter, int userId) {
515         return false;
516     }
517 
518     /**
519      * Returns whether the given filter is "verified" that is whether it has been verified against
520      * its data URIs.
521      *
522      * The verification would happen only and only if the Intent action is
523      * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
524      * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
525      * is "http" or "https".
526      *
527      * @see android.content.IntentFilter#setAutoVerify(boolean)
528      * @see android.content.IntentFilter#getAutoVerify()
529      */
isFilterVerified(F filter)530     protected boolean isFilterVerified(F filter) {
531         return filter.isVerified();
532     }
533 
534     /**
535      * Returns whether this filter is owned by this package. This must be
536      * implemented to provide correct filtering of Intents that have
537      * specified a package name they are to be delivered to.
538      */
isPackageForFilter(String packageName, F filter)539     protected abstract boolean isPackageForFilter(String packageName, F filter);
540 
newArray(int size)541     protected abstract F[] newArray(int size);
542 
543     @SuppressWarnings("unchecked")
newResult(F filter, int match, int userId)544     protected R newResult(F filter, int match, int userId) {
545         return (R)filter;
546     }
547 
548     @SuppressWarnings("unchecked")
sortResults(List<R> results)549     protected void sortResults(List<R> results) {
550         Collections.sort(results, mResolvePrioritySorter);
551     }
552 
553     /**
554      * Apply filtering to the results. This happens before the results are sorted.
555      */
filterResults(List<R> results)556     protected void filterResults(List<R> results) {
557     }
558 
dumpFilter(PrintWriter out, String prefix, F filter)559     protected void dumpFilter(PrintWriter out, String prefix, F filter) {
560         out.print(prefix); out.println(filter);
561     }
562 
filterToLabel(F filter)563     protected Object filterToLabel(F filter) {
564         return "IntentFilter";
565     }
566 
dumpFilterLabel(PrintWriter out, String prefix, Object label, int count)567     protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
568         out.print(prefix); out.print(label); out.print(": "); out.println(count);
569     }
570 
addFilter(ArrayMap<String, F[]> map, String name, F filter)571     private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) {
572         F[] array = map.get(name);
573         if (array == null) {
574             array = newArray(2);
575             map.put(name,  array);
576             array[0] = filter;
577         } else {
578             final int N = array.length;
579             int i = N;
580             while (i > 0 && array[i-1] == null) {
581                 i--;
582             }
583             if (i < N) {
584                 array[i] = filter;
585             } else {
586                 F[] newa = newArray((N*3)/2);
587                 System.arraycopy(array, 0, newa, 0, N);
588                 newa[N] = filter;
589                 map.put(name, newa);
590             }
591         }
592     }
593 
register_mime_types(F filter, String prefix)594     private final int register_mime_types(F filter, String prefix) {
595         final Iterator<String> i = filter.typesIterator();
596         if (i == null) {
597             return 0;
598         }
599 
600         int num = 0;
601         while (i.hasNext()) {
602             String name = i.next();
603             num++;
604             if (localLOGV) Slog.v(TAG, prefix + name);
605             String baseName = name;
606             final int slashpos = name.indexOf('/');
607             if (slashpos > 0) {
608                 baseName = name.substring(0, slashpos).intern();
609             } else {
610                 name = name + "/*";
611             }
612 
613             addFilter(mTypeToFilter, name, filter);
614 
615             if (slashpos > 0) {
616                 addFilter(mBaseTypeToFilter, baseName, filter);
617             } else {
618                 addFilter(mWildTypeToFilter, baseName, filter);
619             }
620         }
621 
622         return num;
623     }
624 
unregister_mime_types(F filter, String prefix)625     private final int unregister_mime_types(F filter, String prefix) {
626         final Iterator<String> i = filter.typesIterator();
627         if (i == null) {
628             return 0;
629         }
630 
631         int num = 0;
632         while (i.hasNext()) {
633             String name = i.next();
634             num++;
635             if (localLOGV) Slog.v(TAG, prefix + name);
636             String baseName = name;
637             final int slashpos = name.indexOf('/');
638             if (slashpos > 0) {
639                 baseName = name.substring(0, slashpos).intern();
640             } else {
641                 name = name + "/*";
642             }
643 
644             remove_all_objects(mTypeToFilter, name, filter);
645 
646             if (slashpos > 0) {
647                 remove_all_objects(mBaseTypeToFilter, baseName, filter);
648             } else {
649                 remove_all_objects(mWildTypeToFilter, baseName, filter);
650             }
651         }
652         return num;
653     }
654 
register_intent_filter(F filter, Iterator<String> i, ArrayMap<String, F[]> dest, String prefix)655     private final int register_intent_filter(F filter, Iterator<String> i,
656             ArrayMap<String, F[]> dest, String prefix) {
657         if (i == null) {
658             return 0;
659         }
660 
661         int num = 0;
662         while (i.hasNext()) {
663             String name = i.next();
664             num++;
665             if (localLOGV) Slog.v(TAG, prefix + name);
666             addFilter(dest, name, filter);
667         }
668         return num;
669     }
670 
unregister_intent_filter(F filter, Iterator<String> i, ArrayMap<String, F[]> dest, String prefix)671     private final int unregister_intent_filter(F filter, Iterator<String> i,
672             ArrayMap<String, F[]> dest, String prefix) {
673         if (i == null) {
674             return 0;
675         }
676 
677         int num = 0;
678         while (i.hasNext()) {
679             String name = i.next();
680             num++;
681             if (localLOGV) Slog.v(TAG, prefix + name);
682             remove_all_objects(dest, name, filter);
683         }
684         return num;
685     }
686 
remove_all_objects(ArrayMap<String, F[]> map, String name, Object object)687     private final void remove_all_objects(ArrayMap<String, F[]> map, String name,
688             Object object) {
689         F[] array = map.get(name);
690         if (array != null) {
691             int LAST = array.length-1;
692             while (LAST >= 0 && array[LAST] == null) {
693                 LAST--;
694             }
695             for (int idx=LAST; idx>=0; idx--) {
696                 if (array[idx] == object) {
697                     final int remain = LAST - idx;
698                     if (remain > 0) {
699                         System.arraycopy(array, idx+1, array, idx, remain);
700                     }
701                     array[LAST] = null;
702                     LAST--;
703                 }
704             }
705             if (LAST < 0) {
706                 map.remove(name);
707             } else if (LAST < (array.length/2)) {
708                 F[] newa = newArray(LAST+2);
709                 System.arraycopy(array, 0, newa, 0, LAST+1);
710                 map.put(name, newa);
711             }
712         }
713     }
714 
getFastIntentCategories(Intent intent)715     private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
716         final Set<String> categories = intent.getCategories();
717         if (categories == null) {
718             return null;
719         }
720         return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
721     }
722 
buildResolveList(Intent intent, FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly, String resolvedType, String scheme, F[] src, List<R> dest, int userId)723     private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
724             boolean debug, boolean defaultOnly, String resolvedType, String scheme,
725             F[] src, List<R> dest, int userId) {
726         final String action = intent.getAction();
727         final Uri data = intent.getData();
728         final String packageName = intent.getPackage();
729 
730         final boolean excludingStopped = intent.isExcludingStopped();
731 
732         final Printer logPrinter;
733         final PrintWriter logPrintWriter;
734         if (debug) {
735             logPrinter = new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM);
736             logPrintWriter = new FastPrintWriter(logPrinter);
737         } else {
738             logPrinter = null;
739             logPrintWriter = null;
740         }
741 
742         final int N = src != null ? src.length : 0;
743         boolean hasNonDefaults = false;
744         int i;
745         F filter;
746         for (i=0; i<N && (filter=src[i]) != null; i++) {
747             int match;
748             if (debug) Slog.v(TAG, "Matching against filter " + filter);
749 
750             if (excludingStopped && isFilterStopped(filter, userId)) {
751                 if (debug) {
752                     Slog.v(TAG, "  Filter's target is stopped; skipping");
753                 }
754                 continue;
755             }
756 
757             // Is delivery being limited to filters owned by a particular package?
758             if (packageName != null && !isPackageForFilter(packageName, filter)) {
759                 if (debug) {
760                     Slog.v(TAG, "  Filter is not from package " + packageName + "; skipping");
761                 }
762                 continue;
763             }
764 
765             // Are we verified ?
766             if (filter.getAutoVerify()) {
767                 if (localVerificationLOGV || debug) {
768                     Slog.v(TAG, "  Filter verified: " + isFilterVerified(filter));
769                     int authorities = filter.countDataAuthorities();
770                     for (int z = 0; z < authorities; z++) {
771                         Slog.v(TAG, "   " + filter.getDataAuthority(z).getHost());
772                     }
773                 }
774             }
775 
776             // Do we already have this one?
777             if (!allowFilterResult(filter, dest)) {
778                 if (debug) {
779                     Slog.v(TAG, "  Filter's target already added");
780                 }
781                 continue;
782             }
783 
784             match = filter.match(action, resolvedType, scheme, data, categories, TAG);
785             if (match >= 0) {
786                 if (debug) Slog.v(TAG, "  Filter matched!  match=0x" +
787                         Integer.toHexString(match) + " hasDefault="
788                         + filter.hasCategory(Intent.CATEGORY_DEFAULT));
789                 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
790                     final R oneResult = newResult(filter, match, userId);
791                     if (oneResult != null) {
792                         dest.add(oneResult);
793                         if (debug) {
794                             dumpFilter(logPrintWriter, "    ", filter);
795                             logPrintWriter.flush();
796                             filter.dump(logPrinter, "    ");
797                         }
798                     }
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                     }
812                     Slog.v(TAG, "  Filter did not match: " + reason);
813                 }
814             }
815         }
816 
817         if (debug && hasNonDefaults) {
818             if (dest.size() == 0) {
819                 Slog.v(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
820             } else if (dest.size() > 1) {
821                 Slog.v(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
822             }
823         }
824     }
825 
826     // Sorts a List of IntentFilter objects into descending priority order.
827     @SuppressWarnings("rawtypes")
828     private static final Comparator mResolvePrioritySorter = new Comparator() {
829         public int compare(Object o1, Object o2) {
830             final int q1 = ((IntentFilter) o1).getPriority();
831             final int q2 = ((IntentFilter) o2).getPriority();
832             return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);
833         }
834     };
835 
836     /**
837      * All filters that have been registered.
838      */
839     private final ArraySet<F> mFilters = new ArraySet<F>();
840 
841     /**
842      * All of the MIME types that have been registered, such as "image/jpeg",
843      * "image/*", or "{@literal *}/*".
844      */
845     private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>();
846 
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      */
852     private final ArrayMap<String, F[]> mBaseTypeToFilter = new ArrayMap<String, F[]>();
853 
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      */
861     private final ArrayMap<String, F[]> mWildTypeToFilter = new ArrayMap<String, F[]>();
862 
863     /**
864      * All of the URI schemes (such as http) that have been registered.
865      */
866     private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
867 
868     /**
869      * All of the actions that have been registered, but only those that did
870      * not specify data.
871      */
872     private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>();
873 
874     /**
875      * All of the actions that have been registered and specified a MIME type.
876      */
877     private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>();
878 }
879