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