• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import java.io.PrintWriter;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Set;
27 
28 import android.net.Uri;
29 import android.util.FastImmutableArraySet;
30 import android.util.ArrayMap;
31 import android.util.ArraySet;
32 import android.util.Log;
33 import android.util.MutableInt;
34 import android.util.PrintWriterPrinter;
35 import android.util.Slog;
36 import android.util.LogPrinter;
37 import android.util.Printer;
38 
39 import android.content.Intent;
40 import android.content.IntentFilter;
41 import com.android.internal.util.FastPrintWriter;
42 
43 /**
44  * {@hide}
45  */
46 public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
47     final private static String TAG = "IntentResolver";
48     final private static boolean DEBUG = false;
49     final private static boolean localLOGV = DEBUG || false;
50     final private static boolean localVerificationLOGV = DEBUG || false;
51 
addFilter(F f)52     public void addFilter(F f) {
53         if (localLOGV) {
54             Slog.v(TAG, "Adding filter: " + f);
55             f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
56             Slog.v(TAG, "    Building Lookup Maps:");
57         }
58 
59         mFilters.add(f);
60         int numS = register_intent_filter(f, f.schemesIterator(),
61                 mSchemeToFilter, "      Scheme: ");
62         int numT = register_mime_types(f, "      Type: ");
63         if (numS == 0 && numT == 0) {
64             register_intent_filter(f, f.actionsIterator(),
65                     mActionToFilter, "      Action: ");
66         }
67         if (numT != 0) {
68             register_intent_filter(f, f.actionsIterator(),
69                     mTypedActionToFilter, "      TypedAction: ");
70         }
71     }
72 
filterEquals(IntentFilter f1, IntentFilter f2)73     private boolean filterEquals(IntentFilter f1, IntentFilter f2) {
74         int s1 = f1.countActions();
75         int s2 = f2.countActions();
76         if (s1 != s2) {
77             return false;
78         }
79         for (int i=0; i<s1; i++) {
80             if (!f2.hasAction(f1.getAction(i))) {
81                 return false;
82             }
83         }
84         s1 = f1.countCategories();
85         s2 = f2.countCategories();
86         if (s1 != s2) {
87             return false;
88         }
89         for (int i=0; i<s1; i++) {
90             if (!f2.hasCategory(f1.getCategory(i))) {
91                 return false;
92             }
93         }
94         s1 = f1.countDataTypes();
95         s2 = f2.countDataTypes();
96         if (s1 != s2) {
97             return false;
98         }
99         for (int i=0; i<s1; i++) {
100             if (!f2.hasExactDataType(f1.getDataType(i))) {
101                 return false;
102             }
103         }
104         s1 = f1.countDataSchemes();
105         s2 = f2.countDataSchemes();
106         if (s1 != s2) {
107             return false;
108         }
109         for (int i=0; i<s1; i++) {
110             if (!f2.hasDataScheme(f1.getDataScheme(i))) {
111                 return false;
112             }
113         }
114         s1 = f1.countDataAuthorities();
115         s2 = f2.countDataAuthorities();
116         if (s1 != s2) {
117             return false;
118         }
119         for (int i=0; i<s1; i++) {
120             if (!f2.hasDataAuthority(f1.getDataAuthority(i))) {
121                 return false;
122             }
123         }
124         s1 = f1.countDataPaths();
125         s2 = f2.countDataPaths();
126         if (s1 != s2) {
127             return false;
128         }
129         for (int i=0; i<s1; i++) {
130             if (!f2.hasDataPath(f1.getDataPath(i))) {
131                 return false;
132             }
133         }
134         s1 = f1.countDataSchemeSpecificParts();
135         s2 = f2.countDataSchemeSpecificParts();
136         if (s1 != s2) {
137             return false;
138         }
139         for (int i=0; i<s1; i++) {
140             if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) {
141                 return false;
142             }
143         }
144         return true;
145     }
146 
collectFilters(F[] array, IntentFilter matching)147     private ArrayList<F> collectFilters(F[] array, IntentFilter matching) {
148         ArrayList<F> res = null;
149         if (array != null) {
150             for (int i=0; i<array.length; i++) {
151                 F cur = array[i];
152                 if (cur == null) {
153                     break;
154                 }
155                 if (filterEquals(cur, matching)) {
156                     if (res == null) {
157                         res = new ArrayList<>();
158                     }
159                     res.add(cur);
160                 }
161             }
162         }
163         return res;
164     }
165 
findFilters(IntentFilter matching)166     public ArrayList<F> findFilters(IntentFilter matching) {
167         if (matching.countDataSchemes() == 1) {
168             // Fast case.
169             return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching);
170         } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) {
171             // Another fast case.
172             return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching);
173         } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0
174                 && matching.countActions() == 1) {
175             // Last fast case.
176             return collectFilters(mActionToFilter.get(matching.getAction(0)), matching);
177         } else {
178             ArrayList<F> res = null;
179             for (F cur : mFilters) {
180                 if (filterEquals(cur, matching)) {
181                     if (res == null) {
182                         res = new ArrayList<>();
183                     }
184                     res.add(cur);
185                 }
186             }
187             return res;
188         }
189     }
190 
removeFilter(F f)191     public void removeFilter(F f) {
192         removeFilterInternal(f);
193         mFilters.remove(f);
194     }
195 
removeFilterInternal(F f)196     void removeFilterInternal(F f) {
197         if (localLOGV) {
198             Slog.v(TAG, "Removing filter: " + f);
199             f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
200             Slog.v(TAG, "    Cleaning Lookup Maps:");
201         }
202 
203         int numS = unregister_intent_filter(f, f.schemesIterator(),
204                 mSchemeToFilter, "      Scheme: ");
205         int numT = unregister_mime_types(f, "      Type: ");
206         if (numS == 0 && numT == 0) {
207             unregister_intent_filter(f, f.actionsIterator(),
208                     mActionToFilter, "      Action: ");
209         }
210         if (numT != 0) {
211             unregister_intent_filter(f, f.actionsIterator(),
212                     mTypedActionToFilter, "      TypedAction: ");
213         }
214     }
215 
dumpMap(PrintWriter out, String titlePrefix, String title, String prefix, ArrayMap<String, F[]> map, String packageName, boolean printFilter, boolean collapseDuplicates)216     boolean dumpMap(PrintWriter out, String titlePrefix, String title,
217             String prefix, ArrayMap<String, F[]> map, String packageName,
218             boolean printFilter, boolean collapseDuplicates) {
219         final String eprefix = prefix + "  ";
220         final String fprefix = prefix + "    ";
221         final ArrayMap<Object, MutableInt> found = new ArrayMap<>();
222         boolean printedSomething = false;
223         Printer printer = null;
224         for (int mapi=0; mapi<map.size(); mapi++) {
225             F[] a = map.valueAt(mapi);
226             final int N = a.length;
227             boolean printedHeader = false;
228             F filter;
229             if (collapseDuplicates && !printFilter) {
230                 found.clear();
231                 for (int i=0; i<N && (filter=a[i]) != null; i++) {
232                     if (packageName != null && !isPackageForFilter(packageName, filter)) {
233                         continue;
234                     }
235                     Object label = filterToLabel(filter);
236                     int index = found.indexOfKey(label);
237                     if (index < 0) {
238                         found.put(label, new MutableInt(1));
239                     } else {
240                         found.valueAt(index).value++;
241                     }
242                 }
243                 for (int i=0; i<found.size(); i++) {
244                     if (title != null) {
245                         out.print(titlePrefix); out.println(title);
246                         title = null;
247                     }
248                     if (!printedHeader) {
249                         out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
250                         printedHeader = true;
251                     }
252                     printedSomething = true;
253                     dumpFilterLabel(out, fprefix, found.keyAt(i), found.valueAt(i).value);
254                 }
255             } else {
256                 for (int i=0; i<N && (filter=a[i]) != null; i++) {
257                     if (packageName != null && !isPackageForFilter(packageName, filter)) {
258                         continue;
259                     }
260                     if (title != null) {
261                         out.print(titlePrefix); out.println(title);
262                         title = null;
263                     }
264                     if (!printedHeader) {
265                         out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
266                         printedHeader = true;
267                     }
268                     printedSomething = true;
269                     dumpFilter(out, fprefix, filter);
270                     if (printFilter) {
271                         if (printer == null) {
272                             printer = new PrintWriterPrinter(out);
273                         }
274                         filter.dump(printer, fprefix + "  ");
275                     }
276                 }
277             }
278         }
279         return printedSomething;
280     }
281 
dump(PrintWriter out, String title, String prefix, String packageName, boolean printFilter, boolean collapseDuplicates)282     public boolean dump(PrintWriter out, String title, String prefix, String packageName,
283             boolean printFilter, boolean collapseDuplicates) {
284         String innerPrefix = prefix + "  ";
285         String sepPrefix = "\n" + prefix;
286         String curPrefix = title + "\n" + prefix;
287         if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
288                 mTypeToFilter, packageName, printFilter, collapseDuplicates)) {
289             curPrefix = sepPrefix;
290         }
291         if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
292                 mBaseTypeToFilter, packageName, printFilter, collapseDuplicates)) {
293             curPrefix = sepPrefix;
294         }
295         if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
296                 mWildTypeToFilter, packageName, printFilter, collapseDuplicates)) {
297             curPrefix = sepPrefix;
298         }
299         if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
300                 mSchemeToFilter, packageName, printFilter, collapseDuplicates)) {
301             curPrefix = sepPrefix;
302         }
303         if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
304                 mActionToFilter, packageName, printFilter, collapseDuplicates)) {
305             curPrefix = sepPrefix;
306         }
307         if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
308                 mTypedActionToFilter, packageName, printFilter, collapseDuplicates)) {
309             curPrefix = sepPrefix;
310         }
311         return curPrefix == sepPrefix;
312     }
313 
314     private class IteratorWrapper implements Iterator<F> {
315         private final Iterator<F> mI;
316         private F mCur;
317 
IteratorWrapper(Iterator<F> it)318         IteratorWrapper(Iterator<F> it) {
319             mI = it;
320         }
321 
hasNext()322         public boolean hasNext() {
323             return mI.hasNext();
324         }
325 
next()326         public F next() {
327             return (mCur = mI.next());
328         }
329 
remove()330         public void remove() {
331             if (mCur != null) {
332                 removeFilterInternal(mCur);
333             }
334             mI.remove();
335         }
336 
337     }
338 
339     /**
340      * Returns an iterator allowing filters to be removed.
341      */
filterIterator()342     public Iterator<F> filterIterator() {
343         return new IteratorWrapper(mFilters.iterator());
344     }
345 
346     /**
347      * Returns a read-only set of the filters.
348      */
filterSet()349     public Set<F> filterSet() {
350         return Collections.unmodifiableSet(mFilters);
351     }
352 
queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId)353     public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,
354             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, resolvedType, scheme,
365                     listCut.get(i), resultList, userId);
366         }
367         filterResults(resultList);
368         sortResults(resultList);
369         return resultList;
370     }
371 
queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId)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, resolvedType,
447                     scheme, firstTypeCut, finalList, userId);
448         }
449         if (secondTypeCut != null) {
450             buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
451                     scheme, secondTypeCut, finalList, userId);
452         }
453         if (thirdTypeCut != null) {
454             buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
455                     scheme, thirdTypeCut, finalList, userId);
456         }
457         if (schemeCut != null) {
458             buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
459                     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      */
allowFilterResult(F filter, List<R> dest)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      */
isFilterStopped(F filter, int userId)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      */
isFilterVerified(F filter)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      */
isPackageForFilter(String packageName, F filter)512     protected abstract boolean isPackageForFilter(String packageName, F filter);
513 
newArray(int size)514     protected abstract F[] newArray(int size);
515 
516     @SuppressWarnings("unchecked")
newResult(F filter, int match, int userId)517     protected R newResult(F filter, int match, int userId) {
518         return (R)filter;
519     }
520 
521     @SuppressWarnings("unchecked")
sortResults(List<R> results)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      */
filterResults(List<R> results)529     protected void filterResults(List<R> results) {
530     }
531 
dumpFilter(PrintWriter out, String prefix, F filter)532     protected void dumpFilter(PrintWriter out, String prefix, F filter) {
533         out.print(prefix); out.println(filter);
534     }
535 
filterToLabel(F filter)536     protected Object filterToLabel(F filter) {
537         return "IntentFilter";
538     }
539 
dumpFilterLabel(PrintWriter out, String prefix, Object label, int count)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 
addFilter(ArrayMap<String, F[]> map, String name, F filter)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 
register_mime_types(F filter, String prefix)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 
unregister_mime_types(F filter, String prefix)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 
register_intent_filter(F filter, Iterator<String> i, ArrayMap<String, F[]> dest, String prefix)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 
unregister_intent_filter(F filter, Iterator<String> i, ArrayMap<String, F[]> dest, String prefix)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 
remove_all_objects(ArrayMap<String, F[]> map, String name, Object object)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 
getFastIntentCategories(Intent intent)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 
buildResolveList(Intent intent, FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly, String resolvedType, String scheme, F[] src, List<R> dest, int userId)696     private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
697             boolean debug, boolean defaultOnly, String resolvedType, String scheme,
698             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