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