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    final private static boolean localVerificationLOGV = DEBUG || false;
51
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
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
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
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
191    public void removeFilter(F f) {
192        removeFilterInternal(f);
193        mFilters.remove(f);
194    }
195
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
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
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
318        IteratorWrapper(Iterator<F> it) {
319            mI = it;
320        }
321
322        public boolean hasNext() {
323            return mI.hasNext();
324        }
325
326        public F next() {
327            return (mCur = mI.next());
328        }
329
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     */
342    public Iterator<F> filterIterator() {
343        return new IteratorWrapper(mFilters.iterator());
344    }
345
346    /**
347     * Returns a read-only set of the filters.
348     */
349    public Set<F> filterSet() {
350        return Collections.unmodifiableSet(mFilters);
351    }
352
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        filterResults(resultList);
368        sortResults(resultList);
369        return resultList;
370    }
371
372    public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
373            int userId) {
374        String scheme = intent.getScheme();
375
376        ArrayList<R> finalList = new ArrayList<R>();
377
378        final boolean debug = localLOGV ||
379                ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
380
381        if (debug) Slog.v(
382            TAG, "Resolving type=" + resolvedType + " scheme=" + scheme
383            + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent);
384
385        F[] firstTypeCut = null;
386        F[] secondTypeCut = null;
387        F[] thirdTypeCut = null;
388        F[] schemeCut = null;
389
390        // If the intent includes a MIME type, then we want to collect all of
391        // the filters that match that MIME type.
392        if (resolvedType != null) {
393            int slashpos = resolvedType.indexOf('/');
394            if (slashpos > 0) {
395                final String baseType = resolvedType.substring(0, slashpos);
396                if (!baseType.equals("*")) {
397                    if (resolvedType.length() != slashpos+2
398                            || resolvedType.charAt(slashpos+1) != '*') {
399                        // Not a wild card, so we can just look for all filters that
400                        // completely match or wildcards whose base type matches.
401                        firstTypeCut = mTypeToFilter.get(resolvedType);
402                        if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
403                        secondTypeCut = mWildTypeToFilter.get(baseType);
404                        if (debug) Slog.v(TAG, "Second type cut: "
405                                + Arrays.toString(secondTypeCut));
406                    } else {
407                        // We can match anything with our base type.
408                        firstTypeCut = mBaseTypeToFilter.get(baseType);
409                        if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
410                        secondTypeCut = mWildTypeToFilter.get(baseType);
411                        if (debug) Slog.v(TAG, "Second type cut: "
412                                + Arrays.toString(secondTypeCut));
413                    }
414                    // Any */* types always apply, but we only need to do this
415                    // if the intent type was not already */*.
416                    thirdTypeCut = mWildTypeToFilter.get("*");
417                    if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut));
418                } else if (intent.getAction() != null) {
419                    // The intent specified any type ({@literal *}/*).  This
420                    // can be a whole heck of a lot of things, so as a first
421                    // cut let's use the action instead.
422                    firstTypeCut = mTypedActionToFilter.get(intent.getAction());
423                    if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut));
424                }
425            }
426        }
427
428        // If the intent includes a data URI, then we want to collect all of
429        // the filters that match its scheme (we will further refine matches
430        // on the authority and path by directly matching each resulting filter).
431        if (scheme != null) {
432            schemeCut = mSchemeToFilter.get(scheme);
433            if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut));
434        }
435
436        // If the intent does not specify any data -- either a MIME type or
437        // a URI -- then we will only be looking for matches against empty
438        // data.
439        if (resolvedType == null && scheme == null && intent.getAction() != null) {
440            firstTypeCut = mActionToFilter.get(intent.getAction());
441            if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut));
442        }
443
444        FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
445        if (firstTypeCut != null) {
446            buildResolveList(intent, categories, debug, defaultOnly,
447                    resolvedType, scheme, firstTypeCut, finalList, userId);
448        }
449        if (secondTypeCut != null) {
450            buildResolveList(intent, categories, debug, defaultOnly,
451                    resolvedType, scheme, secondTypeCut, finalList, userId);
452        }
453        if (thirdTypeCut != null) {
454            buildResolveList(intent, categories, debug, defaultOnly,
455                    resolvedType, scheme, thirdTypeCut, finalList, userId);
456        }
457        if (schemeCut != null) {
458            buildResolveList(intent, categories, debug, defaultOnly,
459                    resolvedType, scheme, schemeCut, finalList, userId);
460        }
461        filterResults(finalList);
462        sortResults(finalList);
463
464        if (debug) {
465            Slog.v(TAG, "Final result list:");
466            for (int i=0; i<finalList.size(); i++) {
467                Slog.v(TAG, "  " + finalList.get(i));
468            }
469        }
470        return finalList;
471    }
472
473    /**
474     * Control whether the given filter is allowed to go into the result
475     * list.  Mainly intended to prevent adding multiple filters for the
476     * same target object.
477     */
478    protected boolean allowFilterResult(F filter, List<R> dest) {
479        return true;
480    }
481
482    /**
483     * Returns whether the object associated with the given filter is
484     * "stopped", that is whether it should not be included in the result
485     * if the intent requests to excluded stopped objects.
486     */
487    protected boolean isFilterStopped(F filter, int userId) {
488        return false;
489    }
490
491    /**
492     * Returns whether the given filter is "verified" that is whether it has been verified against
493     * its data URIs.
494     *
495     * The verification would happen only and only if the Intent action is
496     * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
497     * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
498     * is "http" or "https".
499     *
500     * @see android.content.IntentFilter#setAutoVerify(boolean)
501     * @see android.content.IntentFilter#getAutoVerify()
502     */
503    protected boolean isFilterVerified(F filter) {
504        return filter.isVerified();
505    }
506
507    /**
508     * Returns whether this filter is owned by this package. This must be
509     * implemented to provide correct filtering of Intents that have
510     * specified a package name they are to be delivered to.
511     */
512    protected abstract boolean isPackageForFilter(String packageName, F filter);
513
514    protected abstract F[] newArray(int size);
515
516    @SuppressWarnings("unchecked")
517    protected R newResult(F filter, int match, int userId) {
518        return (R)filter;
519    }
520
521    @SuppressWarnings("unchecked")
522    protected void sortResults(List<R> results) {
523        Collections.sort(results, mResolvePrioritySorter);
524    }
525
526    /**
527     * Apply filtering to the results. This happens before the results are sorted.
528     */
529    protected void filterResults(List<R> results) {
530    }
531
532    protected void dumpFilter(PrintWriter out, String prefix, F filter) {
533        out.print(prefix); out.println(filter);
534    }
535
536    protected Object filterToLabel(F filter) {
537        return "IntentFilter";
538    }
539
540    protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
541        out.print(prefix); out.print(label); out.print(": "); out.println(count);
542    }
543
544    private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) {
545        F[] array = map.get(name);
546        if (array == null) {
547            array = newArray(2);
548            map.put(name,  array);
549            array[0] = filter;
550        } else {
551            final int N = array.length;
552            int i = N;
553            while (i > 0 && array[i-1] == null) {
554                i--;
555            }
556            if (i < N) {
557                array[i] = filter;
558            } else {
559                F[] newa = newArray((N*3)/2);
560                System.arraycopy(array, 0, newa, 0, N);
561                newa[N] = filter;
562                map.put(name, newa);
563            }
564        }
565    }
566
567    private final int register_mime_types(F filter, String prefix) {
568        final Iterator<String> i = filter.typesIterator();
569        if (i == null) {
570            return 0;
571        }
572
573        int num = 0;
574        while (i.hasNext()) {
575            String name = i.next();
576            num++;
577            if (localLOGV) Slog.v(TAG, prefix + name);
578            String baseName = name;
579            final int slashpos = name.indexOf('/');
580            if (slashpos > 0) {
581                baseName = name.substring(0, slashpos).intern();
582            } else {
583                name = name + "/*";
584            }
585
586            addFilter(mTypeToFilter, name, filter);
587
588            if (slashpos > 0) {
589                addFilter(mBaseTypeToFilter, baseName, filter);
590            } else {
591                addFilter(mWildTypeToFilter, baseName, filter);
592            }
593        }
594
595        return num;
596    }
597
598    private final int unregister_mime_types(F filter, String prefix) {
599        final Iterator<String> i = filter.typesIterator();
600        if (i == null) {
601            return 0;
602        }
603
604        int num = 0;
605        while (i.hasNext()) {
606            String name = i.next();
607            num++;
608            if (localLOGV) Slog.v(TAG, prefix + name);
609            String baseName = name;
610            final int slashpos = name.indexOf('/');
611            if (slashpos > 0) {
612                baseName = name.substring(0, slashpos).intern();
613            } else {
614                name = name + "/*";
615            }
616
617            remove_all_objects(mTypeToFilter, name, filter);
618
619            if (slashpos > 0) {
620                remove_all_objects(mBaseTypeToFilter, baseName, filter);
621            } else {
622                remove_all_objects(mWildTypeToFilter, baseName, filter);
623            }
624        }
625        return num;
626    }
627
628    private final int register_intent_filter(F filter, Iterator<String> i,
629            ArrayMap<String, F[]> dest, String prefix) {
630        if (i == null) {
631            return 0;
632        }
633
634        int num = 0;
635        while (i.hasNext()) {
636            String name = i.next();
637            num++;
638            if (localLOGV) Slog.v(TAG, prefix + name);
639            addFilter(dest, name, filter);
640        }
641        return num;
642    }
643
644    private final int unregister_intent_filter(F filter, Iterator<String> i,
645            ArrayMap<String, F[]> dest, String prefix) {
646        if (i == null) {
647            return 0;
648        }
649
650        int num = 0;
651        while (i.hasNext()) {
652            String name = i.next();
653            num++;
654            if (localLOGV) Slog.v(TAG, prefix + name);
655            remove_all_objects(dest, name, filter);
656        }
657        return num;
658    }
659
660    private final void remove_all_objects(ArrayMap<String, F[]> map, String name,
661            Object object) {
662        F[] array = map.get(name);
663        if (array != null) {
664            int LAST = array.length-1;
665            while (LAST >= 0 && array[LAST] == null) {
666                LAST--;
667            }
668            for (int idx=LAST; idx>=0; idx--) {
669                if (array[idx] == object) {
670                    final int remain = LAST - idx;
671                    if (remain > 0) {
672                        System.arraycopy(array, idx+1, array, idx, remain);
673                    }
674                    array[LAST] = null;
675                    LAST--;
676                }
677            }
678            if (LAST < 0) {
679                map.remove(name);
680            } else if (LAST < (array.length/2)) {
681                F[] newa = newArray(LAST+2);
682                System.arraycopy(array, 0, newa, 0, LAST+1);
683                map.put(name, newa);
684            }
685        }
686    }
687
688    private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
689        final Set<String> categories = intent.getCategories();
690        if (categories == null) {
691            return null;
692        }
693        return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
694    }
695
696    private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
697            boolean debug, boolean defaultOnly,
698            String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
699        final String action = intent.getAction();
700        final Uri data = intent.getData();
701        final String packageName = intent.getPackage();
702
703        final boolean excludingStopped = intent.isExcludingStopped();
704
705        final Printer logPrinter;
706        final PrintWriter logPrintWriter;
707        if (debug) {
708            logPrinter = new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM);
709            logPrintWriter = new FastPrintWriter(logPrinter);
710        } else {
711            logPrinter = null;
712            logPrintWriter = null;
713        }
714
715        final int N = src != null ? src.length : 0;
716        boolean hasNonDefaults = false;
717        int i;
718        F filter;
719        for (i=0; i<N && (filter=src[i]) != null; i++) {
720            int match;
721            if (debug) Slog.v(TAG, "Matching against filter " + filter);
722
723            if (excludingStopped && isFilterStopped(filter, userId)) {
724                if (debug) {
725                    Slog.v(TAG, "  Filter's target is stopped; skipping");
726                }
727                continue;
728            }
729
730            // Is delivery being limited to filters owned by a particular package?
731            if (packageName != null && !isPackageForFilter(packageName, filter)) {
732                if (debug) {
733                    Slog.v(TAG, "  Filter is not from package " + packageName + "; skipping");
734                }
735                continue;
736            }
737
738            // Are we verified ?
739            if (filter.getAutoVerify()) {
740                if (localVerificationLOGV || debug) {
741                    Slog.v(TAG, "  Filter verified: " + isFilterVerified(filter));
742                    int authorities = filter.countDataAuthorities();
743                    for (int z = 0; z < authorities; z++) {
744                        Slog.v(TAG, "   " + filter.getDataAuthority(z).getHost());
745                    }
746                }
747            }
748
749            // Do we already have this one?
750            if (!allowFilterResult(filter, dest)) {
751                if (debug) {
752                    Slog.v(TAG, "  Filter's target already added");
753                }
754                continue;
755            }
756
757            match = filter.match(action, resolvedType, scheme, data, categories, TAG);
758            if (match >= 0) {
759                if (debug) Slog.v(TAG, "  Filter matched!  match=0x" +
760                        Integer.toHexString(match) + " hasDefault="
761                        + filter.hasCategory(Intent.CATEGORY_DEFAULT));
762                if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
763                    final R oneResult = newResult(filter, match, userId);
764                    if (oneResult != null) {
765                        dest.add(oneResult);
766                        if (debug) {
767                            dumpFilter(logPrintWriter, "    ", filter);
768                            logPrintWriter.flush();
769                            filter.dump(logPrinter, "    ");
770                        }
771                    }
772                } else {
773                    hasNonDefaults = true;
774                }
775            } else {
776                if (debug) {
777                    String reason;
778                    switch (match) {
779                        case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
780                        case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
781                        case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
782                        case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
783                        default: reason = "unknown reason"; break;
784                    }
785                    Slog.v(TAG, "  Filter did not match: " + reason);
786                }
787            }
788        }
789
790        if (debug && hasNonDefaults) {
791            if (dest.size() == 0) {
792                Slog.v(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
793            } else if (dest.size() > 1) {
794                Slog.v(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
795            }
796        }
797    }
798
799    // Sorts a List of IntentFilter objects into descending priority order.
800    @SuppressWarnings("rawtypes")
801    private static final Comparator mResolvePrioritySorter = new Comparator() {
802        public int compare(Object o1, Object o2) {
803            final int q1 = ((IntentFilter) o1).getPriority();
804            final int q2 = ((IntentFilter) o2).getPriority();
805            return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);
806        }
807    };
808
809    /**
810     * All filters that have been registered.
811     */
812    private final ArraySet<F> mFilters = new ArraySet<F>();
813
814    /**
815     * All of the MIME types that have been registered, such as "image/jpeg",
816     * "image/*", or "{@literal *}/*".
817     */
818    private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>();
819
820    /**
821     * The base names of all of all fully qualified MIME types that have been
822     * registered, such as "image" or "*".  Wild card MIME types such as
823     * "image/*" will not be here.
824     */
825    private final ArrayMap<String, F[]> mBaseTypeToFilter = new ArrayMap<String, F[]>();
826
827    /**
828     * The base names of all of the MIME types with a sub-type wildcard that
829     * have been registered.  For example, a filter with "image/*" will be
830     * included here as "image" but one with "image/jpeg" will not be
831     * included here.  This also includes the "*" for the "{@literal *}/*"
832     * MIME type.
833     */
834    private final ArrayMap<String, F[]> mWildTypeToFilter = new ArrayMap<String, F[]>();
835
836    /**
837     * All of the URI schemes (such as http) that have been registered.
838     */
839    private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
840
841    /**
842     * All of the actions that have been registered, but only those that did
843     * not specify data.
844     */
845    private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>();
846
847    /**
848     * All of the actions that have been registered and specified a MIME type.
849     */
850    private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>();
851}
852