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