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        sortResults(resultList);
368        return resultList;
369    }
370
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     */
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     */
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     */
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     */
510    protected abstract boolean isPackageForFilter(String packageName, F filter);
511
512    protected abstract F[] newArray(int size);
513
514    @SuppressWarnings("unchecked")
515    protected R newResult(F filter, int match, int userId) {
516        return (R)filter;
517    }
518
519    @SuppressWarnings("unchecked")
520    protected void sortResults(List<R> results) {
521        Collections.sort(results, mResolvePrioritySorter);
522    }
523
524    protected void dumpFilter(PrintWriter out, String prefix, F filter) {
525        out.print(prefix); out.println(filter);
526    }
527
528    protected Object filterToLabel(F filter) {
529        return "IntentFilter";
530    }
531
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
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
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
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
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
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
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
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
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