• 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 android.content;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.net.Uri;
25 import android.os.Build;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.os.PatternMatcher;
29 import android.text.TextUtils;
30 import android.util.AndroidException;
31 import android.util.Log;
32 import android.util.Printer;
33 import android.util.proto.ProtoOutputStream;
34 
35 import com.android.internal.util.XmlUtils;
36 
37 import org.xmlpull.v1.XmlPullParser;
38 import org.xmlpull.v1.XmlPullParserException;
39 import org.xmlpull.v1.XmlSerializer;
40 
41 import java.io.IOException;
42 import java.lang.annotation.Retention;
43 import java.lang.annotation.RetentionPolicy;
44 import java.util.ArrayList;
45 import java.util.Collection;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Objects;
49 import java.util.Set;
50 import java.util.function.BiConsumer;
51 import java.util.function.Predicate;
52 
53 /**
54  * Structured description of Intent values to be matched.  An IntentFilter can
55  * match against actions, categories, and data (either via its type, scheme,
56  * and/or path) in an Intent.  It also includes a "priority" value which is
57  * used to order multiple matching filters.
58  *
59  * <p>IntentFilter objects are often created in XML as part of a package's
60  * {@link android.R.styleable#AndroidManifest AndroidManifest.xml} file,
61  * using {@link android.R.styleable#AndroidManifestIntentFilter intent-filter}
62  * tags.
63  *
64  * <p>There are three Intent characteristics you can filter on: the
65  * <em>action</em>, <em>data</em>, and <em>categories</em>.  For each of these
66  * characteristics you can provide
67  * multiple possible matching values (via {@link #addAction},
68  * {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart},
69  * {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively).
70  * For actions, if no data characteristics are specified, then the filter will
71  * only match intents that contain no data.
72  *
73  * <p>The data characteristic is
74  * itself divided into three attributes: type, scheme, authority, and path.
75  * Any that are
76  * specified must match the contents of the Intent.  If you specify a scheme
77  * but no type, only Intent that does not have a type (such as mailto:) will
78  * match; a content: URI will never match because they always have a MIME type
79  * that is supplied by their content provider.  Specifying a type with no scheme
80  * has somewhat special meaning: it will match either an Intent with no URI
81  * field, or an Intent with a content: or file: URI.  If you specify neither,
82  * then only an Intent with no data or type will match.  To specify an authority,
83  * you must also specify one or more schemes that it is associated with.
84  * To specify a path, you also must specify both one or more authorities and
85  * one or more schemes it is associated with.
86  *
87  * <div class="special reference">
88  * <h3>Developer Guides</h3>
89  * <p>For information about how to create and resolve intents, read the
90  * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
91  * developer guide.</p>
92  * </div>
93  *
94  * <h3>Filter Rules</h3>
95  * <p>A match is based on the following rules.  Note that
96  * for an IntentFilter to match an Intent, three conditions must hold:
97  * the <strong>action</strong> and <strong>category</strong> must match, and
98  * the data (both the <strong>data type</strong> and
99  * <strong>data scheme+authority+path</strong> if specified) must match
100  * (see {@link #match(ContentResolver, Intent, boolean, String)} for more details
101  * on how the data fields match).
102  *
103  * <p><strong>Action</strong> matches if any of the given values match the
104  * Intent action; if the filter specifies no actions, then it will only match
105  * Intents that do not contain an action.
106  *
107  * <p><strong>Data Type</strong> matches if any of the given values match the
108  * Intent type.  The Intent
109  * type is determined by calling {@link Intent#resolveType}.  A wildcard can be
110  * used for the MIME sub-type, in both the Intent and IntentFilter, so that the
111  * type "audio/*" will match "audio/mpeg", "audio/aiff", "audio/*", etc.
112  * <em>Note that MIME type matching here is <b>case sensitive</b>, unlike
113  * formal RFC MIME types!</em>  You should thus always use lower case letters
114  * for your MIME types.
115  *
116  * <p><strong>Data Scheme</strong> matches if any of the given values match the
117  * Intent data's scheme.
118  * The Intent scheme is determined by calling {@link Intent#getData}
119  * and {@link android.net.Uri#getScheme} on that URI.
120  * <em>Note that scheme matching here is <b>case sensitive</b>, unlike
121  * formal RFC schemes!</em>  You should thus always use lower case letters
122  * for your schemes.
123  *
124  * <p><strong>Data Scheme Specific Part</strong> matches if any of the given values match
125  * the Intent's data scheme specific part <em>and</em> one of the data schemes in the filter
126  * has matched the Intent, <em>or</em> no scheme specific parts were supplied in the filter.
127  * The Intent scheme specific part is determined by calling
128  * {@link Intent#getData} and {@link android.net.Uri#getSchemeSpecificPart} on that URI.
129  * <em>Note that scheme specific part matching is <b>case sensitive</b>.</em>
130  *
131  * <p><strong>Data Authority</strong> matches if any of the given values match
132  * the Intent's data authority <em>and</em> one of the data schemes in the filter
133  * has matched the Intent, <em>or</em> no authorities were supplied in the filter.
134  * The Intent authority is determined by calling
135  * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
136  * <em>Note that authority matching here is <b>case sensitive</b>, unlike
137  * formal RFC host names!</em>  You should thus always use lower case letters
138  * for your authority.
139  *
140  * <p><strong>Data Path</strong> matches if any of the given values match the
141  * Intent's data path <em>and</em> both a scheme and authority in the filter
142  * has matched against the Intent, <em>or</em> no paths were supplied in the
143  * filter.  The Intent authority is determined by calling
144  * {@link Intent#getData} and {@link android.net.Uri#getPath} on that URI.
145  *
146  * <p><strong>Categories</strong> match if <em>all</em> of the categories in
147  * the Intent match categories given in the filter.  Extra categories in the
148  * filter that are not in the Intent will not cause the match to fail.  Note
149  * that unlike the action, an IntentFilter with no categories
150  * will only match an Intent that does not have any categories.
151  */
152 public class IntentFilter implements Parcelable {
153     private static final String TAG = "IntentFilter";
154 
155     private static final String AGLOB_STR = "aglob";
156     private static final String SGLOB_STR = "sglob";
157     private static final String PREFIX_STR = "prefix";
158     private static final String SUFFIX_STR = "suffix";
159     private static final String LITERAL_STR = "literal";
160     private static final String PATH_STR = "path";
161     private static final String PORT_STR = "port";
162     private static final String HOST_STR = "host";
163     private static final String AUTH_STR = "auth";
164     private static final String SSP_STR = "ssp";
165     private static final String SCHEME_STR = "scheme";
166     private static final String STATIC_TYPE_STR = "staticType";
167     private static final String TYPE_STR = "type";
168     private static final String GROUP_STR = "group";
169     private static final String CAT_STR = "cat";
170     private static final String NAME_STR = "name";
171     private static final String ACTION_STR = "action";
172     private static final String AUTO_VERIFY_STR = "autoVerify";
173 
174     /**
175      * The filter {@link #setPriority} value at which system high-priority
176      * receivers are placed; that is, receivers that should execute before
177      * application code. Applications should never use filters with this or
178      * higher priorities.
179      *
180      * @see #setPriority
181      */
182     public static final int SYSTEM_HIGH_PRIORITY = 1000;
183 
184     /**
185      * The filter {@link #setPriority} value at which system low-priority
186      * receivers are placed; that is, receivers that should execute after
187      * application code. Applications should never use filters with this or
188      * lower priorities.
189      *
190      * @see #setPriority
191      */
192     public static final int SYSTEM_LOW_PRIORITY = -1000;
193 
194     /**
195      * The part of a match constant that describes the category of match
196      * that occurred.  May be either {@link #MATCH_CATEGORY_EMPTY},
197      * {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_SCHEME_SPECIFIC_PART},
198      * {@link #MATCH_CATEGORY_HOST}, {@link #MATCH_CATEGORY_PORT},
199      * {@link #MATCH_CATEGORY_PATH}, or {@link #MATCH_CATEGORY_TYPE}.  Higher
200      * values indicate a better match.
201      */
202     public static final int MATCH_CATEGORY_MASK = 0xfff0000;
203 
204     /**
205      * The part of a match constant that applies a quality adjustment to the
206      * basic category of match.  The value {@link #MATCH_ADJUSTMENT_NORMAL}
207      * is no adjustment; higher numbers than that improve the quality, while
208      * lower numbers reduce it.
209      */
210     public static final int MATCH_ADJUSTMENT_MASK = 0x000ffff;
211 
212     /**
213      * Quality adjustment applied to the category of match that signifies
214      * the default, base value; higher numbers improve the quality while
215      * lower numbers reduce it.
216      */
217     public static final int MATCH_ADJUSTMENT_NORMAL = 0x8000;
218 
219     /**
220      * The filter matched an intent that had no data specified.
221      */
222     public static final int MATCH_CATEGORY_EMPTY = 0x0100000;
223     /**
224      * The filter matched an intent with the same data URI scheme.
225      */
226     public static final int MATCH_CATEGORY_SCHEME = 0x0200000;
227     /**
228      * The filter matched an intent with the same data URI scheme and
229      * authority host.
230      */
231     public static final int MATCH_CATEGORY_HOST = 0x0300000;
232     /**
233      * The filter matched an intent with the same data URI scheme and
234      * authority host and port.
235      */
236     public static final int MATCH_CATEGORY_PORT = 0x0400000;
237     /**
238      * The filter matched an intent with the same data URI scheme,
239      * authority, and path.
240      */
241     public static final int MATCH_CATEGORY_PATH = 0x0500000;
242     /**
243      * The filter matched an intent with the same data URI scheme and
244      * scheme specific part.
245      */
246     public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0x0580000;
247     /**
248      * The filter matched an intent with the same data MIME type.
249      */
250     public static final int MATCH_CATEGORY_TYPE = 0x0600000;
251 
252     /**
253      * The filter didn't match due to different MIME types.
254      */
255     public static final int NO_MATCH_TYPE = -1;
256     /**
257      * The filter didn't match due to different data URIs.
258      */
259     public static final int NO_MATCH_DATA = -2;
260     /**
261      * The filter didn't match due to different actions.
262      */
263     public static final int NO_MATCH_ACTION = -3;
264     /**
265      * The filter didn't match because it required one or more categories
266      * that were not in the Intent.
267      */
268     public static final int NO_MATCH_CATEGORY = -4;
269 
270     /**
271      * HTTP scheme.
272      *
273      * @see #addDataScheme(String)
274      * @hide
275      */
276     public static final String SCHEME_HTTP = "http";
277     /**
278      * HTTPS scheme.
279      *
280      * @see #addDataScheme(String)
281      * @hide
282      */
283     public static final String SCHEME_HTTPS = "https";
284 
285     /**
286      * Package scheme
287      *
288      * @see #addDataScheme(String)
289      * @hide
290      */
291     public static final String SCHEME_PACKAGE = "package";
292 
293     /**
294      * The value to indicate a wildcard for incoming match arguments.
295      * @hide
296      */
297     public static final String WILDCARD = "*";
298     /** @hide */
299     public static final String WILDCARD_PATH = "/" + WILDCARD;
300 
301     private int mPriority;
302     @UnsupportedAppUsage
303     private int mOrder;
304     @UnsupportedAppUsage
305     private final ArrayList<String> mActions;
306     private ArrayList<String> mCategories = null;
307     private ArrayList<String> mDataSchemes = null;
308     private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
309     private ArrayList<AuthorityEntry> mDataAuthorities = null;
310     private ArrayList<PatternMatcher> mDataPaths = null;
311     private ArrayList<String> mStaticDataTypes = null;
312     private ArrayList<String> mDataTypes = null;
313     private ArrayList<String> mMimeGroups = null;
314     private boolean mHasStaticPartialTypes = false;
315     private boolean mHasDynamicPartialTypes = false;
316 
317     private static final int STATE_VERIFY_AUTO         = 0x00000001;
318     private static final int STATE_NEED_VERIFY         = 0x00000010;
319     private static final int STATE_NEED_VERIFY_CHECKED = 0x00000100;
320     private static final int STATE_VERIFIED            = 0x00001000;
321 
322     private int mVerifyState;
323     /** @hide */
324     public static final int VISIBILITY_NONE = 0;
325     /** @hide */
326     public static final int VISIBILITY_EXPLICIT = 1;
327     /** @hide */
328     public static final int VISIBILITY_IMPLICIT = 2;
329     /** @hide */
330     @IntDef(prefix = { "VISIBILITY_" }, value = {
331             VISIBILITY_NONE,
332             VISIBILITY_EXPLICIT,
333             VISIBILITY_IMPLICIT,
334     })
335     @Retention(RetentionPolicy.SOURCE)
336     public @interface InstantAppVisibility {}
337     /** Whether or not the intent filter is visible to instant apps. */
338     private @InstantAppVisibility int mInstantAppVisibility;
339     // These functions are the start of more optimized code for managing
340     // the string sets...  not yet implemented.
341 
findStringInSet(String[] set, String string, int[] lengths, int lenPos)342     private static int findStringInSet(String[] set, String string,
343             int[] lengths, int lenPos) {
344         if (set == null) return -1;
345         final int N = lengths[lenPos];
346         for (int i=0; i<N; i++) {
347             if (set[i].equals(string)) return i;
348         }
349         return -1;
350     }
351 
addStringToSet(String[] set, String string, int[] lengths, int lenPos)352     private static String[] addStringToSet(String[] set, String string,
353             int[] lengths, int lenPos) {
354         if (findStringInSet(set, string, lengths, lenPos) >= 0) return set;
355         if (set == null) {
356             set = new String[2];
357             set[0] = string;
358             lengths[lenPos] = 1;
359             return set;
360         }
361         final int N = lengths[lenPos];
362         if (N < set.length) {
363             set[N] = string;
364             lengths[lenPos] = N+1;
365             return set;
366         }
367 
368         String[] newSet = new String[(N*3)/2 + 2];
369         System.arraycopy(set, 0, newSet, 0, N);
370         set = newSet;
371         set[N] = string;
372         lengths[lenPos] = N+1;
373         return set;
374     }
375 
removeStringFromSet(String[] set, String string, int[] lengths, int lenPos)376     private static String[] removeStringFromSet(String[] set, String string,
377             int[] lengths, int lenPos) {
378         int pos = findStringInSet(set, string, lengths, lenPos);
379         if (pos < 0) return set;
380         final int N = lengths[lenPos];
381         if (N > (set.length/4)) {
382             int copyLen = N-(pos+1);
383             if (copyLen > 0) {
384                 System.arraycopy(set, pos+1, set, pos, copyLen);
385             }
386             set[N-1] = null;
387             lengths[lenPos] = N-1;
388             return set;
389         }
390 
391         String[] newSet = new String[set.length/3];
392         if (pos > 0) System.arraycopy(set, 0, newSet, 0, pos);
393         if ((pos+1) < N) System.arraycopy(set, pos+1, newSet, pos, N-(pos+1));
394         return newSet;
395     }
396 
397     /**
398      * This exception is thrown when a given MIME type does not have a valid
399      * syntax.
400      */
401     public static class MalformedMimeTypeException extends AndroidException {
MalformedMimeTypeException()402         public MalformedMimeTypeException() {
403         }
404 
MalformedMimeTypeException(String name)405         public MalformedMimeTypeException(String name) {
406             super(name);
407         }
408     }
409 
410     /**
411      * Create a new IntentFilter instance with a specified action and MIME
412      * type, where you know the MIME type is correctly formatted.  This catches
413      * the {@link MalformedMimeTypeException} exception that the constructor
414      * can call and turns it into a runtime exception.
415      *
416      * @param action The action to match, such as Intent.ACTION_VIEW.
417      * @param dataType The type to match, such as "vnd.android.cursor.dir/person".
418      *
419      * @return A new IntentFilter for the given action and type.
420      *
421      * @see #IntentFilter(String, String)
422      */
create(String action, String dataType)423     public static IntentFilter create(String action, String dataType) {
424         try {
425             return new IntentFilter(action, dataType);
426         } catch (MalformedMimeTypeException e) {
427             throw new RuntimeException("Bad MIME type", e);
428         }
429     }
430 
431     /**
432      * New empty IntentFilter.
433      */
IntentFilter()434     public IntentFilter() {
435         mPriority = 0;
436         mActions = new ArrayList<String>();
437     }
438 
439     /**
440      * New IntentFilter that matches a single action with no data.  If
441      * no data characteristics are subsequently specified, then the
442      * filter will only match intents that contain no data.
443      *
444      * @param action The action to match, such as Intent.ACTION_MAIN.
445      */
IntentFilter(String action)446     public IntentFilter(String action) {
447         mPriority = 0;
448         mActions = new ArrayList<String>();
449         addAction(action);
450     }
451 
452     /**
453      * New IntentFilter that matches a single action and data type.
454      *
455      * <p><em>Note: MIME type matching in the Android framework is
456      * case-sensitive, unlike formal RFC MIME types.  As a result,
457      * you should always write your MIME types with lower case letters,
458      * and any MIME types you receive from outside of Android should be
459      * converted to lower case before supplying them here.</em></p>
460      *
461      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
462      * not syntactically correct.
463      *
464      * @param action The action to match, such as Intent.ACTION_VIEW.
465      * @param dataType The type to match, such as "vnd.android.cursor.dir/person".
466      *
467      */
IntentFilter(String action, String dataType)468     public IntentFilter(String action, String dataType)
469         throws MalformedMimeTypeException {
470         mPriority = 0;
471         mActions = new ArrayList<String>();
472         addAction(action);
473         addDataType(dataType);
474     }
475 
476     /**
477      * New IntentFilter containing a copy of an existing filter.
478      *
479      * @param o The original filter to copy.
480      */
IntentFilter(IntentFilter o)481     public IntentFilter(IntentFilter o) {
482         mPriority = o.mPriority;
483         mOrder = o.mOrder;
484         mActions = new ArrayList<String>(o.mActions);
485         if (o.mCategories != null) {
486             mCategories = new ArrayList<String>(o.mCategories);
487         }
488         if (o.mStaticDataTypes != null) {
489             mStaticDataTypes = new ArrayList<String>(o.mStaticDataTypes);
490         }
491         if (o.mDataTypes != null) {
492             mDataTypes = new ArrayList<String>(o.mDataTypes);
493         }
494         if (o.mDataSchemes != null) {
495             mDataSchemes = new ArrayList<String>(o.mDataSchemes);
496         }
497         if (o.mDataSchemeSpecificParts != null) {
498             mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(o.mDataSchemeSpecificParts);
499         }
500         if (o.mDataAuthorities != null) {
501             mDataAuthorities = new ArrayList<AuthorityEntry>(o.mDataAuthorities);
502         }
503         if (o.mDataPaths != null) {
504             mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
505         }
506         if (o.mMimeGroups != null) {
507             mMimeGroups = new ArrayList<String>(o.mMimeGroups);
508         }
509         mHasStaticPartialTypes = o.mHasStaticPartialTypes;
510         mHasDynamicPartialTypes = o.mHasDynamicPartialTypes;
511         mVerifyState = o.mVerifyState;
512         mInstantAppVisibility = o.mInstantAppVisibility;
513     }
514 
515     /**
516      * Modify priority of this filter.  This only affects receiver filters.
517      * The priority of activity filters are set in XML and cannot be changed
518      * programmatically. The default priority is 0. Positive values will be
519      * before the default, lower values will be after it. Applications should
520      * use a value that is larger than {@link #SYSTEM_LOW_PRIORITY} and
521      * smaller than {@link #SYSTEM_HIGH_PRIORITY} .
522      *
523      * @param priority The new priority value.
524      *
525      * @see #getPriority
526      * @see #SYSTEM_LOW_PRIORITY
527      * @see #SYSTEM_HIGH_PRIORITY
528      */
setPriority(int priority)529     public final void setPriority(int priority) {
530         mPriority = priority;
531     }
532 
533     /**
534      * Return the priority of this filter.
535      *
536      * @return The priority of the filter.
537      *
538      * @see #setPriority
539      */
getPriority()540     public final int getPriority() {
541         return mPriority;
542     }
543 
544     /** @hide */
545     @SystemApi
setOrder(int order)546     public final void setOrder(int order) {
547         mOrder = order;
548     }
549 
550     /** @hide */
551     @SystemApi
getOrder()552     public final int getOrder() {
553         return mOrder;
554     }
555 
556     /**
557      * Set whether this filter will needs to be automatically verified against its data URIs or not.
558      * The default is false.
559      *
560      * The verification would need to happen only and only if the Intent action is
561      * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
562      * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
563      * is "http" or "https".
564      *
565      * True means that the filter will need to use its data URIs to be verified.
566      *
567      * @param autoVerify The new autoVerify value.
568      *
569      * @see #getAutoVerify()
570      * @see #addAction(String)
571      * @see #getAction(int)
572      * @see #addCategory(String)
573      * @see #getCategory(int)
574      * @see #addDataScheme(String)
575      * @see #getDataScheme(int)
576      *
577      * @hide
578      */
579     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setAutoVerify(boolean autoVerify)580     public final void setAutoVerify(boolean autoVerify) {
581         mVerifyState &= ~STATE_VERIFY_AUTO;
582         if (autoVerify) mVerifyState |= STATE_VERIFY_AUTO;
583     }
584 
585     /**
586      * Return if this filter will needs to be automatically verified again its data URIs or not.
587      *
588      * @return True if the filter will needs to be automatically verified. False otherwise.
589      *
590      * @see #setAutoVerify(boolean)
591      *
592      * @hide
593      */
getAutoVerify()594     public final boolean getAutoVerify() {
595         return ((mVerifyState & STATE_VERIFY_AUTO) == STATE_VERIFY_AUTO);
596     }
597 
598     /**
599      * Return if this filter handle all HTTP or HTTPS data URI or not.  This is the
600      * core check for whether a given activity qualifies as a "browser".
601      *
602      * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise.
603      *
604      * This will check if:
605      *
606      * - either the Intent category is {@link android.content.Intent#CATEGORY_APP_BROWSER}
607      * - either the Intent action is {@link android.content.Intent#ACTION_VIEW} and
608      * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
609      * data scheme is "http" or "https" and that there is no specific host defined.
610      *
611      * @hide
612      */
handleAllWebDataURI()613     public final boolean handleAllWebDataURI() {
614         return hasCategory(Intent.CATEGORY_APP_BROWSER) ||
615                 (handlesWebUris(false) && countDataAuthorities() == 0);
616     }
617 
618     /**
619      * Return if this filter handles HTTP or HTTPS data URIs.
620      *
621      * @return True if the filter handles ACTION_VIEW/CATEGORY_BROWSABLE,
622      * has at least one HTTP or HTTPS data URI pattern defined, and optionally
623      * does not define any non-http/https data URI patterns.
624      *
625      * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
626      * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
627      * data scheme is "http" or "https".
628      *
629      * @param onlyWebSchemes When true, requires that the intent filter declare
630      *     that it handles *only* http: or https: schemes.  This is a requirement for
631      *     the intent filter's domain linkage being verifiable.
632      * @hide
633      */
handlesWebUris(boolean onlyWebSchemes)634     public final boolean handlesWebUris(boolean onlyWebSchemes) {
635         // Require ACTION_VIEW, CATEGORY_BROWSEABLE, and at least one scheme
636         if (!hasAction(Intent.ACTION_VIEW)
637             || !hasCategory(Intent.CATEGORY_BROWSABLE)
638             || mDataSchemes == null
639             || mDataSchemes.size() == 0) {
640             return false;
641         }
642 
643         // Now allow only the schemes "http" and "https"
644         final int N = mDataSchemes.size();
645         for (int i = 0; i < N; i++) {
646             final String scheme = mDataSchemes.get(i);
647             final boolean isWebScheme =
648                     SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme);
649             if (onlyWebSchemes) {
650                 // If we're specifically trying to ensure that there are no non-web schemes
651                 // declared in this filter, then if we ever see a non-http/https scheme then
652                 // we know it's a failure.
653                 if (!isWebScheme) {
654                     return false;
655                 }
656             } else {
657                 // If we see any http/https scheme declaration in this case then the
658                 // filter matches what we're looking for.
659                 if (isWebScheme) {
660                     return true;
661                 }
662             }
663         }
664 
665         // We get here if:
666         //   1) onlyWebSchemes and no non-web schemes were found, i.e success; or
667         //   2) !onlyWebSchemes and no http/https schemes were found, i.e. failure.
668         return onlyWebSchemes;
669     }
670 
671     /**
672      * Return if this filter needs to be automatically verified again its data URIs or not.
673      *
674      * @return True if the filter needs to be automatically verified. False otherwise.
675      *
676      * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
677      * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
678      * data scheme is "http" or "https".
679      *
680      * @see #setAutoVerify(boolean)
681      *
682      * @hide
683      */
needsVerification()684     public final boolean needsVerification() {
685         return getAutoVerify() && handlesWebUris(true);
686     }
687 
688     /**
689      * Return if this filter has been verified
690      *
691      * @return true if the filter has been verified or if autoVerify is false.
692      *
693      * @hide
694      */
695     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
isVerified()696     public final boolean isVerified() {
697         if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) {
698             return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY);
699         }
700         return false;
701     }
702 
703     /**
704      * Set if this filter has been verified
705      *
706      * @param verified true if this filter has been verified. False otherwise.
707      *
708      * @hide
709      */
setVerified(boolean verified)710     public void setVerified(boolean verified) {
711         mVerifyState |= STATE_NEED_VERIFY_CHECKED;
712         mVerifyState &= ~STATE_VERIFIED;
713         if (verified) mVerifyState |= STATE_VERIFIED;
714     }
715 
716     /** @hide */
setVisibilityToInstantApp(@nstantAppVisibility int visibility)717     public void setVisibilityToInstantApp(@InstantAppVisibility int visibility) {
718         mInstantAppVisibility = visibility;
719     }
720     /** @hide */
getVisibilityToInstantApp()721     public @InstantAppVisibility int getVisibilityToInstantApp() {
722         return mInstantAppVisibility;
723     }
724     /** @hide */
isVisibleToInstantApp()725     public boolean isVisibleToInstantApp() {
726         return mInstantAppVisibility != VISIBILITY_NONE;
727     }
728     /** @hide */
isExplicitlyVisibleToInstantApp()729     public boolean isExplicitlyVisibleToInstantApp() {
730         return mInstantAppVisibility == VISIBILITY_EXPLICIT;
731     }
732     /** @hide */
isImplicitlyVisibleToInstantApp()733     public boolean isImplicitlyVisibleToInstantApp() {
734         return mInstantAppVisibility == VISIBILITY_IMPLICIT;
735     }
736 
737     /**
738      * Add a new Intent action to match against.  If any actions are included
739      * in the filter, then an Intent's action must be one of those values for
740      * it to match.  If no actions are included, the Intent action is ignored.
741      *
742      * @param action Name of the action to match, such as Intent.ACTION_VIEW.
743      */
addAction(String action)744     public final void addAction(String action) {
745         if (!mActions.contains(action)) {
746             mActions.add(action.intern());
747         }
748     }
749 
750     /**
751      * Return the number of actions in the filter.
752      */
countActions()753     public final int countActions() {
754         return mActions.size();
755     }
756 
757     /**
758      * Return an action in the filter.
759      */
getAction(int index)760     public final String getAction(int index) {
761         return mActions.get(index);
762     }
763 
764     /**
765      * Is the given action included in the filter?  Note that if the filter
766      * does not include any actions, false will <em>always</em> be returned.
767      *
768      * @param action The action to look for.
769      *
770      * @return True if the action is explicitly mentioned in the filter.
771      */
hasAction(String action)772     public final boolean hasAction(String action) {
773         return action != null && mActions.contains(action);
774     }
775 
776     /**
777      * Match this filter against an Intent's action.  If the filter does not
778      * specify any actions, the match will always fail.
779      *
780      * @param action The desired action to look for.
781      *
782      * @return True if the action is listed in the filter.
783      */
matchAction(String action)784     public final boolean matchAction(String action) {
785         return matchAction(action, false /*wildcardSupported*/, null /*ignoreActions*/);
786     }
787 
788     /**
789      * Variant of {@link #matchAction(String)} that allows a wildcard for the provided action.
790      * @param wildcardSupported if true, will allow action to use wildcards
791      * @param ignoreActions if not null, the set of actions to should not be considered valid while
792      *                      calculating the match
793      */
matchAction(String action, boolean wildcardSupported, @Nullable Collection<String> ignoreActions)794     private boolean matchAction(String action, boolean wildcardSupported,
795             @Nullable Collection<String> ignoreActions) {
796         if (wildcardSupported && WILDCARD.equals(action)) {
797             if (ignoreActions == null) {
798                 return !mActions.isEmpty();
799             }
800             for (int i = mActions.size() - 1; i >= 0; i--) {
801                 if (!ignoreActions.contains(mActions.get(i))) {
802                     return true;
803                 }
804             }
805             return false;
806         }
807         if (ignoreActions != null && ignoreActions.contains(action)) {
808             return false;
809         }
810         return hasAction(action);
811     }
812 
813     /**
814      * Return an iterator over the filter's actions.  If there are no actions,
815      * returns null.
816      */
actionsIterator()817     public final Iterator<String> actionsIterator() {
818         return mActions != null ? mActions.iterator() : null;
819     }
820 
821     /**
822      * Add a new Intent data type to match against.  If any types are
823      * included in the filter, then an Intent's data must be <em>either</em>
824      * one of these types <em>or</em> a matching scheme.  If no data types
825      * are included, then an Intent will only match if it specifies no data.
826      *
827      * <p><em>Note: MIME type matching in the Android framework is
828      * case-sensitive, unlike formal RFC MIME types.  As a result,
829      * you should always write your MIME types with lower case letters,
830      * and any MIME types you receive from outside of Android should be
831      * converted to lower case before supplying them here.</em></p>
832      *
833      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
834      * not syntactically correct.
835      *
836      * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person".
837      *
838      * @see #matchData
839      */
addDataType(String type)840     public final void addDataType(String type)
841         throws MalformedMimeTypeException {
842         processMimeType(type, (internalType, isPartial) -> {
843             if (mDataTypes == null) {
844                 mDataTypes = new ArrayList<>();
845             }
846             if (mStaticDataTypes == null) {
847                 mStaticDataTypes = new ArrayList<>();
848             }
849 
850             if (mDataTypes.contains(internalType)) {
851                 return;
852             }
853 
854             mDataTypes.add(internalType.intern());
855             mStaticDataTypes.add(internalType.intern());
856             mHasStaticPartialTypes = mHasStaticPartialTypes || isPartial;
857         });
858     }
859 
860     /**
861      * Add a new Intent data type <em>from MIME group</em> to match against.  If any types are
862      * included in the filter, then an Intent's data must be <em>either</em>
863      * one of these types <em>or</em> a matching scheme.  If no data types
864      * are included, then an Intent will only match if it specifies no data.
865      *
866      * <p><em>Note: MIME type matching in the Android framework is
867      * case-sensitive, unlike formal RFC MIME types.  As a result,
868      * you should always write your MIME types with lower case letters,
869      * and any MIME types you receive from outside of Android should be
870      * converted to lower case before supplying them here.</em></p>
871      *
872      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
873      * not syntactically correct.
874      *
875      * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person".
876      *
877      * @see #clearDynamicDataTypes()
878      * @hide
879      */
addDynamicDataType(String type)880     public final void addDynamicDataType(String type)
881             throws MalformedMimeTypeException {
882         processMimeType(type, (internalType, isPartial) -> {
883             if (mDataTypes == null) {
884                 mDataTypes = new ArrayList<>();
885             }
886 
887             if (!mDataTypes.contains(internalType)) {
888                 mDataTypes.add(internalType.intern());
889 
890                 mHasDynamicPartialTypes = mHasDynamicPartialTypes || isPartial;
891             }
892         });
893     }
894 
895     /**
896      * Process mime type - convert to representation used internally and check if type is partial,
897      * and then call provided action
898      */
processMimeType(String type, BiConsumer<String, Boolean> action)899     private void processMimeType(String type, BiConsumer<String, Boolean> action)
900             throws MalformedMimeTypeException {
901         final int slashpos = type.indexOf('/');
902         final int typelen = type.length();
903         if (slashpos <= 0 || typelen < slashpos + 2) {
904             throw new MalformedMimeTypeException(type);
905         }
906 
907         String internalType = type;
908         boolean isPartialType = false;
909         if (typelen == slashpos + 2 && type.charAt(slashpos + 1) == '*') {
910             internalType = type.substring(0, slashpos);
911             isPartialType = true;
912         }
913 
914         action.accept(internalType, isPartialType);
915     }
916 
917     /**
918      * Remove all previously added Intent data types from IntentFilter.
919      *
920      * @see #addDynamicDataType(String)
921      * @hide
922      */
clearDynamicDataTypes()923     public final void clearDynamicDataTypes() {
924         if (mDataTypes == null) {
925             return;
926         }
927 
928         if (mStaticDataTypes != null) {
929             mDataTypes.clear();
930             mDataTypes.addAll(mStaticDataTypes);
931         } else {
932             mDataTypes = null;
933         }
934 
935         mHasDynamicPartialTypes = false;
936     }
937 
938     /**
939      * Return the number of static data types in the filter.
940      * @hide
941      */
countStaticDataTypes()942     public int countStaticDataTypes() {
943         return mStaticDataTypes != null ? mStaticDataTypes.size() : 0;
944     }
945 
946     /**
947      * Is the given data type included in the filter?  Note that if the filter
948      * does not include any type, false will <em>always</em> be returned.
949      *
950      * @param type The data type to look for.
951      *
952      * @return True if the type is explicitly mentioned in the filter.
953      */
hasDataType(String type)954     public final boolean hasDataType(String type) {
955         return mDataTypes != null && findMimeType(type);
956     }
957 
958     /** @hide */
959     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasExactDataType(String type)960     public final boolean hasExactDataType(String type) {
961         return mDataTypes != null && mDataTypes.contains(type);
962     }
963 
964     /** @hide */
hasExactDynamicDataType(String type)965     public final boolean hasExactDynamicDataType(String type) {
966         return hasExactDataType(type) && !hasExactStaticDataType(type);
967     }
968 
969     /** @hide */
hasExactStaticDataType(String type)970     public final boolean hasExactStaticDataType(String type) {
971         return mStaticDataTypes != null && mStaticDataTypes.contains(type);
972     }
973 
974     /**
975      * Return the number of data types in the filter.
976      */
countDataTypes()977     public final int countDataTypes() {
978         return mDataTypes != null ? mDataTypes.size() : 0;
979     }
980 
981     /**
982      * Return a data type in the filter.
983      */
getDataType(int index)984     public final String getDataType(int index) {
985         return mDataTypes.get(index);
986     }
987 
988     /**
989      * Return an iterator over the filter's data types.
990      */
typesIterator()991     public final Iterator<String> typesIterator() {
992         return mDataTypes != null ? mDataTypes.iterator() : null;
993     }
994 
995     /**
996      * Return copy of filter's data types.
997      * @hide
998      */
dataTypes()999     public final List<String> dataTypes() {
1000         return mDataTypes != null ? new ArrayList<>(mDataTypes) : null;
1001     }
1002 
1003     /** @hide */
addMimeGroup(String name)1004     public final void addMimeGroup(String name) {
1005         if (mMimeGroups == null) {
1006             mMimeGroups = new ArrayList<>();
1007         }
1008         if (!mMimeGroups.contains(name)) {
1009             mMimeGroups.add(name);
1010         }
1011     }
1012 
1013     /** @hide */
hasMimeGroup(String name)1014     public final boolean hasMimeGroup(String name) {
1015         return mMimeGroups != null && mMimeGroups.contains(name);
1016     }
1017 
1018     /** @hide */
getMimeGroup(int index)1019     public final String getMimeGroup(int index) {
1020         return mMimeGroups.get(index);
1021     }
1022 
1023     /** @hide */
countMimeGroups()1024     public final int countMimeGroups() {
1025         return mMimeGroups != null ? mMimeGroups.size() : 0;
1026     }
1027 
1028     /** @hide */
mimeGroupsIterator()1029     public final Iterator<String> mimeGroupsIterator() {
1030         return mMimeGroups != null ? mMimeGroups.iterator() : null;
1031     }
1032 
1033     /**
1034      * Add a new Intent data scheme to match against.  If any schemes are
1035      * included in the filter, then an Intent's data must be <em>either</em>
1036      * one of these schemes <em>or</em> a matching data type.  If no schemes
1037      * are included, then an Intent will match only if it includes no data.
1038      *
1039      * <p><em>Note: scheme matching in the Android framework is
1040      * case-sensitive, unlike formal RFC schemes.  As a result,
1041      * you should always write your schemes with lower case letters,
1042      * and any schemes you receive from outside of Android should be
1043      * converted to lower case before supplying them here.</em></p>
1044      *
1045      * @param scheme Name of the scheme to match, such as "http".
1046      *
1047      * @see #matchData
1048      */
addDataScheme(String scheme)1049     public final void addDataScheme(String scheme) {
1050         if (mDataSchemes == null) mDataSchemes = new ArrayList<String>();
1051         if (!mDataSchemes.contains(scheme)) {
1052             mDataSchemes.add(scheme.intern());
1053         }
1054     }
1055 
1056     /**
1057      * Return the number of data schemes in the filter.
1058      */
countDataSchemes()1059     public final int countDataSchemes() {
1060         return mDataSchemes != null ? mDataSchemes.size() : 0;
1061     }
1062 
1063     /**
1064      * Return a data scheme in the filter.
1065      */
getDataScheme(int index)1066     public final String getDataScheme(int index) {
1067         return mDataSchemes.get(index);
1068     }
1069 
1070     /**
1071      * Is the given data scheme included in the filter?  Note that if the
1072      * filter does not include any scheme, false will <em>always</em> be
1073      * returned.
1074      *
1075      * @param scheme The data scheme to look for.
1076      *
1077      * @return True if the scheme is explicitly mentioned in the filter.
1078      */
hasDataScheme(String scheme)1079     public final boolean hasDataScheme(String scheme) {
1080         return mDataSchemes != null && mDataSchemes.contains(scheme);
1081     }
1082 
1083     /**
1084      * Return an iterator over the filter's data schemes.
1085      */
schemesIterator()1086     public final Iterator<String> schemesIterator() {
1087         return mDataSchemes != null ? mDataSchemes.iterator() : null;
1088     }
1089 
1090     /**
1091      * This is an entry for a single authority in the Iterator returned by
1092      * {@link #authoritiesIterator()}.
1093      */
1094     public final static class AuthorityEntry {
1095         private final String mOrigHost;
1096         private final String mHost;
1097         private final boolean mWild;
1098         private final int mPort;
1099 
AuthorityEntry(String host, String port)1100         public AuthorityEntry(String host, String port) {
1101             mOrigHost = host;
1102             mWild = host.length() > 0 && host.charAt(0) == '*';
1103             mHost = mWild ? host.substring(1).intern() : host;
1104             mPort = port != null ? Integer.parseInt(port) : -1;
1105         }
1106 
AuthorityEntry(Parcel src)1107         AuthorityEntry(Parcel src) {
1108             mOrigHost = src.readString();
1109             mHost = src.readString();
1110             mWild = src.readInt() != 0;
1111             mPort = src.readInt();
1112         }
1113 
writeToParcel(Parcel dest)1114         void writeToParcel(Parcel dest) {
1115             dest.writeString(mOrigHost);
1116             dest.writeString(mHost);
1117             dest.writeInt(mWild ? 1 : 0);
1118             dest.writeInt(mPort);
1119         }
1120 
dumpDebug(ProtoOutputStream proto, long fieldId)1121         void dumpDebug(ProtoOutputStream proto, long fieldId) {
1122             long token = proto.start(fieldId);
1123             // The original host information is already contained in host and wild, no output now.
1124             proto.write(AuthorityEntryProto.HOST, mHost);
1125             proto.write(AuthorityEntryProto.WILD, mWild);
1126             proto.write(AuthorityEntryProto.PORT, mPort);
1127             proto.end(token);
1128         }
1129 
getHost()1130         public String getHost() {
1131             return mOrigHost;
1132         }
1133 
getPort()1134         public int getPort() {
1135             return mPort;
1136         }
1137 
1138         /** @hide */
match(AuthorityEntry other)1139         public boolean match(AuthorityEntry other) {
1140             if (mWild != other.mWild) {
1141                 return false;
1142             }
1143             if (!mHost.equals(other.mHost)) {
1144                 return false;
1145             }
1146             if (mPort != other.mPort) {
1147                 return false;
1148             }
1149             return true;
1150         }
1151 
1152         @Override
equals(@ullable Object obj)1153         public boolean equals(@Nullable Object obj) {
1154             if (obj instanceof AuthorityEntry) {
1155                 final AuthorityEntry other = (AuthorityEntry)obj;
1156                 return match(other);
1157             }
1158             return false;
1159         }
1160 
1161         /**
1162          * Determine whether this AuthorityEntry matches the given data Uri.
1163          * <em>Note that this comparison is case-sensitive, unlike formal
1164          * RFC host names.  You thus should always normalize to lower-case.</em>
1165          *
1166          * @param data The Uri to match.
1167          * @return Returns either {@link IntentFilter#NO_MATCH_DATA},
1168          * {@link IntentFilter#MATCH_CATEGORY_PORT}, or
1169          * {@link IntentFilter#MATCH_CATEGORY_HOST}.
1170          */
match(Uri data)1171         public int match(Uri data) {
1172             return match(data, false);
1173         }
1174 
1175         /**
1176          * Variant of {@link #match(Uri)} that supports wildcards on the scheme, host and
1177          * path of the provided {@link Uri}
1178          *
1179          * @param wildcardSupported if true, will allow parameters to use wildcards
1180          * @hide
1181          */
match(Uri data, boolean wildcardSupported)1182         public int match(Uri data, boolean wildcardSupported) {
1183             String host = data.getHost();
1184             if (host == null) {
1185                 if (wildcardSupported && mWild && mHost.isEmpty()) {
1186                     // special case, if no host is provided, but the Authority is wildcard, match
1187                     return MATCH_CATEGORY_HOST;
1188                 } else {
1189                     return NO_MATCH_DATA;
1190                 }
1191             }
1192             if (false) Log.v("IntentFilter",
1193                     "Match host " + host + ": " + mHost);
1194             if (!wildcardSupported || !WILDCARD.equals(host)) {
1195                 if (mWild) {
1196                     if (host.length() < mHost.length()) {
1197                         return NO_MATCH_DATA;
1198                     }
1199                     host = host.substring(host.length() - mHost.length());
1200                 }
1201                 if (host.compareToIgnoreCase(mHost) != 0) {
1202                     return NO_MATCH_DATA;
1203                 }
1204             }
1205             // if we're dealing with wildcard support, we ignore ports
1206             if (!wildcardSupported && mPort >= 0) {
1207                 if (mPort != data.getPort()) {
1208                     return NO_MATCH_DATA;
1209                 }
1210                 return MATCH_CATEGORY_PORT;
1211             }
1212             return MATCH_CATEGORY_HOST;
1213         }
1214     }
1215 
1216     /**
1217      * Add a new Intent data "scheme specific part" to match against.  The filter must
1218      * include one or more schemes (via {@link #addDataScheme}) for the
1219      * scheme specific part to be considered.  If any scheme specific parts are
1220      * included in the filter, then an Intent's data must match one of
1221      * them.  If no scheme specific parts are included, then only the scheme must match.
1222      *
1223      * <p>The "scheme specific part" that this matches against is the string returned
1224      * by {@link android.net.Uri#getSchemeSpecificPart() Uri.getSchemeSpecificPart}.
1225      * For Uris that contain a path, this kind of matching is not generally of interest,
1226      * since {@link #addDataAuthority(String, String)} and
1227      * {@link #addDataPath(String, int)} can provide a better mechanism for matching
1228      * them.  However, for Uris that do not contain a path, the authority and path
1229      * are empty, so this is the only way to match against the non-scheme part.</p>
1230      *
1231      * @param ssp Either a raw string that must exactly match the scheme specific part
1232      * path, or a simple pattern, depending on <var>type</var>.
1233      * @param type Determines how <var>ssp</var> will be compared to
1234      * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
1235      * {@link PatternMatcher#PATTERN_PREFIX},
1236      * {@link PatternMatcher#PATTERN_SUFFIX}, or
1237      * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
1238      *
1239      * @see #matchData
1240      * @see #addDataScheme
1241      */
addDataSchemeSpecificPart(String ssp, int type)1242     public final void addDataSchemeSpecificPart(String ssp, int type) {
1243         addDataSchemeSpecificPart(new PatternMatcher(ssp, type));
1244     }
1245 
1246     /** @hide */
addDataSchemeSpecificPart(PatternMatcher ssp)1247     public final void addDataSchemeSpecificPart(PatternMatcher ssp) {
1248         if (mDataSchemeSpecificParts == null) {
1249             mDataSchemeSpecificParts = new ArrayList<PatternMatcher>();
1250         }
1251         mDataSchemeSpecificParts.add(ssp);
1252     }
1253 
1254     /**
1255      * Return the number of data scheme specific parts in the filter.
1256      */
countDataSchemeSpecificParts()1257     public final int countDataSchemeSpecificParts() {
1258         return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.size() : 0;
1259     }
1260 
1261     /**
1262      * Return a data scheme specific part in the filter.
1263      */
getDataSchemeSpecificPart(int index)1264     public final PatternMatcher getDataSchemeSpecificPart(int index) {
1265         return mDataSchemeSpecificParts.get(index);
1266     }
1267 
1268     /**
1269      * Is the given data scheme specific part included in the filter?  Note that if the
1270      * filter does not include any scheme specific parts, false will <em>always</em> be
1271      * returned.
1272      *
1273      * @param data The scheme specific part that is being looked for.
1274      *
1275      * @return Returns true if the data string matches a scheme specific part listed in the
1276      *         filter.
1277      */
hasDataSchemeSpecificPart(String data)1278     public final boolean hasDataSchemeSpecificPart(String data) {
1279         return hasDataSchemeSpecificPart(data, false);
1280     }
1281 
1282     /**
1283      * Variant of {@link #hasDataSchemeSpecificPart(String)} that supports wildcards on the provided
1284      * ssp.
1285      * @param supportWildcards if true, will allow parameters to use wildcards
1286      */
hasDataSchemeSpecificPart(String data, boolean supportWildcards)1287     private boolean hasDataSchemeSpecificPart(String data, boolean supportWildcards) {
1288         if (mDataSchemeSpecificParts == null) {
1289             return false;
1290         }
1291         if (supportWildcards && WILDCARD.equals(data) && mDataSchemeSpecificParts.size() > 0) {
1292             return true;
1293         }
1294         final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
1295         for (int i = 0; i < numDataSchemeSpecificParts; i++) {
1296             final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
1297             if (pe.match(data)) {
1298                 return true;
1299             }
1300         }
1301         return false;
1302     }
1303 
1304     /** @hide */
1305     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasDataSchemeSpecificPart(PatternMatcher ssp)1306     public final boolean hasDataSchemeSpecificPart(PatternMatcher ssp) {
1307         if (mDataSchemeSpecificParts == null) {
1308             return false;
1309         }
1310         final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
1311         for (int i = 0; i < numDataSchemeSpecificParts; i++) {
1312             final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
1313             if (pe.getType() == ssp.getType() && pe.getPath().equals(ssp.getPath())) {
1314                 return true;
1315             }
1316         }
1317         return false;
1318     }
1319 
1320     /**
1321      * Return an iterator over the filter's data scheme specific parts.
1322      */
schemeSpecificPartsIterator()1323     public final Iterator<PatternMatcher> schemeSpecificPartsIterator() {
1324         return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.iterator() : null;
1325     }
1326 
1327     /**
1328      * Add a new Intent data authority to match against.  The filter must
1329      * include one or more schemes (via {@link #addDataScheme}) for the
1330      * authority to be considered.  If any authorities are
1331      * included in the filter, then an Intent's data must match one of
1332      * them.  If no authorities are included, then only the scheme must match.
1333      *
1334      * <p><em>Note: host name in the Android framework is
1335      * case-sensitive, unlike formal RFC host names.  As a result,
1336      * you should always write your host names with lower case letters,
1337      * and any host names you receive from outside of Android should be
1338      * converted to lower case before supplying them here.</em></p>
1339      *
1340      * @param host The host part of the authority to match.  May start with a
1341      *             single '*' to wildcard the front of the host name.
1342      * @param port Optional port part of the authority to match.  If null, any
1343      *             port is allowed.
1344      *
1345      * @see #matchData
1346      * @see #addDataScheme
1347      */
addDataAuthority(String host, String port)1348     public final void addDataAuthority(String host, String port) {
1349         if (port != null) port = port.intern();
1350         addDataAuthority(new AuthorityEntry(host.intern(), port));
1351     }
1352 
1353     /** @hide */
addDataAuthority(AuthorityEntry ent)1354     public final void addDataAuthority(AuthorityEntry ent) {
1355         if (mDataAuthorities == null) mDataAuthorities =
1356                 new ArrayList<AuthorityEntry>();
1357         mDataAuthorities.add(ent);
1358     }
1359 
1360     /**
1361      * Return the number of data authorities in the filter.
1362      */
countDataAuthorities()1363     public final int countDataAuthorities() {
1364         return mDataAuthorities != null ? mDataAuthorities.size() : 0;
1365     }
1366 
1367     /**
1368      * Return a data authority in the filter.
1369      */
getDataAuthority(int index)1370     public final AuthorityEntry getDataAuthority(int index) {
1371         return mDataAuthorities.get(index);
1372     }
1373 
1374     /**
1375      * Is the given data authority included in the filter?  Note that if the
1376      * filter does not include any authorities, false will <em>always</em> be
1377      * returned.
1378      *
1379      * @param data The data whose authority is being looked for.
1380      *
1381      * @return Returns true if the data string matches an authority listed in the
1382      *         filter.
1383      */
hasDataAuthority(Uri data)1384     public final boolean hasDataAuthority(Uri data) {
1385         return matchDataAuthority(data) >= 0;
1386     }
1387 
1388     /** @hide */
1389     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasDataAuthority(AuthorityEntry auth)1390     public final boolean hasDataAuthority(AuthorityEntry auth) {
1391         if (mDataAuthorities == null) {
1392             return false;
1393         }
1394         final int numDataAuthorities = mDataAuthorities.size();
1395         for (int i = 0; i < numDataAuthorities; i++) {
1396             if (mDataAuthorities.get(i).match(auth)) {
1397                 return true;
1398             }
1399         }
1400         return false;
1401     }
1402 
1403     /**
1404      * Return an iterator over the filter's data authorities.
1405      */
authoritiesIterator()1406     public final Iterator<AuthorityEntry> authoritiesIterator() {
1407         return mDataAuthorities != null ? mDataAuthorities.iterator() : null;
1408     }
1409 
1410     /**
1411      * Add a new Intent data path to match against.  The filter must
1412      * include one or more schemes (via {@link #addDataScheme}) <em>and</em>
1413      * one or more authorities (via {@link #addDataAuthority}) for the
1414      * path to be considered.  If any paths are
1415      * included in the filter, then an Intent's data must match one of
1416      * them.  If no paths are included, then only the scheme/authority must
1417      * match.
1418      *
1419      * <p>The path given here can either be a literal that must directly
1420      * match or match against a prefix, or it can be a simple globbing pattern.
1421      * If the latter, you can use '*' anywhere in the pattern to match zero
1422      * or more instances of the previous character, '.' as a wildcard to match
1423      * any character, and '\' to escape the next character.
1424      *
1425      * @param path Either a raw string that must exactly match the file
1426      * path, or a simple pattern, depending on <var>type</var>.
1427      * @param type Determines how <var>path</var> will be compared to
1428      * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
1429      * {@link PatternMatcher#PATTERN_PREFIX},
1430      * {@link PatternMatcher#PATTERN_SUFFIX}, or
1431      * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
1432      *
1433      * @see #matchData
1434      * @see #addDataScheme
1435      * @see #addDataAuthority
1436      */
addDataPath(String path, int type)1437     public final void addDataPath(String path, int type) {
1438         addDataPath(new PatternMatcher(path.intern(), type));
1439     }
1440 
1441     /** @hide */
addDataPath(PatternMatcher path)1442     public final void addDataPath(PatternMatcher path) {
1443         if (mDataPaths == null) mDataPaths = new ArrayList<PatternMatcher>();
1444         mDataPaths.add(path);
1445     }
1446 
1447     /**
1448      * Return the number of data paths in the filter.
1449      */
countDataPaths()1450     public final int countDataPaths() {
1451         return mDataPaths != null ? mDataPaths.size() : 0;
1452     }
1453 
1454     /**
1455      * Return a data path in the filter.
1456      */
getDataPath(int index)1457     public final PatternMatcher getDataPath(int index) {
1458         return mDataPaths.get(index);
1459     }
1460 
1461     /**
1462      * Is the given data path included in the filter?  Note that if the
1463      * filter does not include any paths, false will <em>always</em> be
1464      * returned.
1465      *
1466      * @param data The data path to look for.  This is without the scheme
1467      *             prefix.
1468      *
1469      * @return True if the data string matches a path listed in the
1470      *         filter.
1471      */
hasDataPath(String data)1472     public final boolean hasDataPath(String data) {
1473         return hasDataPath(data, false);
1474     }
1475 
1476     /**
1477      * Variant of {@link #hasDataPath(String)} that supports wildcards on the provided scheme, host,
1478      * and path.
1479      * @param wildcardSupported if true, will allow parameters to use wildcards
1480      */
hasDataPath(String data, boolean wildcardSupported)1481     private boolean hasDataPath(String data, boolean wildcardSupported) {
1482         if (mDataPaths == null) {
1483             return false;
1484         }
1485         if (wildcardSupported && WILDCARD_PATH.equals(data)) {
1486             return true;
1487         }
1488         final int numDataPaths = mDataPaths.size();
1489         for (int i = 0; i < numDataPaths; i++) {
1490             final PatternMatcher pe = mDataPaths.get(i);
1491             if (pe.match(data)) {
1492                 return true;
1493             }
1494         }
1495         return false;
1496     }
1497 
1498     /** @hide */
1499     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasDataPath(PatternMatcher path)1500     public final boolean hasDataPath(PatternMatcher path) {
1501         if (mDataPaths == null) {
1502             return false;
1503         }
1504         final int numDataPaths = mDataPaths.size();
1505         for (int i = 0; i < numDataPaths; i++) {
1506             final PatternMatcher pe = mDataPaths.get(i);
1507             if (pe.getType() == path.getType() && pe.getPath().equals(path.getPath())) {
1508                 return true;
1509             }
1510         }
1511         return false;
1512     }
1513 
1514     /**
1515      * Return an iterator over the filter's data paths.
1516      */
pathsIterator()1517     public final Iterator<PatternMatcher> pathsIterator() {
1518         return mDataPaths != null ? mDataPaths.iterator() : null;
1519     }
1520 
1521     /**
1522      * Match this intent filter against the given Intent data.  This ignores
1523      * the data scheme -- unlike {@link #matchData}, the authority will match
1524      * regardless of whether there is a matching scheme.
1525      *
1526      * @param data The data whose authority is being looked for.
1527      *
1528      * @return Returns either {@link #MATCH_CATEGORY_HOST},
1529      * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}.
1530      */
matchDataAuthority(Uri data)1531     public final int matchDataAuthority(Uri data) {
1532         return matchDataAuthority(data, false);
1533     }
1534 
1535     /**
1536      * Variant of {@link #matchDataAuthority(Uri)} that allows wildcard matching of the provided
1537      * authority.
1538      *
1539      * @param wildcardSupported if true, will allow parameters to use wildcards
1540      * @hide
1541      */
matchDataAuthority(Uri data, boolean wildcardSupported)1542     public final int matchDataAuthority(Uri data, boolean wildcardSupported) {
1543         if (data == null || mDataAuthorities == null) {
1544             return NO_MATCH_DATA;
1545         }
1546         final int numDataAuthorities = mDataAuthorities.size();
1547         for (int i = 0; i < numDataAuthorities; i++) {
1548             final AuthorityEntry ae = mDataAuthorities.get(i);
1549             int match = ae.match(data, wildcardSupported);
1550             if (match >= 0) {
1551                 return match;
1552             }
1553         }
1554         return NO_MATCH_DATA;
1555     }
1556 
1557     /**
1558      * Match this filter against an Intent's data (type, scheme and path). If
1559      * the filter does not specify any types and does not specify any
1560      * schemes/paths, the match will only succeed if the intent does not
1561      * also specify a type or data.  If the filter does not specify any schemes,
1562      * it will implicitly match intents with no scheme, or the schemes "content:"
1563      * or "file:" (basically performing a MIME-type only match).  If the filter
1564      * does not specify any MIME types, the Intent also must not specify a MIME
1565      * type.
1566      *
1567      * <p>Be aware that to match against an authority, you must also specify a base
1568      * scheme the authority is in.  To match against a data path, both a scheme
1569      * and authority must be specified.  If the filter does not specify any
1570      * types or schemes that it matches against, it is considered to be empty
1571      * (any authority or data path given is ignored, as if it were empty as
1572      * well).
1573      *
1574      * <p><em>Note: MIME type, Uri scheme, and host name matching in the
1575      * Android framework is case-sensitive, unlike the formal RFC definitions.
1576      * As a result, you should always write these elements with lower case letters,
1577      * and normalize any MIME types or Uris you receive from
1578      * outside of Android to ensure these elements are lower case before
1579      * supplying them here.</em></p>
1580      *
1581      * @param type The desired data type to look for, as returned by
1582      *             Intent.resolveType().
1583      * @param scheme The desired data scheme to look for, as returned by
1584      *               Intent.getScheme().
1585      * @param data The full data string to match against, as supplied in
1586      *             Intent.data.
1587      *
1588      * @return Returns either a valid match constant (a combination of
1589      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
1590      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match
1591      * or {@link #NO_MATCH_DATA} if the scheme/path didn't match.
1592      *
1593      * @see #match
1594      */
matchData(String type, String scheme, Uri data)1595     public final int matchData(String type, String scheme, Uri data) {
1596         return matchData(type, scheme, data, false);
1597     }
1598 
1599     /**
1600      * Variant of {@link #matchData(String, String, Uri)} that allows wildcard matching
1601      * of the provided type, scheme, host and path parameters.
1602      * @param wildcardSupported if true, will allow parameters to use wildcards
1603      */
matchData(String type, String scheme, Uri data, boolean wildcardSupported)1604     private int matchData(String type, String scheme, Uri data, boolean wildcardSupported) {
1605         final boolean wildcardWithMimegroups = wildcardSupported && countMimeGroups() != 0;
1606         final List<String> types = mDataTypes;
1607         final ArrayList<String> schemes = mDataSchemes;
1608 
1609         int match = MATCH_CATEGORY_EMPTY;
1610 
1611         if (!wildcardWithMimegroups && types == null && schemes == null) {
1612             return ((type == null && data == null)
1613                 ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
1614         }
1615 
1616         if (schemes != null) {
1617             if (schemes.contains(scheme != null ? scheme : "")
1618                     || wildcardSupported && WILDCARD.equals(scheme)) {
1619                 match = MATCH_CATEGORY_SCHEME;
1620             } else {
1621                 return NO_MATCH_DATA;
1622             }
1623 
1624             final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
1625             if (schemeSpecificParts != null && data != null) {
1626                 match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart(), wildcardSupported)
1627                         ? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA;
1628             }
1629             if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
1630                 // If there isn't any matching ssp, we need to match an authority.
1631                 final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
1632                 if (authorities != null) {
1633                     int authMatch = matchDataAuthority(data, wildcardSupported);
1634                     if (authMatch >= 0) {
1635                         final ArrayList<PatternMatcher> paths = mDataPaths;
1636                         if (paths == null) {
1637                             match = authMatch;
1638                         } else if (hasDataPath(data.getPath(), wildcardSupported)) {
1639                             match = MATCH_CATEGORY_PATH;
1640                         } else {
1641                             return NO_MATCH_DATA;
1642                         }
1643                     } else {
1644                         return NO_MATCH_DATA;
1645                     }
1646                 }
1647             }
1648             // If neither an ssp nor an authority matched, we're done.
1649             if (match == NO_MATCH_DATA) {
1650                 return NO_MATCH_DATA;
1651             }
1652         } else {
1653             // Special case: match either an Intent with no data URI,
1654             // or with a scheme: URI.  This is to give a convenience for
1655             // the common case where you want to deal with data in a
1656             // content provider, which is done by type, and we don't want
1657             // to force everyone to say they handle content: or file: URIs.
1658             if (scheme != null && !"".equals(scheme)
1659                     && !"content".equals(scheme)
1660                     && !"file".equals(scheme)
1661                     && !(wildcardSupported && WILDCARD.equals(scheme))) {
1662                 return NO_MATCH_DATA;
1663             }
1664         }
1665 
1666         if (wildcardWithMimegroups) {
1667             return MATCH_CATEGORY_TYPE;
1668         } else if (types != null) {
1669             if (findMimeType(type)) {
1670                 match = MATCH_CATEGORY_TYPE;
1671             } else {
1672                 return NO_MATCH_TYPE;
1673             }
1674         } else {
1675             // If no MIME types are specified, then we will only match against
1676             // an Intent that does not have a MIME type.
1677             if (type != null) {
1678                 return NO_MATCH_TYPE;
1679             }
1680         }
1681 
1682         return match + MATCH_ADJUSTMENT_NORMAL;
1683     }
1684 
1685     /**
1686      * Add a new Intent category to match against.  The semantics of
1687      * categories is the opposite of actions -- an Intent includes the
1688      * categories that it requires, all of which must be included in the
1689      * filter in order to match.  In other words, adding a category to the
1690      * filter has no impact on matching unless that category is specified in
1691      * the intent.
1692      *
1693      * @param category Name of category to match, such as Intent.CATEGORY_EMBED.
1694      */
addCategory(String category)1695     public final void addCategory(String category) {
1696         if (mCategories == null) mCategories = new ArrayList<String>();
1697         if (!mCategories.contains(category)) {
1698             mCategories.add(category.intern());
1699         }
1700     }
1701 
1702     /**
1703      * Return the number of categories in the filter.
1704      */
countCategories()1705     public final int countCategories() {
1706         return mCategories != null ? mCategories.size() : 0;
1707     }
1708 
1709     /**
1710      * Return a category in the filter.
1711      */
getCategory(int index)1712     public final String getCategory(int index) {
1713         return mCategories.get(index);
1714     }
1715 
1716     /**
1717      * Is the given category included in the filter?
1718      *
1719      * @param category The category that the filter supports.
1720      *
1721      * @return True if the category is explicitly mentioned in the filter.
1722      */
hasCategory(String category)1723     public final boolean hasCategory(String category) {
1724         return mCategories != null && mCategories.contains(category);
1725     }
1726 
1727     /**
1728      * Return an iterator over the filter's categories.
1729      *
1730      * @return Iterator if this filter has categories or {@code null} if none.
1731      */
categoriesIterator()1732     public final Iterator<String> categoriesIterator() {
1733         return mCategories != null ? mCategories.iterator() : null;
1734     }
1735 
1736     /**
1737      * Match this filter against an Intent's categories.  Each category in
1738      * the Intent must be specified by the filter; if any are not in the
1739      * filter, the match fails.
1740      *
1741      * @param categories The categories included in the intent, as returned by
1742      *                   Intent.getCategories().
1743      *
1744      * @return If all categories match (success), null; else the name of the
1745      *         first category that didn't match.
1746      */
matchCategories(Set<String> categories)1747     public final String matchCategories(Set<String> categories) {
1748         if (categories == null) {
1749             return null;
1750         }
1751 
1752         Iterator<String> it = categories.iterator();
1753 
1754         if (mCategories == null) {
1755             return it.hasNext() ? it.next() : null;
1756         }
1757 
1758         while (it.hasNext()) {
1759             final String category = it.next();
1760             if (!mCategories.contains(category)) {
1761                 return category;
1762             }
1763         }
1764 
1765         return null;
1766     }
1767 
1768     /**
1769      * Return a {@link Predicate} which tests whether this filter matches the
1770      * given <var>intent</var>.
1771      * <p>
1772      * The intent's type will always be tested using a simple
1773      * {@link Intent#getType()} check. To instead perform a detailed type
1774      * resolution before matching, use
1775      * {@link #asPredicateWithTypeResolution(ContentResolver)}.
1776      */
asPredicate()1777     public @NonNull Predicate<Intent> asPredicate() {
1778         return i -> match(null, i, false, TAG) >= 0;
1779     }
1780 
1781     /**
1782      * Return a {@link Predicate} which tests whether this filter matches the
1783      * given <var>intent</var>.
1784      * <p>
1785      * The intent's type will always be resolved by calling
1786      * {@link Intent#resolveType(ContentResolver)} before matching.
1787      *
1788      * @param resolver to be used when calling
1789      *            {@link Intent#resolveType(ContentResolver)} before matching.
1790      */
asPredicateWithTypeResolution( @onNull ContentResolver resolver)1791     public @NonNull Predicate<Intent> asPredicateWithTypeResolution(
1792             @NonNull ContentResolver resolver) {
1793         Objects.requireNonNull(resolver);
1794         return i -> match(resolver, i, true, TAG) >= 0;
1795     }
1796 
1797     /**
1798      * Test whether this filter matches the given <var>intent</var>.
1799      *
1800      * @param intent The Intent to compare against.
1801      * @param resolve If true, the intent's type will be resolved by calling
1802      *                Intent.resolveType(); otherwise a simple match against
1803      *                Intent.type will be performed.
1804      * @param logTag Tag to use in debugging messages.
1805      *
1806      * @return Returns either a valid match constant (a combination of
1807      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
1808      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
1809      * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
1810      * {@link #NO_MATCH_ACTION} if the action didn't match, or
1811      * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
1812      *
1813      * @see #match(String, String, String, android.net.Uri , Set, String)
1814      */
match(ContentResolver resolver, Intent intent, boolean resolve, String logTag)1815     public final int match(ContentResolver resolver, Intent intent,
1816             boolean resolve, String logTag) {
1817         String type = resolve ? intent.resolveType(resolver) : intent.getType();
1818         return match(intent.getAction(), type, intent.getScheme(),
1819                      intent.getData(), intent.getCategories(), logTag);
1820     }
1821 
1822     /**
1823      * Test whether this filter matches the given intent data.  A match is
1824      * only successful if the actions and categories in the Intent match
1825      * against the filter, as described in {@link IntentFilter}; in that case,
1826      * the match result returned will be as per {@link #matchData}.
1827      *
1828      * @param action The intent action to match against (Intent.getAction).
1829      * @param type The intent type to match against (Intent.resolveType()).
1830      * @param scheme The data scheme to match against (Intent.getScheme()).
1831      * @param data The data URI to match against (Intent.getData()).
1832      * @param categories The categories to match against
1833      *                   (Intent.getCategories()).
1834      * @param logTag Tag to use in debugging messages.
1835      *
1836      * @return Returns either a valid match constant (a combination of
1837      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
1838      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
1839      * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
1840      * {@link #NO_MATCH_ACTION} if the action didn't match, or
1841      * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
1842      *
1843      * @see #matchData
1844      * @see Intent#getAction
1845      * @see Intent#resolveType
1846      * @see Intent#getScheme
1847      * @see Intent#getData
1848      * @see Intent#getCategories
1849      */
match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag)1850     public final int match(String action, String type, String scheme,
1851             Uri data, Set<String> categories, String logTag) {
1852         return match(action, type, scheme, data, categories, logTag, false /*supportWildcards*/,
1853                 null /*ignoreActions*/);
1854     }
1855 
1856     /**
1857      * Variant of {@link #match(ContentResolver, Intent, boolean, String)} that supports wildcards
1858      * in the action, type, scheme, host and path.
1859      * @param supportWildcards if true, will allow supported parameters to use wildcards to match
1860      *                         this filter
1861      * @param ignoreActions a collection of actions that, if not null should be ignored and not used
1862      *                      if provided as the matching action or the set of actions defined by this
1863      *                      filter
1864      * @hide
1865      */
match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag, boolean supportWildcards, @Nullable Collection<String> ignoreActions)1866     public final int match(String action, String type, String scheme,
1867             Uri data, Set<String> categories, String logTag, boolean supportWildcards,
1868             @Nullable Collection<String> ignoreActions) {
1869         if (action != null && !matchAction(action, supportWildcards, ignoreActions)) {
1870             if (false) Log.v(
1871                 logTag, "No matching action " + action + " for " + this);
1872             return NO_MATCH_ACTION;
1873         }
1874 
1875         int dataMatch = matchData(type, scheme, data, supportWildcards);
1876         if (dataMatch < 0) {
1877             if (false) {
1878                 if (dataMatch == NO_MATCH_TYPE) {
1879                     Log.v(logTag, "No matching type " + type
1880                           + " for " + this);
1881                 }
1882                 if (dataMatch == NO_MATCH_DATA) {
1883                     Log.v(logTag, "No matching scheme/path " + data
1884                           + " for " + this);
1885                 }
1886             }
1887             return dataMatch;
1888         }
1889 
1890         String categoryMismatch = matchCategories(categories);
1891         if (categoryMismatch != null) {
1892             if (false) {
1893                 Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);
1894             }
1895             return NO_MATCH_CATEGORY;
1896         }
1897 
1898         // It would be nice to treat container activities as more
1899         // important than ones that can be embedded, but this is not the way...
1900         if (false) {
1901             if (categories != null) {
1902                 dataMatch -= mCategories.size() - categories.size();
1903             }
1904         }
1905 
1906         return dataMatch;
1907     }
1908 
1909     /**
1910      * Write the contents of the IntentFilter as an XML stream.
1911      */
writeToXml(XmlSerializer serializer)1912     public void writeToXml(XmlSerializer serializer) throws IOException {
1913 
1914         if (getAutoVerify()) {
1915             serializer.attribute(null, AUTO_VERIFY_STR, Boolean.toString(true));
1916         }
1917 
1918         int N = countActions();
1919         for (int i=0; i<N; i++) {
1920             serializer.startTag(null, ACTION_STR);
1921             serializer.attribute(null, NAME_STR, mActions.get(i));
1922             serializer.endTag(null, ACTION_STR);
1923         }
1924         N = countCategories();
1925         for (int i=0; i<N; i++) {
1926             serializer.startTag(null, CAT_STR);
1927             serializer.attribute(null, NAME_STR, mCategories.get(i));
1928             serializer.endTag(null, CAT_STR);
1929         }
1930         writeDataTypesToXml(serializer);
1931         N = countMimeGroups();
1932         for (int i=0; i<N; i++) {
1933             serializer.startTag(null, GROUP_STR);
1934             serializer.attribute(null, NAME_STR, mMimeGroups.get(i));
1935             serializer.endTag(null, GROUP_STR);
1936         }
1937         N = countDataSchemes();
1938         for (int i=0; i<N; i++) {
1939             serializer.startTag(null, SCHEME_STR);
1940             serializer.attribute(null, NAME_STR, mDataSchemes.get(i));
1941             serializer.endTag(null, SCHEME_STR);
1942         }
1943         N = countDataSchemeSpecificParts();
1944         for (int i=0; i<N; i++) {
1945             serializer.startTag(null, SSP_STR);
1946             PatternMatcher pe = mDataSchemeSpecificParts.get(i);
1947             switch (pe.getType()) {
1948                 case PatternMatcher.PATTERN_LITERAL:
1949                     serializer.attribute(null, LITERAL_STR, pe.getPath());
1950                     break;
1951                 case PatternMatcher.PATTERN_PREFIX:
1952                     serializer.attribute(null, PREFIX_STR, pe.getPath());
1953                     break;
1954                 case PatternMatcher.PATTERN_SIMPLE_GLOB:
1955                     serializer.attribute(null, SGLOB_STR, pe.getPath());
1956                     break;
1957                 case PatternMatcher.PATTERN_ADVANCED_GLOB:
1958                     serializer.attribute(null, AGLOB_STR, pe.getPath());
1959                     break;
1960                 case PatternMatcher.PATTERN_SUFFIX:
1961                     serializer.attribute(null, SUFFIX_STR, pe.getPath());
1962                     break;
1963             }
1964             serializer.endTag(null, SSP_STR);
1965         }
1966         N = countDataAuthorities();
1967         for (int i=0; i<N; i++) {
1968             serializer.startTag(null, AUTH_STR);
1969             AuthorityEntry ae = mDataAuthorities.get(i);
1970             serializer.attribute(null, HOST_STR, ae.getHost());
1971             if (ae.getPort() >= 0) {
1972                 serializer.attribute(null, PORT_STR, Integer.toString(ae.getPort()));
1973             }
1974             serializer.endTag(null, AUTH_STR);
1975         }
1976         N = countDataPaths();
1977         for (int i=0; i<N; i++) {
1978             serializer.startTag(null, PATH_STR);
1979             PatternMatcher pe = mDataPaths.get(i);
1980             switch (pe.getType()) {
1981                 case PatternMatcher.PATTERN_LITERAL:
1982                     serializer.attribute(null, LITERAL_STR, pe.getPath());
1983                     break;
1984                 case PatternMatcher.PATTERN_PREFIX:
1985                     serializer.attribute(null, PREFIX_STR, pe.getPath());
1986                     break;
1987                 case PatternMatcher.PATTERN_SIMPLE_GLOB:
1988                     serializer.attribute(null, SGLOB_STR, pe.getPath());
1989                     break;
1990                 case PatternMatcher.PATTERN_ADVANCED_GLOB:
1991                     serializer.attribute(null, AGLOB_STR, pe.getPath());
1992                     break;
1993                 case PatternMatcher.PATTERN_SUFFIX:
1994                     serializer.attribute(null, SUFFIX_STR, pe.getPath());
1995                     break;
1996             }
1997             serializer.endTag(null, PATH_STR);
1998         }
1999     }
2000 
2001     /**
2002      * Write data types (both static and dynamic) to XML.
2003      * In implementation we rely on two facts:
2004      * - {@link #mStaticDataTypes} is subsequence of {@link #mDataTypes}
2005      * - both {@link #mStaticDataTypes} and {@link #mDataTypes} does not contain duplicates
2006      */
writeDataTypesToXml(XmlSerializer serializer)2007     private void writeDataTypesToXml(XmlSerializer serializer) throws IOException {
2008         if (mStaticDataTypes == null) {
2009             return;
2010         }
2011 
2012         int i = 0;
2013         for (String staticType: mStaticDataTypes) {
2014             while (!mDataTypes.get(i).equals(staticType)) {
2015                 writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
2016                 i++;
2017             }
2018 
2019             writeDataTypeToXml(serializer, staticType, STATIC_TYPE_STR);
2020             i++;
2021         }
2022 
2023         while (i < mDataTypes.size()) {
2024             writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
2025             i++;
2026         }
2027     }
2028 
writeDataTypeToXml(XmlSerializer serializer, String type, String tag)2029     private void writeDataTypeToXml(XmlSerializer serializer, String type, String tag)
2030             throws IOException {
2031         serializer.startTag(null, tag);
2032 
2033         if (type.indexOf('/') < 0) {
2034             type = type + "/*";
2035         }
2036 
2037         serializer.attribute(null, NAME_STR, type);
2038         serializer.endTag(null, tag);
2039     }
2040 
readFromXml(XmlPullParser parser)2041     public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
2042             IOException {
2043         String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR);
2044         setAutoVerify(TextUtils.isEmpty(autoVerify) ? false : Boolean.getBoolean(autoVerify));
2045 
2046         int outerDepth = parser.getDepth();
2047         int type;
2048         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
2049                && (type != XmlPullParser.END_TAG
2050                        || parser.getDepth() > outerDepth)) {
2051             if (type == XmlPullParser.END_TAG
2052                     || type == XmlPullParser.TEXT) {
2053                 continue;
2054             }
2055 
2056             String tagName = parser.getName();
2057             if (tagName.equals(ACTION_STR)) {
2058                 String name = parser.getAttributeValue(null, NAME_STR);
2059                 if (name != null) {
2060                     addAction(name);
2061                 }
2062             } else if (tagName.equals(CAT_STR)) {
2063                 String name = parser.getAttributeValue(null, NAME_STR);
2064                 if (name != null) {
2065                     addCategory(name);
2066                 }
2067             } else if (tagName.equals(STATIC_TYPE_STR)) {
2068                 String name = parser.getAttributeValue(null, NAME_STR);
2069                 if (name != null) {
2070                     try {
2071                         addDataType(name);
2072                     } catch (MalformedMimeTypeException e) {
2073                     }
2074                 }
2075             } else if (tagName.equals(TYPE_STR)) {
2076                 String name = parser.getAttributeValue(null, NAME_STR);
2077                 if (name != null) {
2078                     try {
2079                         addDynamicDataType(name);
2080                     } catch (MalformedMimeTypeException e) {
2081                     }
2082                 }
2083             } else if (tagName.equals(GROUP_STR)) {
2084                 String name = parser.getAttributeValue(null, NAME_STR);
2085                 if (name != null) {
2086                     addMimeGroup(name);
2087                 }
2088             } else if (tagName.equals(SCHEME_STR)) {
2089                 String name = parser.getAttributeValue(null, NAME_STR);
2090                 if (name != null) {
2091                     addDataScheme(name);
2092                 }
2093             } else if (tagName.equals(SSP_STR)) {
2094                 String ssp = parser.getAttributeValue(null, LITERAL_STR);
2095                 if (ssp != null) {
2096                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_LITERAL);
2097                 } else if ((ssp=parser.getAttributeValue(null, PREFIX_STR)) != null) {
2098                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_PREFIX);
2099                 } else if ((ssp=parser.getAttributeValue(null, SGLOB_STR)) != null) {
2100                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB);
2101                 } else if ((ssp=parser.getAttributeValue(null, AGLOB_STR)) != null) {
2102                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_ADVANCED_GLOB);
2103                 } else if ((ssp=parser.getAttributeValue(null, SUFFIX_STR)) != null) {
2104                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SUFFIX);
2105                 }
2106             } else if (tagName.equals(AUTH_STR)) {
2107                 String host = parser.getAttributeValue(null, HOST_STR);
2108                 String port = parser.getAttributeValue(null, PORT_STR);
2109                 if (host != null) {
2110                     addDataAuthority(host, port);
2111                 }
2112             } else if (tagName.equals(PATH_STR)) {
2113                 String path = parser.getAttributeValue(null, LITERAL_STR);
2114                 if (path != null) {
2115                     addDataPath(path, PatternMatcher.PATTERN_LITERAL);
2116                 } else if ((path=parser.getAttributeValue(null, PREFIX_STR)) != null) {
2117                     addDataPath(path, PatternMatcher.PATTERN_PREFIX);
2118                 } else if ((path=parser.getAttributeValue(null, SGLOB_STR)) != null) {
2119                     addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB);
2120                 } else if ((path=parser.getAttributeValue(null, AGLOB_STR)) != null) {
2121                     addDataPath(path, PatternMatcher.PATTERN_ADVANCED_GLOB);
2122                 } else if ((path=parser.getAttributeValue(null, SUFFIX_STR)) != null) {
2123                     addDataPath(path, PatternMatcher.PATTERN_SUFFIX);
2124                 }
2125             } else {
2126                 Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
2127             }
2128             XmlUtils.skipCurrentTag(parser);
2129         }
2130     }
2131 
2132     /** @hide */
dumpDebug(ProtoOutputStream proto, long fieldId)2133     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
2134         long token = proto.start(fieldId);
2135         if (mActions.size() > 0) {
2136             Iterator<String> it = mActions.iterator();
2137             while (it.hasNext()) {
2138                 proto.write(IntentFilterProto.ACTIONS, it.next());
2139             }
2140         }
2141         if (mCategories != null) {
2142             Iterator<String> it = mCategories.iterator();
2143             while (it.hasNext()) {
2144                 proto.write(IntentFilterProto.CATEGORIES, it.next());
2145             }
2146         }
2147         if (mDataSchemes != null) {
2148             Iterator<String> it = mDataSchemes.iterator();
2149             while (it.hasNext()) {
2150                 proto.write(IntentFilterProto.DATA_SCHEMES, it.next());
2151             }
2152         }
2153         if (mDataSchemeSpecificParts != null) {
2154             Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
2155             while (it.hasNext()) {
2156                 it.next().dumpDebug(proto, IntentFilterProto.DATA_SCHEME_SPECS);
2157             }
2158         }
2159         if (mDataAuthorities != null) {
2160             Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
2161             while (it.hasNext()) {
2162                 it.next().dumpDebug(proto, IntentFilterProto.DATA_AUTHORITIES);
2163             }
2164         }
2165         if (mDataPaths != null) {
2166             Iterator<PatternMatcher> it = mDataPaths.iterator();
2167             while (it.hasNext()) {
2168                 it.next().dumpDebug(proto, IntentFilterProto.DATA_PATHS);
2169             }
2170         }
2171         if (mDataTypes != null) {
2172             Iterator<String> it = mDataTypes.iterator();
2173             while (it.hasNext()) {
2174                 proto.write(IntentFilterProto.DATA_TYPES, it.next());
2175             }
2176         }
2177         if (mMimeGroups != null) {
2178             Iterator<String> it = mMimeGroups.iterator();
2179             while (it.hasNext()) {
2180                 proto.write(IntentFilterProto.MIME_GROUPS, it.next());
2181             }
2182         }
2183         if (mPriority != 0 || hasPartialTypes()) {
2184             proto.write(IntentFilterProto.PRIORITY, mPriority);
2185             proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, hasPartialTypes());
2186         }
2187         proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify());
2188         proto.end(token);
2189     }
2190 
dump(Printer du, String prefix)2191     public void dump(Printer du, String prefix) {
2192         StringBuilder sb = new StringBuilder(256);
2193         if (mActions.size() > 0) {
2194             Iterator<String> it = mActions.iterator();
2195             while (it.hasNext()) {
2196                 sb.setLength(0);
2197                 sb.append(prefix); sb.append("Action: \"");
2198                         sb.append(it.next()); sb.append("\"");
2199                 du.println(sb.toString());
2200             }
2201         }
2202         if (mCategories != null) {
2203             Iterator<String> it = mCategories.iterator();
2204             while (it.hasNext()) {
2205                 sb.setLength(0);
2206                 sb.append(prefix); sb.append("Category: \"");
2207                         sb.append(it.next()); sb.append("\"");
2208                 du.println(sb.toString());
2209             }
2210         }
2211         if (mDataSchemes != null) {
2212             Iterator<String> it = mDataSchemes.iterator();
2213             while (it.hasNext()) {
2214                 sb.setLength(0);
2215                 sb.append(prefix); sb.append("Scheme: \"");
2216                         sb.append(it.next()); sb.append("\"");
2217                 du.println(sb.toString());
2218             }
2219         }
2220         if (mDataSchemeSpecificParts != null) {
2221             Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
2222             while (it.hasNext()) {
2223                 PatternMatcher pe = it.next();
2224                 sb.setLength(0);
2225                 sb.append(prefix); sb.append("Ssp: \"");
2226                         sb.append(pe); sb.append("\"");
2227                 du.println(sb.toString());
2228             }
2229         }
2230         if (mDataAuthorities != null) {
2231             Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
2232             while (it.hasNext()) {
2233                 AuthorityEntry ae = it.next();
2234                 sb.setLength(0);
2235                 sb.append(prefix); sb.append("Authority: \"");
2236                         sb.append(ae.mHost); sb.append("\": ");
2237                         sb.append(ae.mPort);
2238                 if (ae.mWild) sb.append(" WILD");
2239                 du.println(sb.toString());
2240             }
2241         }
2242         if (mDataPaths != null) {
2243             Iterator<PatternMatcher> it = mDataPaths.iterator();
2244             while (it.hasNext()) {
2245                 PatternMatcher pe = it.next();
2246                 sb.setLength(0);
2247                 sb.append(prefix); sb.append("Path: \"");
2248                         sb.append(pe); sb.append("\"");
2249                 du.println(sb.toString());
2250             }
2251         }
2252         if (mStaticDataTypes != null) {
2253             Iterator<String> it = mStaticDataTypes.iterator();
2254             while (it.hasNext()) {
2255                 sb.setLength(0);
2256                 sb.append(prefix); sb.append("StaticType: \"");
2257                 sb.append(it.next()); sb.append("\"");
2258                 du.println(sb.toString());
2259             }
2260         }
2261         if (mDataTypes != null) {
2262             Iterator<String> it = mDataTypes.iterator();
2263             while (it.hasNext()) {
2264                 String dataType = it.next();
2265                 if (hasExactStaticDataType(dataType)) {
2266                     continue;
2267                 }
2268 
2269                 sb.setLength(0);
2270                 sb.append(prefix); sb.append("Type: \"");
2271                 sb.append(dataType); sb.append("\"");
2272                 du.println(sb.toString());
2273             }
2274         }
2275         if (mMimeGroups != null) {
2276             Iterator<String> it = mMimeGroups.iterator();
2277             while (it.hasNext()) {
2278                 sb.setLength(0);
2279                 sb.append(prefix); sb.append("MimeGroup: \"");
2280                 sb.append(it.next()); sb.append("\"");
2281                 du.println(sb.toString());
2282             }
2283         }
2284         if (mPriority != 0 || mOrder != 0 || hasPartialTypes()) {
2285             sb.setLength(0);
2286             sb.append(prefix);
2287             sb.append("mPriority="); sb.append(mPriority);
2288             sb.append(", mOrder="); sb.append(mOrder);
2289             sb.append(", mHasStaticPartialTypes="); sb.append(mHasStaticPartialTypes);
2290             sb.append(", mHasDynamicPartialTypes="); sb.append(mHasDynamicPartialTypes);
2291             du.println(sb.toString());
2292         }
2293         if (getAutoVerify()) {
2294             sb.setLength(0);
2295             sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
2296             du.println(sb.toString());
2297         }
2298     }
2299 
2300     public static final @android.annotation.NonNull Parcelable.Creator<IntentFilter> CREATOR
2301             = new Parcelable.Creator<IntentFilter>() {
2302         public IntentFilter createFromParcel(Parcel source) {
2303             return new IntentFilter(source);
2304         }
2305 
2306         public IntentFilter[] newArray(int size) {
2307             return new IntentFilter[size];
2308         }
2309     };
2310 
describeContents()2311     public final int describeContents() {
2312         return 0;
2313     }
2314 
writeToParcel(Parcel dest, int flags)2315     public final void writeToParcel(Parcel dest, int flags) {
2316         dest.writeStringList(mActions);
2317         if (mCategories != null) {
2318             dest.writeInt(1);
2319             dest.writeStringList(mCategories);
2320         } else {
2321             dest.writeInt(0);
2322         }
2323         if (mDataSchemes != null) {
2324             dest.writeInt(1);
2325             dest.writeStringList(mDataSchemes);
2326         } else {
2327             dest.writeInt(0);
2328         }
2329         if (mStaticDataTypes != null) {
2330             dest.writeInt(1);
2331             dest.writeStringList(mStaticDataTypes);
2332         } else {
2333             dest.writeInt(0);
2334         }
2335         if (mDataTypes != null) {
2336             dest.writeInt(1);
2337             dest.writeStringList(mDataTypes);
2338         } else {
2339             dest.writeInt(0);
2340         }
2341         if (mMimeGroups != null) {
2342             dest.writeInt(1);
2343             dest.writeStringList(mMimeGroups);
2344         } else {
2345             dest.writeInt(0);
2346         }
2347         if (mDataSchemeSpecificParts != null) {
2348             final int N = mDataSchemeSpecificParts.size();
2349             dest.writeInt(N);
2350             for (int i=0; i<N; i++) {
2351                 mDataSchemeSpecificParts.get(i).writeToParcel(dest, flags);
2352             }
2353         } else {
2354             dest.writeInt(0);
2355         }
2356         if (mDataAuthorities != null) {
2357             final int N = mDataAuthorities.size();
2358             dest.writeInt(N);
2359             for (int i=0; i<N; i++) {
2360                 mDataAuthorities.get(i).writeToParcel(dest);
2361             }
2362         } else {
2363             dest.writeInt(0);
2364         }
2365         if (mDataPaths != null) {
2366             final int N = mDataPaths.size();
2367             dest.writeInt(N);
2368             for (int i=0; i<N; i++) {
2369                 mDataPaths.get(i).writeToParcel(dest, flags);
2370             }
2371         } else {
2372             dest.writeInt(0);
2373         }
2374         dest.writeInt(mPriority);
2375         dest.writeInt(mHasStaticPartialTypes ? 1 : 0);
2376         dest.writeInt(mHasDynamicPartialTypes ? 1 : 0);
2377         dest.writeInt(getAutoVerify() ? 1 : 0);
2378         dest.writeInt(mInstantAppVisibility);
2379         dest.writeInt(mOrder);
2380     }
2381 
2382     /**
2383      * For debugging -- perform a check on the filter, return true if it passed
2384      * or false if it failed.
2385      *
2386      * {@hide}
2387      */
debugCheck()2388     public boolean debugCheck() {
2389         return true;
2390 
2391         // This code looks for intent filters that do not specify data.
2392         /*
2393         if (mActions != null && mActions.size() == 1
2394                 && mActions.contains(Intent.ACTION_MAIN)) {
2395             return true;
2396         }
2397 
2398         if (mDataTypes == null && mDataSchemes == null) {
2399             Log.w("IntentFilter", "QUESTIONABLE INTENT FILTER:");
2400             dump(Log.WARN, "IntentFilter", "  ");
2401             return false;
2402         }
2403 
2404         return true;
2405         */
2406     }
2407 
2408     /**
2409      * Perform a check on data paths and scheme specific parts of the intent filter.
2410      * Return true if it passed.
2411      * @hide
2412      */
checkDataPathAndSchemeSpecificParts()2413     public boolean checkDataPathAndSchemeSpecificParts() {
2414         final int numDataPath = mDataPaths == null
2415                 ? 0 : mDataPaths.size();
2416         final int numDataSchemeSpecificParts = mDataSchemeSpecificParts == null
2417                 ? 0 : mDataSchemeSpecificParts.size();
2418         for (int i = 0; i < numDataPath; i++) {
2419             if (!mDataPaths.get(i).check()) {
2420                 return false;
2421             }
2422         }
2423         for (int i = 0; i < numDataSchemeSpecificParts; i++) {
2424             if (!mDataSchemeSpecificParts.get(i).check()) {
2425                 return false;
2426             }
2427         }
2428         return true;
2429     }
2430 
2431     /** @hide */
IntentFilter(Parcel source)2432     public IntentFilter(Parcel source) {
2433         mActions = new ArrayList<String>();
2434         source.readStringList(mActions);
2435         if (source.readInt() != 0) {
2436             mCategories = new ArrayList<String>();
2437             source.readStringList(mCategories);
2438         }
2439         if (source.readInt() != 0) {
2440             mDataSchemes = new ArrayList<String>();
2441             source.readStringList(mDataSchemes);
2442         }
2443         if (source.readInt() != 0) {
2444             mStaticDataTypes = new ArrayList<String>();
2445             source.readStringList(mStaticDataTypes);
2446         }
2447         if (source.readInt() != 0) {
2448             mDataTypes = new ArrayList<String>();
2449             source.readStringList(mDataTypes);
2450         }
2451         if (source.readInt() != 0) {
2452             mMimeGroups = new ArrayList<String>();
2453             source.readStringList(mMimeGroups);
2454         }
2455         int N = source.readInt();
2456         if (N > 0) {
2457             mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(N);
2458             for (int i=0; i<N; i++) {
2459                 mDataSchemeSpecificParts.add(new PatternMatcher(source));
2460             }
2461         }
2462         N = source.readInt();
2463         if (N > 0) {
2464             mDataAuthorities = new ArrayList<AuthorityEntry>(N);
2465             for (int i=0; i<N; i++) {
2466                 mDataAuthorities.add(new AuthorityEntry(source));
2467             }
2468         }
2469         N = source.readInt();
2470         if (N > 0) {
2471             mDataPaths = new ArrayList<PatternMatcher>(N);
2472             for (int i=0; i<N; i++) {
2473                 mDataPaths.add(new PatternMatcher(source));
2474             }
2475         }
2476         mPriority = source.readInt();
2477         mHasStaticPartialTypes = source.readInt() > 0;
2478         mHasDynamicPartialTypes = source.readInt() > 0;
2479         setAutoVerify(source.readInt() > 0);
2480         setVisibilityToInstantApp(source.readInt());
2481         mOrder = source.readInt();
2482     }
2483 
hasPartialTypes()2484     private boolean hasPartialTypes() {
2485         return mHasStaticPartialTypes || mHasDynamicPartialTypes;
2486     }
2487 
findMimeType(String type)2488     private final boolean findMimeType(String type) {
2489         final ArrayList<String> t = mDataTypes;
2490 
2491         if (type == null) {
2492             return false;
2493         }
2494 
2495         if (t.contains(type)) {
2496             return true;
2497         }
2498 
2499         // Deal with an Intent wanting to match every type in the IntentFilter.
2500         final int typeLength = type.length();
2501         if (typeLength == 3 && type.equals("*/*")) {
2502             return !t.isEmpty();
2503         }
2504 
2505         // Deal with this IntentFilter wanting to match every Intent type.
2506         if (hasPartialTypes() && t.contains("*")) {
2507             return true;
2508         }
2509 
2510         final int slashpos = type.indexOf('/');
2511         if (slashpos > 0) {
2512             if (hasPartialTypes() && t.contains(type.substring(0, slashpos))) {
2513                 return true;
2514             }
2515             if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') {
2516                 // Need to look through all types for one that matches
2517                 // our base...
2518                 final int numTypes = t.size();
2519                 for (int i = 0; i < numTypes; i++) {
2520                     final String v = t.get(i);
2521                     if (type.regionMatches(0, v, 0, slashpos+1)) {
2522                         return true;
2523                     }
2524                 }
2525             }
2526         }
2527 
2528         return false;
2529     }
2530 
2531     /**
2532      * @hide
2533      */
getHostsList()2534     public ArrayList<String> getHostsList() {
2535         ArrayList<String> result = new ArrayList<>();
2536         Iterator<IntentFilter.AuthorityEntry> it = authoritiesIterator();
2537         if (it != null) {
2538             while (it.hasNext()) {
2539                 IntentFilter.AuthorityEntry entry = it.next();
2540                 result.add(entry.getHost());
2541             }
2542         }
2543         return result;
2544     }
2545 
2546     /**
2547      * @hide
2548      */
getHosts()2549     public String[] getHosts() {
2550         ArrayList<String> list = getHostsList();
2551         return list.toArray(new String[list.size()]);
2552     }
2553 }
2554