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