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