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