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