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