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.FlaggedApi;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.compat.annotation.ChangeId;
25 import android.compat.annotation.Disabled;
26 import android.compat.annotation.Overridable;
27 import android.compat.annotation.UnsupportedAppUsage;
28 import android.content.pm.Flags;
29 import android.net.Uri;
30 import android.os.Build;
31 import android.os.Bundle;
32 import android.os.Parcel;
33 import android.os.Parcelable;
34 import android.os.PatternMatcher;
35 import android.os.PersistableBundle;
36 import android.text.TextUtils;
37 import android.util.AndroidException;
38 import android.util.ArraySet;
39 import android.util.Log;
40 import android.util.Printer;
41 import android.util.proto.ProtoOutputStream;
42 
43 import com.android.internal.util.XmlUtils;
44 
45 import org.xmlpull.v1.XmlPullParser;
46 import org.xmlpull.v1.XmlPullParserException;
47 import org.xmlpull.v1.XmlSerializer;
48 
49 import java.io.IOException;
50 import java.lang.annotation.Retention;
51 import java.lang.annotation.RetentionPolicy;
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Objects;
57 import java.util.Set;
58 import java.util.function.BiConsumer;
59 import java.util.function.Predicate;
60 
61 /**
62  * Structured description of Intent values to be matched.  An IntentFilter can
63  * match against actions, categories, and data (either via its type, scheme,
64  * and/or path) in an Intent.  It also includes a "priority" value which is
65  * used to order multiple matching filters.
66  *
67  * <p>IntentFilter objects are often created in XML as part of a package's
68  * {@link android.R.styleable#AndroidManifest AndroidManifest.xml} file,
69  * using {@link android.R.styleable#AndroidManifestIntentFilter intent-filter}
70  * tags.
71  *
72  * <p>There are three Intent characteristics you can filter on: the
73  * <em>action</em>, <em>data</em>, and <em>categories</em>.  For each of these
74  * characteristics you can provide
75  * multiple possible matching values (via {@link #addAction},
76  * {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart},
77  * {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively).
78  * For actions, if no data characteristics are specified, then the filter will
79  * only match intents that contain no data.
80  *
81  * <p>The data characteristic is
82  * itself divided into three attributes: type, scheme, authority, and path.
83  * Any that are
84  * specified must match the contents of the Intent.  If you specify a scheme
85  * but no type, only Intent that does not have a type (such as mailto:) will
86  * match; a content: URI will never match because they always have a MIME type
87  * that is supplied by their content provider.  Specifying a type with no scheme
88  * has somewhat special meaning: it will match either an Intent with no URI
89  * field, or an Intent with a content: or file: URI.  If you specify neither,
90  * then only an Intent with no data or type will match.  To specify an authority,
91  * you must also specify one or more schemes that it is associated with.
92  * To specify a path, you also must specify both one or more authorities and
93  * one or more schemes it is associated with.
94  *
95  * <div class="special reference">
96  * <h3>Developer Guides</h3>
97  * <p>For information about how to create and resolve intents, read the
98  * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
99  * developer guide.</p>
100  * </div>
101  *
102  * <h3>Filter Rules</h3>
103  * <p>A match is based on the following rules.  Note that
104  * for an IntentFilter to match an Intent, three conditions must hold:
105  * the <strong>action</strong> and <strong>category</strong> must match, and
106  * the data (both the <strong>data type</strong> and
107  * <strong>data scheme+authority+path</strong> if specified) must match
108  * (see {@link #match(ContentResolver, Intent, boolean, String)} for more details
109  * on how the data fields match).
110  *
111  * <p><strong>Action</strong> matches if any of the given values match the
112  * Intent action; if the filter specifies no actions, then it will only match
113  * Intents that do not contain an action.
114  *
115  * <p><strong>Data Type</strong> matches if any of the given values match the
116  * Intent type.  The Intent
117  * type is determined by calling {@link Intent#resolveType}.  A wildcard can be
118  * used for the MIME sub-type, in both the Intent and IntentFilter, so that the
119  * type "audio/*" will match "audio/mpeg", "audio/aiff", "audio/*", etc.
120  * <em>Note that MIME type matching here is <b>case sensitive</b>, unlike
121  * formal RFC MIME types!</em>  You should thus always use lower case letters
122  * for your MIME types.
123  *
124  * <p><strong>Data Scheme</strong> matches if any of the given values match the
125  * Intent data's scheme.
126  * The Intent scheme is determined by calling {@link Intent#getData}
127  * and {@link android.net.Uri#getScheme} on that URI.
128  * <em>Note that scheme matching here is <b>case sensitive</b>, unlike
129  * formal RFC schemes!</em>  You should thus always use lower case letters
130  * for your schemes.
131  *
132  * <p><strong>Data Scheme Specific Part</strong> matches if any of the given values match
133  * the Intent's data scheme specific part <em>and</em> one of the data schemes in the filter
134  * has matched the Intent, <em>or</em> no scheme specific parts were supplied in the filter.
135  * The Intent scheme specific part is determined by calling
136  * {@link Intent#getData} and {@link android.net.Uri#getSchemeSpecificPart} on that URI.
137  * <em>Note that scheme specific part matching is <b>case sensitive</b>.</em>
138  *
139  * <p><strong>Data Authority</strong> matches if any of the given values match
140  * the Intent's data authority <em>and</em> one of the data schemes in the filter
141  * has matched the Intent, <em>or</em> no authorities were supplied in the filter.
142  * The Intent authority is determined by calling
143  * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
144  * <em>Note that authority matching here is <b>case sensitive</b>, unlike
145  * formal RFC host names!</em>  You should thus always use lower case letters
146  * for your authority.
147  *
148  * <p><strong>Data Path</strong> matches if any of the given values match the
149  * Intent's data path <em>and</em> both a scheme and authority in the filter
150  * has matched against the Intent, <em>or</em> no paths were supplied in the
151  * filter.  The Intent authority is determined by calling
152  * {@link Intent#getData} and {@link android.net.Uri#getPath} on that URI.
153  *
154  * <p><strong>Categories</strong> match if <em>all</em> of the categories in
155  * the Intent match categories given in the filter.  Extra categories in the
156  * filter that are not in the Intent will not cause the match to fail.  Note
157  * that unlike the action, an IntentFilter with no categories
158  * will only match an Intent that does not have any categories.
159  */
160 @android.ravenwood.annotation.RavenwoodKeepWholeClass
161 public class IntentFilter implements Parcelable {
162     private static final String TAG = "IntentFilter";
163 
164     private static final String AGLOB_STR = "aglob";
165     private static final String SGLOB_STR = "sglob";
166     private static final String PREFIX_STR = "prefix";
167     private static final String SUFFIX_STR = "suffix";
168     private static final String LITERAL_STR = "literal";
169     private static final String PATH_STR = "path";
170     private static final String PORT_STR = "port";
171     private static final String HOST_STR = "host";
172     private static final String AUTH_STR = "auth";
173     private static final String SSP_STR = "ssp";
174     private static final String SCHEME_STR = "scheme";
175     private static final String STATIC_TYPE_STR = "staticType";
176     private static final String TYPE_STR = "type";
177     private static final String GROUP_STR = "group";
178     private static final String CAT_STR = "cat";
179     private static final String NAME_STR = "name";
180     private static final String ACTION_STR = "action";
181     private static final String AUTO_VERIFY_STR = "autoVerify";
182     private static final String EXTRAS_STR = "extras";
183     private static final String URI_RELATIVE_FILTER_GROUP_STR = "uriRelativeFilterGroup";
184 
185     private static final int[] EMPTY_INT_ARRAY = new int[0];
186     private static final long[] EMPTY_LONG_ARRAY = new long[0];
187     private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
188     private static final String[] EMPTY_STRING_ARRAY = new String[0];
189     private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
190 
191     /**
192      * An intent with action set as null used to always pass the action test during intent
193      * filter matching. This causes a lot of confusion and unexpected intent matches.
194      * Null action intents should be blocked when the intent sender application targets V or higher.
195      *
196      * @hide
197      */
198     @ChangeId
199     @Disabled
200     @Overridable
201     public static final long BLOCK_NULL_ACTION_INTENTS = 293560872;
202 
203     /**
204      * The filter {@link #setPriority} value at which system high-priority
205      * receivers are placed; that is, receivers that should execute before
206      * application code. Applications should never use filters with this or
207      * higher priorities.
208      *
209      * @see #setPriority
210      */
211     public static final int SYSTEM_HIGH_PRIORITY = 1000;
212 
213     /**
214      * The filter {@link #setPriority} value at which system low-priority
215      * receivers are placed; that is, receivers that should execute after
216      * application code. Applications should never use filters with this or
217      * lower priorities.
218      *
219      * @see #setPriority
220      */
221     public static final int SYSTEM_LOW_PRIORITY = -1000;
222 
223     /**
224      * The part of a match constant that describes the category of match
225      * that occurred.  May be either {@link #MATCH_CATEGORY_EMPTY},
226      * {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_SCHEME_SPECIFIC_PART},
227      * {@link #MATCH_CATEGORY_HOST}, {@link #MATCH_CATEGORY_PORT},
228      * {@link #MATCH_CATEGORY_PATH}, or {@link #MATCH_CATEGORY_TYPE}.  Higher
229      * values indicate a better match.
230      */
231     public static final int MATCH_CATEGORY_MASK = 0xfff0000;
232 
233     /**
234      * The part of a match constant that applies a quality adjustment to the
235      * basic category of match.  The value {@link #MATCH_ADJUSTMENT_NORMAL}
236      * is no adjustment; higher numbers than that improve the quality, while
237      * lower numbers reduce it.
238      */
239     public static final int MATCH_ADJUSTMENT_MASK = 0x000ffff;
240 
241     /**
242      * Quality adjustment applied to the category of match that signifies
243      * the default, base value; higher numbers improve the quality while
244      * lower numbers reduce it.
245      */
246     public static final int MATCH_ADJUSTMENT_NORMAL = 0x8000;
247 
248     /**
249      * The filter matched an intent that had no data specified.
250      */
251     public static final int MATCH_CATEGORY_EMPTY = 0x0100000;
252     /**
253      * The filter matched an intent with the same data URI scheme.
254      */
255     public static final int MATCH_CATEGORY_SCHEME = 0x0200000;
256     /**
257      * The filter matched an intent with the same data URI scheme and
258      * authority host.
259      */
260     public static final int MATCH_CATEGORY_HOST = 0x0300000;
261     /**
262      * The filter matched an intent with the same data URI scheme and
263      * authority host and port.
264      */
265     public static final int MATCH_CATEGORY_PORT = 0x0400000;
266     /**
267      * The filter matched an intent with the same data URI scheme,
268      * authority, and path.
269      */
270     public static final int MATCH_CATEGORY_PATH = 0x0500000;
271     /**
272      * The filter matched an intent with the same data URI scheme and
273      * scheme specific part.
274      */
275     public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0x0580000;
276     /**
277      * The filter matched an intent with the same data MIME type.
278      */
279     public static final int MATCH_CATEGORY_TYPE = 0x0600000;
280 
281     /**
282      * The filter didn't match due to different MIME types.
283      */
284     public static final int NO_MATCH_TYPE = -1;
285     /**
286      * The filter didn't match due to different data URIs.
287      */
288     public static final int NO_MATCH_DATA = -2;
289     /**
290      * The filter didn't match due to different actions.
291      */
292     public static final int NO_MATCH_ACTION = -3;
293     /**
294      * The filter didn't match because it required one or more categories
295      * that were not in the Intent.
296      */
297     public static final int NO_MATCH_CATEGORY = -4;
298     /**
299      * That filter didn't match due to different extras data.
300      * @hide
301      */
302     public static final int NO_MATCH_EXTRAS = -5;
303 
304     /**
305      * HTTP scheme.
306      *
307      * @see #addDataScheme(String)
308      * @hide
309      */
310     public static final String SCHEME_HTTP = "http";
311     /**
312      * HTTPS scheme.
313      *
314      * @see #addDataScheme(String)
315      * @hide
316      */
317     public static final String SCHEME_HTTPS = "https";
318 
319     /**
320      * Package scheme
321      *
322      * @see #addDataScheme(String)
323      * @hide
324      */
325     public static final String SCHEME_PACKAGE = "package";
326 
327     /**
328      * The value to indicate a wildcard for incoming match arguments.
329      * @hide
330      */
331     public static final String WILDCARD = "*";
332     /** @hide */
333     public static final String WILDCARD_PATH = "/" + WILDCARD;
334 
335     private int mPriority;
336     @UnsupportedAppUsage
337     private int mOrder;
338     @UnsupportedAppUsage
339     private final ArraySet<String> mActions;
340     private ArrayList<String> mCategories = null;
341     private ArrayList<String> mDataSchemes = null;
342     private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
343     private ArrayList<AuthorityEntry> mDataAuthorities = null;
344     private ArrayList<PatternMatcher> mDataPaths = null;
345     private ArrayList<UriRelativeFilterGroup> mUriRelativeFilterGroups = null;
346     private ArrayList<String> mStaticDataTypes = null;
347     private ArrayList<String> mDataTypes = null;
348     private ArrayList<String> mMimeGroups = null;
349     private boolean mHasStaticPartialTypes = false;
350     private boolean mHasDynamicPartialTypes = false;
351     private PersistableBundle mExtras = null;
352 
353     private static final int STATE_VERIFY_AUTO         = 0x00000001;
354     private static final int STATE_NEED_VERIFY         = 0x00000010;
355     private static final int STATE_NEED_VERIFY_CHECKED = 0x00000100;
356     private static final int STATE_VERIFIED            = 0x00001000;
357 
358     private int mVerifyState;
359     /** @hide */
360     public static final int VISIBILITY_NONE = 0;
361     /** @hide */
362     public static final int VISIBILITY_EXPLICIT = 1;
363     /** @hide */
364     public static final int VISIBILITY_IMPLICIT = 2;
365     /** @hide */
366     @IntDef(prefix = { "VISIBILITY_" }, value = {
367             VISIBILITY_NONE,
368             VISIBILITY_EXPLICIT,
369             VISIBILITY_IMPLICIT,
370     })
371     @Retention(RetentionPolicy.SOURCE)
372     public @interface InstantAppVisibility {}
373     /** Whether or not the intent filter is visible to instant apps. */
374     private @InstantAppVisibility int mInstantAppVisibility;
375     // These functions are the start of more optimized code for managing
376     // the string sets...  not yet implemented.
377 
findStringInSet(String[] set, String string, int[] lengths, int lenPos)378     private static int findStringInSet(String[] set, String string,
379             int[] lengths, int lenPos) {
380         if (set == null) return -1;
381         final int N = lengths[lenPos];
382         for (int i=0; i<N; i++) {
383             if (set[i].equals(string)) return i;
384         }
385         return -1;
386     }
387 
addStringToSet(String[] set, String string, int[] lengths, int lenPos)388     private static String[] addStringToSet(String[] set, String string,
389             int[] lengths, int lenPos) {
390         if (findStringInSet(set, string, lengths, lenPos) >= 0) return set;
391         if (set == null) {
392             set = new String[2];
393             set[0] = string;
394             lengths[lenPos] = 1;
395             return set;
396         }
397         final int N = lengths[lenPos];
398         if (N < set.length) {
399             set[N] = string;
400             lengths[lenPos] = N+1;
401             return set;
402         }
403 
404         String[] newSet = new String[(N*3)/2 + 2];
405         System.arraycopy(set, 0, newSet, 0, N);
406         set = newSet;
407         set[N] = string;
408         lengths[lenPos] = N+1;
409         return set;
410     }
411 
removeStringFromSet(String[] set, String string, int[] lengths, int lenPos)412     private static String[] removeStringFromSet(String[] set, String string,
413             int[] lengths, int lenPos) {
414         int pos = findStringInSet(set, string, lengths, lenPos);
415         if (pos < 0) return set;
416         final int N = lengths[lenPos];
417         if (N > (set.length/4)) {
418             int copyLen = N-(pos+1);
419             if (copyLen > 0) {
420                 System.arraycopy(set, pos+1, set, pos, copyLen);
421             }
422             set[N-1] = null;
423             lengths[lenPos] = N-1;
424             return set;
425         }
426 
427         String[] newSet = new String[set.length/3];
428         if (pos > 0) System.arraycopy(set, 0, newSet, 0, pos);
429         if ((pos+1) < N) System.arraycopy(set, pos+1, newSet, pos, N-(pos+1));
430         return newSet;
431     }
432 
433     /**
434      * This exception is thrown when a given MIME type does not have a valid
435      * syntax.
436      */
437     public static class MalformedMimeTypeException extends AndroidException {
MalformedMimeTypeException()438         public MalformedMimeTypeException() {
439         }
440 
MalformedMimeTypeException(String name)441         public MalformedMimeTypeException(String name) {
442             super(name);
443         }
444     }
445 
446     /**
447      * Create a new IntentFilter instance with a specified action and MIME
448      * type, where you know the MIME type is correctly formatted.  This catches
449      * the {@link MalformedMimeTypeException} exception that the constructor
450      * can call and turns it into a runtime exception.
451      *
452      * @param action The action to match, such as Intent.ACTION_VIEW.
453      * @param dataType The type to match, such as "vnd.android.cursor.dir/person".
454      *
455      * @return A new IntentFilter for the given action and type.
456      *
457      * @see #IntentFilter(String, String)
458      */
create(String action, String dataType)459     public static IntentFilter create(String action, String dataType) {
460         try {
461             return new IntentFilter(action, dataType);
462         } catch (MalformedMimeTypeException e) {
463             throw new RuntimeException("Bad MIME type", e);
464         }
465     }
466 
467     /**
468      * New empty IntentFilter.
469      */
IntentFilter()470     public IntentFilter() {
471         mPriority = 0;
472         mActions = new ArraySet<>();
473     }
474 
475     /**
476      * New IntentFilter that matches a single action with no data.  If
477      * no data characteristics are subsequently specified, then the
478      * filter will only match intents that contain no data.
479      *
480      * @param action The action to match, such as Intent.ACTION_MAIN.
481      */
IntentFilter(String action)482     public IntentFilter(String action) {
483         mPriority = 0;
484         mActions = new ArraySet<>();
485         addAction(action);
486     }
487 
488     /**
489      * New IntentFilter that matches a single action and data type.
490      *
491      * <p><em>Note: MIME type matching in the Android framework is
492      * case-sensitive, unlike formal RFC MIME types.  As a result,
493      * you should always write your MIME types with lower case letters,
494      * and any MIME types you receive from outside of Android should be
495      * converted to lower case before supplying them here.</em></p>
496      *
497      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
498      * not syntactically correct.
499      *
500      * @param action The action to match, such as Intent.ACTION_VIEW.
501      * @param dataType The type to match, such as "vnd.android.cursor.dir/person".
502      *
503      */
IntentFilter(String action, String dataType)504     public IntentFilter(String action, String dataType)
505         throws MalformedMimeTypeException {
506         mPriority = 0;
507         mActions = new ArraySet<>();
508         addAction(action);
509         addDataType(dataType);
510     }
511 
512     /**
513      * New IntentFilter containing a copy of an existing filter.
514      *
515      * @param o The original filter to copy.
516      */
IntentFilter(IntentFilter o)517     public IntentFilter(IntentFilter o) {
518         mPriority = o.mPriority;
519         mOrder = o.mOrder;
520         mActions = new ArraySet<>(o.mActions);
521         if (o.mCategories != null) {
522             mCategories = new ArrayList<String>(o.mCategories);
523         }
524         if (o.mStaticDataTypes != null) {
525             mStaticDataTypes = new ArrayList<String>(o.mStaticDataTypes);
526         }
527         if (o.mDataTypes != null) {
528             mDataTypes = new ArrayList<String>(o.mDataTypes);
529         }
530         if (o.mDataSchemes != null) {
531             mDataSchemes = new ArrayList<String>(o.mDataSchemes);
532         }
533         if (o.mDataSchemeSpecificParts != null) {
534             mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(o.mDataSchemeSpecificParts);
535         }
536         if (o.mDataAuthorities != null) {
537             mDataAuthorities = new ArrayList<AuthorityEntry>(o.mDataAuthorities);
538         }
539         if (o.mDataPaths != null) {
540             mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
541         }
542         if (o.mUriRelativeFilterGroups != null) {
543             mUriRelativeFilterGroups =
544                     new ArrayList<UriRelativeFilterGroup>(o.mUriRelativeFilterGroups);
545         }
546         if (o.mMimeGroups != null) {
547             mMimeGroups = new ArrayList<String>(o.mMimeGroups);
548         }
549         if (o.mExtras != null) {
550             mExtras = new PersistableBundle(o.mExtras);
551         }
552         mHasStaticPartialTypes = o.mHasStaticPartialTypes;
553         mHasDynamicPartialTypes = o.mHasDynamicPartialTypes;
554         mVerifyState = o.mVerifyState;
555         mInstantAppVisibility = o.mInstantAppVisibility;
556     }
557 
558     /** @hide */
toLongString()559     public String toLongString() {
560         // Not implemented directly as toString() due to potential memory regression
561         final StringBuilder sb = new StringBuilder();
562         sb.append("IntentFilter {");
563         sb.append(" pri=");
564         sb.append(mPriority);
565         if (countActions() > 0) {
566             sb.append(" act=");
567             sb.append(mActions.toString());
568         }
569         if (countCategories() > 0) {
570             sb.append(" cat=");
571             sb.append(mCategories.toString());
572         }
573         if (countDataSchemes() > 0) {
574             sb.append(" sch=");
575             sb.append(mDataSchemes.toString());
576         }
577         if (Flags.relativeReferenceIntentFilters() && countUriRelativeFilterGroups() > 0) {
578             sb.append(" grp=");
579             sb.append(mUriRelativeFilterGroups.toString());
580         }
581         sb.append(" }");
582         return sb.toString();
583     }
584 
585     /**
586      * Modify priority of this filter.  This only affects receiver filters.
587      * The priority of activity filters are set in XML and cannot be changed
588      * programmatically. The default priority is 0. Positive values will be
589      * before the default, lower values will be after it. Applications should
590      * use a value that is larger than {@link #SYSTEM_LOW_PRIORITY} and
591      * smaller than {@link #SYSTEM_HIGH_PRIORITY} .
592      *
593      * @param priority The new priority value.
594      *
595      * @see #getPriority
596      * @see #SYSTEM_LOW_PRIORITY
597      * @see #SYSTEM_HIGH_PRIORITY
598      */
setPriority(int priority)599     public final void setPriority(int priority) {
600         mPriority = priority;
601     }
602 
603     /**
604      * Return the priority of this filter.
605      *
606      * @return The priority of the filter.
607      *
608      * @see #setPriority
609      */
getPriority()610     public final int getPriority() {
611         return mPriority;
612     }
613 
614     /** @hide */
615     @SystemApi
setOrder(int order)616     public final void setOrder(int order) {
617         mOrder = order;
618     }
619 
620     /** @hide */
621     @SystemApi
getOrder()622     public final int getOrder() {
623         return mOrder;
624     }
625 
626     /**
627      * Set whether this filter will needs to be automatically verified against its data URIs or not.
628      * The default is false.
629      *
630      * The verification would need to happen only and only if the Intent action is
631      * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
632      * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
633      * is "http" or "https".
634      *
635      * True means that the filter will need to use its data URIs to be verified.
636      *
637      * @param autoVerify The new autoVerify value.
638      *
639      * @see #getAutoVerify()
640      * @see #addAction(String)
641      * @see #getAction(int)
642      * @see #addCategory(String)
643      * @see #getCategory(int)
644      * @see #addDataScheme(String)
645      * @see #getDataScheme(int)
646      *
647      * @hide
648      */
649     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setAutoVerify(boolean autoVerify)650     public final void setAutoVerify(boolean autoVerify) {
651         mVerifyState &= ~STATE_VERIFY_AUTO;
652         if (autoVerify) mVerifyState |= STATE_VERIFY_AUTO;
653     }
654 
655     /**
656      * Return if this filter will needs to be automatically verified again its data URIs or not.
657      *
658      * @return True if the filter will needs to be automatically verified. False otherwise.
659      *
660      * @see #setAutoVerify(boolean)
661      *
662      * @hide
663      */
getAutoVerify()664     public final boolean getAutoVerify() {
665         return ((mVerifyState & STATE_VERIFY_AUTO) == STATE_VERIFY_AUTO);
666     }
667 
668     /**
669      * Return if this filter handle all HTTP or HTTPS data URI or not.  This is the
670      * core check for whether a given activity qualifies as a "browser".
671      *
672      * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise.
673      *
674      * This will check if:
675      *
676      * - either the Intent category is {@link android.content.Intent#CATEGORY_APP_BROWSER}
677      * - either the Intent action is {@link android.content.Intent#ACTION_VIEW} and
678      * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
679      * data scheme is "http" or "https" and that there is no specific host defined.
680      *
681      * @hide
682      */
handleAllWebDataURI()683     public final boolean handleAllWebDataURI() {
684         return hasCategory(Intent.CATEGORY_APP_BROWSER) ||
685                 (handlesWebUris(false) && countDataAuthorities() == 0);
686     }
687 
688     /**
689      * Return if this filter handles HTTP or HTTPS data URIs.
690      *
691      * @return True if the filter handles ACTION_VIEW/CATEGORY_BROWSABLE,
692      * has at least one HTTP or HTTPS data URI pattern defined, and optionally
693      * does not define any non-http/https data URI patterns.
694      *
695      * This will check if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
696      * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
697      * data scheme is "http" or "https".
698      *
699      * @param onlyWebSchemes When true, requires that the intent filter declare
700      *     that it handles *only* http: or https: schemes.  This is a requirement for
701      *     the intent filter's domain linkage being verifiable.
702      * @hide
703      */
handlesWebUris(boolean onlyWebSchemes)704     public final boolean handlesWebUris(boolean onlyWebSchemes) {
705         // Require ACTION_VIEW, CATEGORY_BROWSEABLE, and at least one scheme
706         if (!hasAction(Intent.ACTION_VIEW)
707             || !hasCategory(Intent.CATEGORY_BROWSABLE)
708             || mDataSchemes == null
709             || mDataSchemes.size() == 0) {
710             return false;
711         }
712 
713         // Now allow only the schemes "http" and "https"
714         final int N = mDataSchemes.size();
715         for (int i = 0; i < N; i++) {
716             final String scheme = mDataSchemes.get(i);
717             final boolean isWebScheme =
718                     SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme);
719             if (onlyWebSchemes) {
720                 // If we're specifically trying to ensure that there are no non-web schemes
721                 // declared in this filter, then if we ever see a non-http/https scheme then
722                 // we know it's a failure.
723                 if (!isWebScheme) {
724                     return false;
725                 }
726             } else {
727                 // If we see any http/https scheme declaration in this case then the
728                 // filter matches what we're looking for.
729                 if (isWebScheme) {
730                     return true;
731                 }
732             }
733         }
734 
735         // We get here if:
736         //   1) onlyWebSchemes and no non-web schemes were found, i.e. success; or
737         //   2) !onlyWebSchemes and no http/https schemes were found, i.e. failure.
738         return onlyWebSchemes;
739     }
740 
741     /**
742      * Return if this filter needs to be automatically verified again its data URIs or not.
743      *
744      * @return True if the filter needs to be automatically verified. False otherwise.
745      *
746      * This will check if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
747      * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
748      * data scheme is "http" or "https".
749      *
750      * @see #setAutoVerify(boolean)
751      *
752      * @hide
753      */
needsVerification()754     public final boolean needsVerification() {
755         return getAutoVerify() && handlesWebUris(true);
756     }
757 
758     /**
759      * Return if this filter has been verified
760      *
761      * @return true if the filter has been verified or if autoVerify is false.
762      *
763      * @hide
764      */
765     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
isVerified()766     public final boolean isVerified() {
767         if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) {
768             return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY);
769         }
770         return false;
771     }
772 
773     /**
774      * Set if this filter has been verified
775      *
776      * @param verified true if this filter has been verified. False otherwise.
777      *
778      * @hide
779      */
setVerified(boolean verified)780     public void setVerified(boolean verified) {
781         mVerifyState |= STATE_NEED_VERIFY_CHECKED;
782         mVerifyState &= ~STATE_VERIFIED;
783         if (verified) mVerifyState |= STATE_VERIFIED;
784     }
785 
786     /** @hide */
setVisibilityToInstantApp(@nstantAppVisibility int visibility)787     public void setVisibilityToInstantApp(@InstantAppVisibility int visibility) {
788         mInstantAppVisibility = visibility;
789     }
790     /** @hide */
getVisibilityToInstantApp()791     public @InstantAppVisibility int getVisibilityToInstantApp() {
792         return mInstantAppVisibility;
793     }
794     /** @hide */
isVisibleToInstantApp()795     public boolean isVisibleToInstantApp() {
796         return mInstantAppVisibility != VISIBILITY_NONE;
797     }
798     /** @hide */
isExplicitlyVisibleToInstantApp()799     public boolean isExplicitlyVisibleToInstantApp() {
800         return mInstantAppVisibility == VISIBILITY_EXPLICIT;
801     }
802     /** @hide */
isImplicitlyVisibleToInstantApp()803     public boolean isImplicitlyVisibleToInstantApp() {
804         return mInstantAppVisibility == VISIBILITY_IMPLICIT;
805     }
806 
807     /**
808      * Add a new Intent action to match against.  If any actions are included
809      * in the filter, then an Intent's action must be one of those values for
810      * it to match.  If no actions are included, the Intent action is ignored.
811      *
812      * @param action Name of the action to match, such as Intent.ACTION_VIEW.
813      */
addAction(String action)814     public final void addAction(String action) {
815         mActions.add(action.intern());
816     }
817 
818     /**
819      * Return the number of actions in the filter.
820      */
countActions()821     public final int countActions() {
822         return mActions.size();
823     }
824 
825     /**
826      * Returns the number of actions in the filter, or {@code 0} if there are no actions.
827      * <p> This method provides a safe alternative to {@link #countActions()}, which
828      * may throw an exception if there are no actions.
829      * @hide
830      */
safeCountActions()831     public final int safeCountActions() {
832         return mActions == null ? 0 : mActions.size();
833     }
834 
835     /**
836      * Return an action in the filter.
837      */
getAction(int index)838     public final String getAction(int index) {
839         return mActions.valueAt(index);
840     }
841 
842     /**
843      * Is the given action included in the filter?  Note that if the filter
844      * does not include any actions, false will <em>always</em> be returned.
845      *
846      * @param action The action to look for.
847      *
848      * @return True if the action is explicitly mentioned in the filter.
849      */
hasAction(String action)850     public final boolean hasAction(String action) {
851         return action != null && mActions.contains(action);
852     }
853 
854     /**
855      * Match this filter against an Intent's action.  If the filter does not
856      * specify any actions, the match will always fail.
857      *
858      * @param action The desired action to look for.
859      *
860      * @return True if the action is listed in the filter.
861      */
matchAction(String action)862     public final boolean matchAction(String action) {
863         return matchAction(action, false /*wildcardSupported*/, null /*ignoreActions*/);
864     }
865 
866     /**
867      * Variant of {@link #matchAction(String)} that allows a wildcard for the provided action.
868      * @param wildcardSupported if true, will allow action to use wildcards
869      * @param ignoreActions if not null, the set of actions to should not be considered valid while
870      *                      calculating the match
871      */
matchAction(String action, boolean wildcardSupported, @Nullable Collection<String> ignoreActions)872     private boolean matchAction(String action, boolean wildcardSupported,
873             @Nullable Collection<String> ignoreActions) {
874         if (wildcardSupported && WILDCARD.equals(action)) {
875             if (ignoreActions == null) {
876                 return !mActions.isEmpty();
877             }
878             if (mActions.size() > ignoreActions.size()) {
879                 return true;    // some actions are definitely not ignored
880             }
881             for (int i = mActions.size() - 1; i >= 0; i--) {
882                 if (!ignoreActions.contains(mActions.valueAt(i))) {
883                     return true;
884                 }
885             }
886             return false;
887         }
888         if (ignoreActions != null && ignoreActions.contains(action)) {
889             return false;
890         }
891         return hasAction(action);
892     }
893 
894     /**
895      * Return an iterator over the filter's actions.  If there are no actions,
896      * returns null.
897      */
actionsIterator()898     public final Iterator<String> actionsIterator() {
899         return mActions != null ? mActions.iterator() : null;
900     }
901 
902     /**
903      * Add a new Intent data type to match against.  If any types are
904      * included in the filter, then an Intent's data must be <em>either</em>
905      * one of these types <em>or</em> a matching scheme.  If no data types
906      * are included, then an Intent will only match if it specifies no data.
907      *
908      * <p><em>Note: MIME type matching in the Android framework is
909      * case-sensitive, unlike formal RFC MIME types.  As a result,
910      * you should always write your MIME types with lower case letters,
911      * and any MIME types you receive from outside of Android should be
912      * converted to lower case before supplying them here.</em></p>
913      *
914      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
915      * not syntactically correct.
916      *
917      * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person".
918      *
919      * @see #matchData
920      */
addDataType(String type)921     public final void addDataType(String type)
922         throws MalformedMimeTypeException {
923         processMimeType(type, (internalType, isPartial) -> {
924             if (mDataTypes == null) {
925                 mDataTypes = new ArrayList<>();
926             }
927             if (mStaticDataTypes == null) {
928                 mStaticDataTypes = new ArrayList<>();
929             }
930 
931             if (mDataTypes.contains(internalType)) {
932                 return;
933             }
934 
935             mDataTypes.add(internalType.intern());
936             mStaticDataTypes.add(internalType.intern());
937             mHasStaticPartialTypes = mHasStaticPartialTypes || isPartial;
938         });
939     }
940 
941     /**
942      * Add a new Intent data type <em>from MIME group</em> to match against.  If any types are
943      * included in the filter, then an Intent's data must be <em>either</em>
944      * one of these types <em>or</em> a matching scheme.  If no data types
945      * are included, then an Intent will only match if it specifies no data.
946      *
947      * <p><em>Note: MIME type matching in the Android framework is
948      * case-sensitive, unlike formal RFC MIME types.  As a result,
949      * you should always write your MIME types with lower case letters,
950      * and any MIME types you receive from outside of Android should be
951      * converted to lower case before supplying them here.</em></p>
952      *
953      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
954      * not syntactically correct.
955      *
956      * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person".
957      *
958      * @see #clearDynamicDataTypes()
959      * @hide
960      */
addDynamicDataType(String type)961     public final void addDynamicDataType(String type)
962             throws MalformedMimeTypeException {
963         processMimeType(type, (internalType, isPartial) -> {
964             if (mDataTypes == null) {
965                 mDataTypes = new ArrayList<>();
966             }
967 
968             if (!mDataTypes.contains(internalType)) {
969                 mDataTypes.add(internalType.intern());
970 
971                 mHasDynamicPartialTypes = mHasDynamicPartialTypes || isPartial;
972             }
973         });
974     }
975 
976     /**
977      * Process mime type - convert to representation used internally and check if type is partial,
978      * and then call provided action
979      */
processMimeType(String type, BiConsumer<String, Boolean> action)980     private void processMimeType(String type, BiConsumer<String, Boolean> action)
981             throws MalformedMimeTypeException {
982         final int slashpos = type.indexOf('/');
983         final int typelen = type.length();
984         if (slashpos <= 0 || typelen < slashpos + 2) {
985             throw new MalformedMimeTypeException(type);
986         }
987 
988         String internalType = type;
989         boolean isPartialType = false;
990         if (typelen == slashpos + 2 && type.charAt(slashpos + 1) == '*') {
991             internalType = type.substring(0, slashpos);
992             isPartialType = true;
993         }
994 
995         action.accept(internalType, isPartialType);
996     }
997 
998     /**
999      * Remove all previously added Intent data types from IntentFilter.
1000      *
1001      * @see #addDynamicDataType(String)
1002      * @hide
1003      */
clearDynamicDataTypes()1004     public final void clearDynamicDataTypes() {
1005         if (mDataTypes == null) {
1006             return;
1007         }
1008 
1009         if (mStaticDataTypes != null) {
1010             mDataTypes.clear();
1011             mDataTypes.addAll(mStaticDataTypes);
1012         } else {
1013             mDataTypes = null;
1014         }
1015 
1016         mHasDynamicPartialTypes = false;
1017     }
1018 
1019     /**
1020      * Return the number of static data types in the filter.
1021      * @hide
1022      */
countStaticDataTypes()1023     public int countStaticDataTypes() {
1024         return mStaticDataTypes != null ? mStaticDataTypes.size() : 0;
1025     }
1026 
1027     /**
1028      * Is the given data type included in the filter?  Note that if the filter
1029      * does not include any type, false will <em>always</em> be returned.
1030      *
1031      * @param type The data type to look for.
1032      *
1033      * @return True if the type is explicitly mentioned in the filter.
1034      */
hasDataType(String type)1035     public final boolean hasDataType(String type) {
1036         return mDataTypes != null && findMimeType(type);
1037     }
1038 
1039     /** @hide */
1040     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasExactDataType(String type)1041     public final boolean hasExactDataType(String type) {
1042         return mDataTypes != null && mDataTypes.contains(type);
1043     }
1044 
1045     /** @hide */
hasExactDynamicDataType(String type)1046     public final boolean hasExactDynamicDataType(String type) {
1047         return hasExactDataType(type) && !hasExactStaticDataType(type);
1048     }
1049 
1050     /** @hide */
hasExactStaticDataType(String type)1051     public final boolean hasExactStaticDataType(String type) {
1052         return mStaticDataTypes != null && mStaticDataTypes.contains(type);
1053     }
1054 
1055     /**
1056      * Return the number of data types in the filter.
1057      */
countDataTypes()1058     public final int countDataTypes() {
1059         return mDataTypes != null ? mDataTypes.size() : 0;
1060     }
1061 
1062     /**
1063      * Return a data type in the filter.
1064      */
getDataType(int index)1065     public final String getDataType(int index) {
1066         return mDataTypes.get(index);
1067     }
1068 
1069     /**
1070      * Return an iterator over the filter's data types.
1071      */
typesIterator()1072     public final Iterator<String> typesIterator() {
1073         return mDataTypes != null ? mDataTypes.iterator() : null;
1074     }
1075 
1076     /**
1077      * Return copy of filter's data types.
1078      * @hide
1079      */
dataTypes()1080     public final List<String> dataTypes() {
1081         return mDataTypes != null ? new ArrayList<>(mDataTypes) : null;
1082     }
1083 
1084     /** @hide */
addMimeGroup(String name)1085     public final void addMimeGroup(String name) {
1086         if (mMimeGroups == null) {
1087             mMimeGroups = new ArrayList<>();
1088         }
1089         if (!mMimeGroups.contains(name)) {
1090             mMimeGroups.add(name);
1091         }
1092     }
1093 
1094     /** @hide */
hasMimeGroup(String name)1095     public final boolean hasMimeGroup(String name) {
1096         return mMimeGroups != null && mMimeGroups.contains(name);
1097     }
1098 
1099     /** @hide */
getMimeGroup(int index)1100     public final String getMimeGroup(int index) {
1101         return mMimeGroups.get(index);
1102     }
1103 
1104     /** @hide */
countMimeGroups()1105     public final int countMimeGroups() {
1106         return mMimeGroups != null ? mMimeGroups.size() : 0;
1107     }
1108 
1109     /** @hide */
mimeGroupsIterator()1110     public final Iterator<String> mimeGroupsIterator() {
1111         return mMimeGroups != null ? mMimeGroups.iterator() : null;
1112     }
1113 
1114     /**
1115      * Add a new Intent data scheme to match against.  If any schemes are
1116      * included in the filter, then an Intent's data must be <em>either</em>
1117      * one of these schemes <em>or</em> a matching data type.  If no schemes
1118      * are included, then an Intent will match only if it includes no data.
1119      *
1120      * <p><em>Note: scheme matching in the Android framework is
1121      * case-sensitive, unlike formal RFC schemes.  As a result,
1122      * you should always write your schemes with lower case letters,
1123      * and any schemes you receive from outside of Android should be
1124      * converted to lower case before supplying them here.</em></p>
1125      *
1126      * @param scheme Name of the scheme to match, such as "http".
1127      *
1128      * @see #matchData
1129      */
addDataScheme(String scheme)1130     public final void addDataScheme(String scheme) {
1131         if (mDataSchemes == null) mDataSchemes = new ArrayList<String>();
1132         if (!mDataSchemes.contains(scheme)) {
1133             mDataSchemes.add(scheme.intern());
1134         }
1135     }
1136 
1137     /**
1138      * Return the number of data schemes in the filter.
1139      */
countDataSchemes()1140     public final int countDataSchemes() {
1141         return mDataSchemes != null ? mDataSchemes.size() : 0;
1142     }
1143 
1144     /**
1145      * Return a data scheme in the filter.
1146      */
getDataScheme(int index)1147     public final String getDataScheme(int index) {
1148         return mDataSchemes.get(index);
1149     }
1150 
1151     /**
1152      * Is the given data scheme included in the filter?  Note that if the
1153      * filter does not include any scheme, false will <em>always</em> be
1154      * returned.
1155      *
1156      * @param scheme The data scheme to look for.
1157      *
1158      * @return True if the scheme is explicitly mentioned in the filter.
1159      */
hasDataScheme(String scheme)1160     public final boolean hasDataScheme(String scheme) {
1161         return mDataSchemes != null && mDataSchemes.contains(scheme);
1162     }
1163 
1164     /**
1165      * Return an iterator over the filter's data schemes.
1166      */
schemesIterator()1167     public final Iterator<String> schemesIterator() {
1168         return mDataSchemes != null ? mDataSchemes.iterator() : null;
1169     }
1170 
1171     /**
1172      * This is an entry for a single authority in the Iterator returned by
1173      * {@link #authoritiesIterator()}.
1174      */
1175     public final static class AuthorityEntry {
1176         private final String mOrigHost;
1177         private final String mHost;
1178         private final boolean mWild;
1179         private final int mPort;
1180 
AuthorityEntry(String host, String port)1181         public AuthorityEntry(String host, String port) {
1182             mOrigHost = host;
1183             mWild = host.length() > 0 && host.charAt(0) == '*';
1184             mHost = mWild ? host.substring(1).intern() : host;
1185             mPort = port != null ? Integer.parseInt(port) : -1;
1186         }
1187 
AuthorityEntry(Parcel src)1188         AuthorityEntry(Parcel src) {
1189             mOrigHost = src.readString();
1190             mHost = src.readString();
1191             mWild = src.readInt() != 0;
1192             mPort = src.readInt();
1193         }
1194 
writeToParcel(Parcel dest)1195         void writeToParcel(Parcel dest) {
1196             dest.writeString(mOrigHost);
1197             dest.writeString(mHost);
1198             dest.writeInt(mWild ? 1 : 0);
1199             dest.writeInt(mPort);
1200         }
1201 
dumpDebug(ProtoOutputStream proto, long fieldId)1202         void dumpDebug(ProtoOutputStream proto, long fieldId) {
1203             long token = proto.start(fieldId);
1204             // The original host information is already contained in host and wild, no output now.
1205             proto.write(AuthorityEntryProto.HOST, mHost);
1206             proto.write(AuthorityEntryProto.WILD, mWild);
1207             proto.write(AuthorityEntryProto.PORT, mPort);
1208             proto.end(token);
1209         }
1210 
getHost()1211         public String getHost() {
1212             return mOrigHost;
1213         }
1214 
getPort()1215         public int getPort() {
1216             return mPort;
1217         }
1218 
1219         /** @hide */
match(AuthorityEntry other)1220         public boolean match(AuthorityEntry other) {
1221             if (mWild != other.mWild) {
1222                 return false;
1223             }
1224             if (!mHost.equals(other.mHost)) {
1225                 return false;
1226             }
1227             if (mPort != other.mPort) {
1228                 return false;
1229             }
1230             return true;
1231         }
1232 
1233         @Override
equals(@ullable Object obj)1234         public boolean equals(@Nullable Object obj) {
1235             if (obj instanceof AuthorityEntry) {
1236                 final AuthorityEntry other = (AuthorityEntry)obj;
1237                 return match(other);
1238             }
1239             return false;
1240         }
1241 
1242         /**
1243          * Determine whether this AuthorityEntry matches the given data Uri.
1244          * <em>Note that this comparison is case-sensitive, unlike formal
1245          * RFC host names.  You thus should always normalize to lower-case.</em>
1246          *
1247          * @param data The Uri to match.
1248          * @return Returns either {@link IntentFilter#NO_MATCH_DATA},
1249          * {@link IntentFilter#MATCH_CATEGORY_PORT}, or
1250          * {@link IntentFilter#MATCH_CATEGORY_HOST}.
1251          */
match(Uri data)1252         public int match(Uri data) {
1253             return match(data, false);
1254         }
1255 
1256         /**
1257          * Variant of {@link #match(Uri)} that supports wildcards on the scheme, host and
1258          * path of the provided {@link Uri}
1259          *
1260          * @param wildcardSupported if true, will allow parameters to use wildcards
1261          * @hide
1262          */
match(Uri data, boolean wildcardSupported)1263         public int match(Uri data, boolean wildcardSupported) {
1264             String host = data.getHost();
1265             if (host == null) {
1266                 if (wildcardSupported && mWild && mHost.isEmpty()) {
1267                     // special case, if no host is provided, but the Authority is wildcard, match
1268                     return MATCH_CATEGORY_HOST;
1269                 } else {
1270                     return NO_MATCH_DATA;
1271                 }
1272             }
1273             if (false) Log.v("IntentFilter",
1274                     "Match host " + host + ": " + mHost);
1275             if (!wildcardSupported || !WILDCARD.equals(host)) {
1276                 if (mWild) {
1277                     if (host.length() < mHost.length()) {
1278                         return NO_MATCH_DATA;
1279                     }
1280                     host = host.substring(host.length() - mHost.length());
1281                 }
1282                 if (host.compareToIgnoreCase(mHost) != 0) {
1283                     return NO_MATCH_DATA;
1284                 }
1285             }
1286             // if we're dealing with wildcard support, we ignore ports
1287             if (!wildcardSupported && mPort >= 0) {
1288                 if (mPort != data.getPort()) {
1289                     return NO_MATCH_DATA;
1290                 }
1291                 return MATCH_CATEGORY_PORT;
1292             }
1293             return MATCH_CATEGORY_HOST;
1294         }
1295     }
1296 
1297     /**
1298      * Add a new Intent data "scheme specific part" to match against.  The filter must
1299      * include one or more schemes (via {@link #addDataScheme}) for the
1300      * scheme specific part to be considered.  If any scheme specific parts are
1301      * included in the filter, then an Intent's data must match one of
1302      * them.  If no scheme specific parts are included, then only the scheme must match.
1303      *
1304      * <p>The "scheme specific part" that this matches against is the string returned
1305      * by {@link android.net.Uri#getSchemeSpecificPart() Uri.getSchemeSpecificPart}.
1306      * For Uris that contain a path, this kind of matching is not generally of interest,
1307      * since {@link #addDataAuthority(String, String)} and
1308      * {@link #addDataPath(String, int)} can provide a better mechanism for matching
1309      * them.  However, for Uris that do not contain a path, the authority and path
1310      * are empty, so this is the only way to match against the non-scheme part.</p>
1311      *
1312      * @param ssp Either a raw string that must exactly match the scheme specific part
1313      * path, or a simple pattern, depending on <var>type</var>.
1314      * @param type Determines how <var>ssp</var> will be compared to
1315      * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
1316      * {@link PatternMatcher#PATTERN_PREFIX},
1317      * {@link PatternMatcher#PATTERN_SUFFIX}, or
1318      * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
1319      *
1320      * @see #matchData
1321      * @see #addDataScheme
1322      */
addDataSchemeSpecificPart(String ssp, int type)1323     public final void addDataSchemeSpecificPart(String ssp, int type) {
1324         addDataSchemeSpecificPart(new PatternMatcher(ssp, type));
1325     }
1326 
1327     /** @hide */
addDataSchemeSpecificPart(PatternMatcher ssp)1328     public final void addDataSchemeSpecificPart(PatternMatcher ssp) {
1329         if (mDataSchemeSpecificParts == null) {
1330             mDataSchemeSpecificParts = new ArrayList<PatternMatcher>();
1331         }
1332         mDataSchemeSpecificParts.add(ssp);
1333     }
1334 
1335     /**
1336      * Return the number of data scheme specific parts in the filter.
1337      */
countDataSchemeSpecificParts()1338     public final int countDataSchemeSpecificParts() {
1339         return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.size() : 0;
1340     }
1341 
1342     /**
1343      * Return a data scheme specific part in the filter.
1344      */
getDataSchemeSpecificPart(int index)1345     public final PatternMatcher getDataSchemeSpecificPart(int index) {
1346         return mDataSchemeSpecificParts.get(index);
1347     }
1348 
1349     /**
1350      * Is the given data scheme specific part included in the filter?  Note that if the
1351      * filter does not include any scheme specific parts, false will <em>always</em> be
1352      * returned.
1353      *
1354      * @param data The scheme specific part that is being looked for.
1355      *
1356      * @return Returns true if the data string matches a scheme specific part listed in the
1357      *         filter.
1358      */
hasDataSchemeSpecificPart(String data)1359     public final boolean hasDataSchemeSpecificPart(String data) {
1360         return hasDataSchemeSpecificPart(data, false);
1361     }
1362 
1363     /**
1364      * Variant of {@link #hasDataSchemeSpecificPart(String)} that supports wildcards on the provided
1365      * ssp.
1366      * @param supportWildcards if true, will allow parameters to use wildcards
1367      */
hasDataSchemeSpecificPart(String data, boolean supportWildcards)1368     private boolean hasDataSchemeSpecificPart(String data, boolean supportWildcards) {
1369         if (mDataSchemeSpecificParts == null) {
1370             return false;
1371         }
1372         if (supportWildcards && WILDCARD.equals(data) && mDataSchemeSpecificParts.size() > 0) {
1373             return true;
1374         }
1375         final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
1376         for (int i = 0; i < numDataSchemeSpecificParts; i++) {
1377             final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
1378             if (pe.match(data)) {
1379                 return true;
1380             }
1381         }
1382         return false;
1383     }
1384 
1385     /** @hide */
1386     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasDataSchemeSpecificPart(PatternMatcher ssp)1387     public final boolean hasDataSchemeSpecificPart(PatternMatcher ssp) {
1388         if (mDataSchemeSpecificParts == null) {
1389             return false;
1390         }
1391         final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
1392         for (int i = 0; i < numDataSchemeSpecificParts; i++) {
1393             final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
1394             if (pe.getType() == ssp.getType() && pe.getPath().equals(ssp.getPath())) {
1395                 return true;
1396             }
1397         }
1398         return false;
1399     }
1400 
1401     /**
1402      * Return an iterator over the filter's data scheme specific parts.
1403      */
schemeSpecificPartsIterator()1404     public final Iterator<PatternMatcher> schemeSpecificPartsIterator() {
1405         return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.iterator() : null;
1406     }
1407 
1408     /**
1409      * Add a new Intent data authority to match against.  The filter must
1410      * include one or more schemes (via {@link #addDataScheme}) for the
1411      * authority to be considered.  If any authorities are
1412      * included in the filter, then an Intent's data must match one of
1413      * them.  If no authorities are included, then only the scheme must match.
1414      *
1415      * <p><em>Note: host name in the Android framework is
1416      * case-sensitive, unlike formal RFC host names.  As a result,
1417      * you should always write your host names with lower case letters,
1418      * and any host names you receive from outside of Android should be
1419      * converted to lower case before supplying them here.</em></p>
1420      *
1421      * @param host The host part of the authority to match.  May start with a
1422      *             single '*' to wildcard the front of the host name.
1423      * @param port Optional port part of the authority to match.  If null, any
1424      *             port is allowed.
1425      *
1426      * @see #matchData
1427      * @see #addDataScheme
1428      */
addDataAuthority(String host, String port)1429     public final void addDataAuthority(String host, String port) {
1430         if (port != null) port = port.intern();
1431         addDataAuthority(new AuthorityEntry(host.intern(), port));
1432     }
1433 
1434     /** @hide */
addDataAuthority(AuthorityEntry ent)1435     public final void addDataAuthority(AuthorityEntry ent) {
1436         if (mDataAuthorities == null) mDataAuthorities =
1437                 new ArrayList<AuthorityEntry>();
1438         mDataAuthorities.add(ent);
1439     }
1440 
1441     /**
1442      * Return the number of data authorities in the filter.
1443      */
countDataAuthorities()1444     public final int countDataAuthorities() {
1445         return mDataAuthorities != null ? mDataAuthorities.size() : 0;
1446     }
1447 
1448     /**
1449      * Return a data authority in the filter.
1450      */
getDataAuthority(int index)1451     public final AuthorityEntry getDataAuthority(int index) {
1452         return mDataAuthorities.get(index);
1453     }
1454 
1455     /**
1456      * Is the given data authority included in the filter?  Note that if the
1457      * filter does not include any authorities, false will <em>always</em> be
1458      * returned.
1459      *
1460      * @param data The data whose authority is being looked for.
1461      *
1462      * @return Returns true if the data string matches an authority listed in the
1463      *         filter.
1464      */
hasDataAuthority(Uri data)1465     public final boolean hasDataAuthority(Uri data) {
1466         return matchDataAuthority(data) >= 0;
1467     }
1468 
1469     /** @hide */
1470     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasDataAuthority(AuthorityEntry auth)1471     public final boolean hasDataAuthority(AuthorityEntry auth) {
1472         if (mDataAuthorities == null) {
1473             return false;
1474         }
1475         final int numDataAuthorities = mDataAuthorities.size();
1476         for (int i = 0; i < numDataAuthorities; i++) {
1477             if (mDataAuthorities.get(i).match(auth)) {
1478                 return true;
1479             }
1480         }
1481         return false;
1482     }
1483 
1484     /**
1485      * Return an iterator over the filter's data authorities.
1486      */
authoritiesIterator()1487     public final Iterator<AuthorityEntry> authoritiesIterator() {
1488         return mDataAuthorities != null ? mDataAuthorities.iterator() : null;
1489     }
1490 
1491     /**
1492      * Add a new Intent data path to match against.  The filter must
1493      * include one or more schemes (via {@link #addDataScheme}) <em>and</em>
1494      * one or more authorities (via {@link #addDataAuthority}) for the
1495      * path to be considered.  If any paths are
1496      * included in the filter, then an Intent's data must match one of
1497      * them.  If no paths are included, then only the scheme/authority must
1498      * match.
1499      *
1500      * <p>The path given here can either be a literal that must directly
1501      * match or match against a prefix, or it can be a simple globbing pattern.
1502      * If the latter, you can use '*' anywhere in the pattern to match zero
1503      * or more instances of the previous character, '.' as a wildcard to match
1504      * any character, and '\' to escape the next character.
1505      *
1506      * @param path Either a raw string that must exactly match the file
1507      * path, or a simple pattern, depending on <var>type</var>.
1508      * @param type Determines how <var>path</var> will be compared to
1509      * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
1510      * {@link PatternMatcher#PATTERN_PREFIX},
1511      * {@link PatternMatcher#PATTERN_SUFFIX}, or
1512      * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
1513      *
1514      * @see #matchData
1515      * @see #addDataScheme
1516      * @see #addDataAuthority
1517      */
addDataPath(String path, int type)1518     public final void addDataPath(String path, int type) {
1519         addDataPath(new PatternMatcher(path.intern(), type));
1520     }
1521 
1522     /** @hide */
addDataPath(PatternMatcher path)1523     public final void addDataPath(PatternMatcher path) {
1524         if (mDataPaths == null) mDataPaths = new ArrayList<PatternMatcher>();
1525         mDataPaths.add(path);
1526     }
1527 
1528     /**
1529      * Return the number of data paths in the filter.
1530      */
countDataPaths()1531     public final int countDataPaths() {
1532         return mDataPaths != null ? mDataPaths.size() : 0;
1533     }
1534 
1535     /**
1536      * Return a data path in the filter.
1537      */
getDataPath(int index)1538     public final PatternMatcher getDataPath(int index) {
1539         return mDataPaths.get(index);
1540     }
1541 
1542     /**
1543      * Is the given data path included in the filter?  Note that if the
1544      * filter does not include any paths, false will <em>always</em> be
1545      * returned.
1546      *
1547      * @param data The data path to look for.  This is without the scheme
1548      *             prefix.
1549      *
1550      * @return True if the data string matches a path listed in the
1551      *         filter.
1552      */
hasDataPath(String data)1553     public final boolean hasDataPath(String data) {
1554         return hasDataPath(data, false);
1555     }
1556 
1557     /**
1558      * Variant of {@link #hasDataPath(String)} that supports wildcards on the provided scheme, host,
1559      * and path.
1560      * @param wildcardSupported if true, will allow parameters to use wildcards
1561      */
hasDataPath(String data, boolean wildcardSupported)1562     private boolean hasDataPath(String data, boolean wildcardSupported) {
1563         if (mDataPaths == null) {
1564             return false;
1565         }
1566         if (wildcardSupported && WILDCARD_PATH.equals(data)) {
1567             return true;
1568         }
1569         final int numDataPaths = mDataPaths.size();
1570         for (int i = 0; i < numDataPaths; i++) {
1571             final PatternMatcher pe = mDataPaths.get(i);
1572             if (pe.match(data)) {
1573                 return true;
1574             }
1575         }
1576         return false;
1577     }
1578 
1579     /** @hide */
1580     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasDataPath(PatternMatcher path)1581     public final boolean hasDataPath(PatternMatcher path) {
1582         if (mDataPaths == null) {
1583             return false;
1584         }
1585         final int numDataPaths = mDataPaths.size();
1586         for (int i = 0; i < numDataPaths; i++) {
1587             final PatternMatcher pe = mDataPaths.get(i);
1588             if (pe.getType() == path.getType() && pe.getPath().equals(path.getPath())) {
1589                 return true;
1590             }
1591         }
1592         return false;
1593     }
1594 
1595     /**
1596      * Return an iterator over the filter's data paths.
1597      */
pathsIterator()1598     public final Iterator<PatternMatcher> pathsIterator() {
1599         return mDataPaths != null ? mDataPaths.iterator() : null;
1600     }
1601 
1602     /**
1603      * Add a new URI relative filter group to match against the Intent data.  The
1604      * intent filter must include one or more schemes (via {@link #addDataScheme})
1605      * <em>and</em> one or more authorities (via {@link #addDataAuthority}) for
1606      * the group to be considered.
1607      *
1608      * <p>Groups will be matched in the order they were added and matching will only
1609      * be done if no data paths match or if none are included. If both data paths and
1610      * groups are not included, then only the scheme/authority must match.</p>
1611      *
1612      * @param group A {@link UriRelativeFilterGroup} to match the URI.
1613      *
1614      * @see UriRelativeFilterGroup
1615      * @see #matchData
1616      * @see #addDataScheme
1617      * @see #addDataAuthority
1618      */
1619     @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
addUriRelativeFilterGroup(@onNull UriRelativeFilterGroup group)1620     public final void addUriRelativeFilterGroup(@NonNull UriRelativeFilterGroup group) {
1621         Objects.requireNonNull(group);
1622         if (mUriRelativeFilterGroups == null) {
1623             mUriRelativeFilterGroups = new ArrayList<>();
1624         }
1625         mUriRelativeFilterGroups.add(group);
1626     }
1627 
1628     /**
1629      * Return the number of URI relative filter groups in the intent filter.
1630      */
1631     @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
countUriRelativeFilterGroups()1632     public final int countUriRelativeFilterGroups() {
1633         return mUriRelativeFilterGroups == null ? 0 : mUriRelativeFilterGroups.size();
1634     }
1635 
1636     /**
1637      * Return a URI relative filter group in the intent filter.
1638      *
1639      * <p>Note: use of this method will result in a NullPointerException
1640      * if no groups exists for this intent filter.</p>
1641      *
1642      * @param index index of the element to return
1643      * @throws IndexOutOfBoundsException if index is out of range
1644      */
1645     @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
1646     @NonNull
getUriRelativeFilterGroup(int index)1647     public final UriRelativeFilterGroup getUriRelativeFilterGroup(int index) {
1648         return mUriRelativeFilterGroups.get(index);
1649     }
1650 
1651     /**
1652      * Removes all existing URI relative filter groups in the intent filter.
1653      */
1654     @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
clearUriRelativeFilterGroups()1655     public final void clearUriRelativeFilterGroups() {
1656         mUriRelativeFilterGroups = null;
1657     }
1658 
1659     /**
1660      * Match this intent filter against the given Intent data.  This ignores
1661      * the data scheme -- unlike {@link #matchData}, the authority will match
1662      * regardless of whether there is a matching scheme.
1663      *
1664      * @param data The data whose authority is being looked for.
1665      *
1666      * @return Returns either {@link #MATCH_CATEGORY_HOST},
1667      * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}.
1668      */
matchDataAuthority(Uri data)1669     public final int matchDataAuthority(Uri data) {
1670         return matchDataAuthority(data, false);
1671     }
1672 
1673     /**
1674      * Variant of {@link #matchDataAuthority(Uri)} that allows wildcard matching of the provided
1675      * authority.
1676      *
1677      * @param wildcardSupported if true, will allow parameters to use wildcards
1678      * @hide
1679      */
matchDataAuthority(Uri data, boolean wildcardSupported)1680     public final int matchDataAuthority(Uri data, boolean wildcardSupported) {
1681         if (data == null || mDataAuthorities == null) {
1682             return NO_MATCH_DATA;
1683         }
1684         final int numDataAuthorities = mDataAuthorities.size();
1685         for (int i = 0; i < numDataAuthorities; i++) {
1686             final AuthorityEntry ae = mDataAuthorities.get(i);
1687             int match = ae.match(data, wildcardSupported);
1688             if (match >= 0) {
1689                 return match;
1690             }
1691         }
1692         return NO_MATCH_DATA;
1693     }
1694 
1695     /**
1696      * Match this filter against an Intent's data (type, scheme and path). If
1697      * the filter does not specify any types and does not specify any
1698      * schemes/paths, the match will only succeed if the intent does not
1699      * also specify a type or data.  If the filter does not specify any schemes,
1700      * it will implicitly match intents with no scheme, or the schemes "content:"
1701      * or "file:" (basically performing a MIME-type only match).  If the filter
1702      * does not specify any MIME types, the Intent also must not specify a MIME
1703      * type.
1704      *
1705      * <p>Be aware that to match against an authority, you must also specify a base
1706      * scheme the authority is in.  To match against a data path, both a scheme
1707      * and authority must be specified.  If the filter does not specify any
1708      * types or schemes that it matches against, it is considered to be empty
1709      * (any authority or data path given is ignored, as if it were empty as
1710      * well).
1711      *
1712      * <p><em>Note: MIME type, Uri scheme, and host name matching in the
1713      * Android framework is case-sensitive, unlike the formal RFC definitions.
1714      * As a result, you should always write these elements with lower case letters,
1715      * and normalize any MIME types or Uris you receive from
1716      * outside of Android to ensure these elements are lower case before
1717      * supplying them here.</em></p>
1718      *
1719      * @param type The desired data type to look for, as returned by
1720      *             Intent.resolveType().
1721      * @param scheme The desired data scheme to look for, as returned by
1722      *               Intent.getScheme().
1723      * @param data The full data string to match against, as supplied in
1724      *             Intent.data.
1725      *
1726      * @return Returns either a valid match constant (a combination of
1727      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
1728      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match
1729      * or {@link #NO_MATCH_DATA} if the scheme/path didn't match.
1730      *
1731      * @see #match
1732      */
matchData(String type, String scheme, Uri data)1733     public final int matchData(String type, String scheme, Uri data) {
1734         return matchData(type, scheme, data, false);
1735     }
1736 
1737     /**
1738      * Variant of {@link #matchData(String, String, Uri)} that allows wildcard matching
1739      * of the provided type, scheme, host and path parameters.
1740      * @param wildcardSupported if true, will allow parameters to use wildcards
1741      */
matchData(String type, String scheme, Uri data, boolean wildcardSupported)1742     private int matchData(String type, String scheme, Uri data, boolean wildcardSupported) {
1743         final boolean wildcardWithMimegroups = wildcardSupported && countMimeGroups() != 0;
1744         final List<String> types = mDataTypes;
1745         final ArrayList<String> schemes = mDataSchemes;
1746 
1747         int match = MATCH_CATEGORY_EMPTY;
1748 
1749         if (!wildcardWithMimegroups && types == null && schemes == null) {
1750             return ((type == null && data == null)
1751                 ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
1752         }
1753 
1754         if (schemes != null) {
1755             if (schemes.contains(scheme != null ? scheme : "")
1756                     || wildcardSupported && WILDCARD.equals(scheme)) {
1757                 match = MATCH_CATEGORY_SCHEME;
1758             } else {
1759                 return NO_MATCH_DATA;
1760             }
1761 
1762             final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
1763             if (schemeSpecificParts != null && data != null) {
1764                 match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart(), wildcardSupported)
1765                         ? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA;
1766             }
1767             if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
1768                 // If there isn't any matching ssp, we need to match an authority.
1769                 final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
1770                 if (authorities != null) {
1771                     int authMatch = matchDataAuthority(data, wildcardSupported);
1772                     if (authMatch >= 0) {
1773                         final ArrayList<PatternMatcher> paths = mDataPaths;
1774                         final ArrayList<UriRelativeFilterGroup> groups = mUriRelativeFilterGroups;
1775                         if (Flags.relativeReferenceIntentFilters()) {
1776                             if (paths == null && groups == null) {
1777                                 match = authMatch;
1778                             } else if (hasDataPath(data.getPath(), wildcardSupported)
1779                                     || matchRelRefGroups(data)) {
1780                                 match = MATCH_CATEGORY_PATH;
1781                             } else {
1782                                 return NO_MATCH_DATA;
1783                             }
1784                         } else {
1785                             if (paths == null) {
1786                                 match = authMatch;
1787                             } else if (hasDataPath(data.getPath(), wildcardSupported)) {
1788                                 match = MATCH_CATEGORY_PATH;
1789                             } else {
1790                                 return NO_MATCH_DATA;
1791                             }
1792                         }
1793                     } else {
1794                         return NO_MATCH_DATA;
1795                     }
1796                 }
1797             }
1798             // If neither an ssp nor an authority matched, we're done.
1799             if (match == NO_MATCH_DATA) {
1800                 return NO_MATCH_DATA;
1801             }
1802         } else {
1803             // Special case: match either an Intent with no data URI,
1804             // or with a scheme: URI.  This is to give a convenience for
1805             // the common case where you want to deal with data in a
1806             // content provider, which is done by type, and we don't want
1807             // to force everyone to say they handle content: or file: URIs.
1808             if (scheme != null && !"".equals(scheme)
1809                     && !"content".equals(scheme)
1810                     && !"file".equals(scheme)
1811                     && !(wildcardSupported && WILDCARD.equals(scheme))) {
1812                 return NO_MATCH_DATA;
1813             }
1814         }
1815 
1816         if (wildcardWithMimegroups) {
1817             return MATCH_CATEGORY_TYPE;
1818         } else if (types != null) {
1819             if (findMimeType(type)) {
1820                 match = MATCH_CATEGORY_TYPE;
1821             } else {
1822                 return NO_MATCH_TYPE;
1823             }
1824         } else {
1825             // If no MIME types are specified, then we will only match against
1826             // an Intent that does not have a MIME type.
1827             if (type != null) {
1828                 return NO_MATCH_TYPE;
1829             }
1830         }
1831 
1832         return match + MATCH_ADJUSTMENT_NORMAL;
1833     }
1834 
matchRelRefGroups(Uri data)1835     private boolean matchRelRefGroups(Uri data) {
1836         if (mUriRelativeFilterGroups == null) {
1837             return false;
1838         }
1839         return UriRelativeFilterGroup.matchGroupsToUri(mUriRelativeFilterGroups, data);
1840     }
1841 
1842     /**
1843      * Add a new Intent category to match against.  The semantics of
1844      * categories is the opposite of actions -- an Intent includes the
1845      * categories that it requires, all of which must be included in the
1846      * filter in order to match.  In other words, adding a category to the
1847      * filter has no impact on matching unless that category is specified in
1848      * the intent.
1849      *
1850      * @param category Name of category to match, such as Intent.CATEGORY_EMBED.
1851      */
addCategory(String category)1852     public final void addCategory(String category) {
1853         if (mCategories == null) mCategories = new ArrayList<String>();
1854         if (!mCategories.contains(category)) {
1855             mCategories.add(category.intern());
1856         }
1857     }
1858 
1859     /**
1860      * Return the number of categories in the filter.
1861      */
countCategories()1862     public final int countCategories() {
1863         return mCategories != null ? mCategories.size() : 0;
1864     }
1865 
1866     /**
1867      * Return a category in the filter.
1868      */
getCategory(int index)1869     public final String getCategory(int index) {
1870         return mCategories.get(index);
1871     }
1872 
1873     /**
1874      * Is the given category included in the filter?
1875      *
1876      * @param category The category that the filter supports.
1877      *
1878      * @return True if the category is explicitly mentioned in the filter.
1879      */
hasCategory(String category)1880     public final boolean hasCategory(String category) {
1881         return mCategories != null && mCategories.contains(category);
1882     }
1883 
1884     /**
1885      * Return an iterator over the filter's categories.
1886      *
1887      * @return Iterator if this filter has categories or {@code null} if none.
1888      */
categoriesIterator()1889     public final Iterator<String> categoriesIterator() {
1890         return mCategories != null ? mCategories.iterator() : null;
1891     }
1892 
1893     /**
1894      * Match this filter against an Intent's categories.  Each category in
1895      * the Intent must be specified by the filter; if any are not in the
1896      * filter, the match fails.
1897      *
1898      * @param categories The categories included in the intent, as returned by
1899      *                   Intent.getCategories().
1900      *
1901      * @return If all categories match (success), null; else the name of the
1902      *         first category that didn't match.
1903      */
matchCategories(Set<String> categories)1904     public final String matchCategories(Set<String> categories) {
1905         if (categories == null) {
1906             return null;
1907         }
1908 
1909         Iterator<String> it = categories.iterator();
1910 
1911         if (mCategories == null) {
1912             return it.hasNext() ? it.next() : null;
1913         }
1914 
1915         while (it.hasNext()) {
1916             final String category = it.next();
1917             if (!mCategories.contains(category)) {
1918                 return category;
1919             }
1920         }
1921 
1922         return null;
1923     }
1924 
1925     /**
1926      * Match this filter against an Intent's extras. An intent must
1927      * have all the extras specified by the filter with the same values,
1928      * for the match to succeed.
1929      *
1930      * <p> An intent con have other extras in addition to those specified
1931      * by the filter and it would not affect whether the match succeeds or not.
1932      *
1933      * @param extras The extras included in the intent, as returned by
1934      *               Intent.getExtras().
1935      *
1936      * @return If all extras match (success), null; else the name of the
1937      *         first extra that didn't match.
1938      *
1939      * @hide
1940      */
matchExtras(@ullable Bundle extras)1941     private String matchExtras(@Nullable Bundle extras) {
1942         if (mExtras == null) {
1943             return null;
1944         }
1945         final Set<String> keys = mExtras.keySet();
1946         for (String key : keys) {
1947             if (extras == null) {
1948                 return key;
1949             }
1950             final Object value = mExtras.get(key);
1951             final Object otherValue = extras.get(key);
1952             if (otherValue == null || value.getClass() != otherValue.getClass()
1953                     || !Objects.deepEquals(value, otherValue)) {
1954                 return key;
1955             }
1956         }
1957         return null;
1958     }
1959 
1960     /**
1961      * Add a new extra name and value to match against. If an extra is included in the filter,
1962      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
1963      * match.
1964      *
1965      * <p> Subsequent calls to this method with the same {@code name} will override any previously
1966      * set {@code value}.
1967      *
1968      * @param name the name of the extra to match against.
1969      * @param value the value of the extra to match against.
1970      *
1971      * @hide
1972      */
addExtra(@onNull String name, int value)1973     public final void addExtra(@NonNull String name, int value) {
1974         Objects.requireNonNull(name);
1975         if (mExtras == null) {
1976             mExtras = new PersistableBundle();
1977         }
1978         mExtras.putInt(name, value);
1979     }
1980 
1981     /**
1982      * Return the value of the extra with {@code name} included in the filter.
1983      *
1984      * @return the value that was previously set using {@link #addExtra(String, int)} or
1985      *         {@code 0} if no value has been set.
1986      * @hide
1987      */
getIntExtra(@onNull String name)1988     public final int getIntExtra(@NonNull String name) {
1989         Objects.requireNonNull(name);
1990         return mExtras == null ? 0 : mExtras.getInt(name);
1991     }
1992 
1993     /**
1994      * Add a new extra name and value to match against. If an extra is included in the filter,
1995      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
1996      * match.
1997      *
1998      * <p> Subsequent calls to this method with the same {@code name} will override any previously
1999      * set {@code value}.
2000      *
2001      * @param name the name of the extra to match against.
2002      * @param value the value of the extra to match against.
2003      *
2004      * @hide
2005      */
addExtra(@onNull String name, @NonNull int[] value)2006     public final void addExtra(@NonNull String name, @NonNull int[] value) {
2007         Objects.requireNonNull(name);
2008         Objects.requireNonNull(value);
2009         if (mExtras == null) {
2010             mExtras = new PersistableBundle();
2011         }
2012         mExtras.putIntArray(name, value);
2013     }
2014 
2015     /**
2016      * Return the value of the extra with {@code name} included in the filter.
2017      *
2018      * @return the value that was previously set using {@link #addExtra(String, int[])} or
2019      *         an empty int array if no value has been set.
2020      * @hide
2021      */
2022     @NonNull
getIntArrayExtra(@onNull String name)2023     public final int[] getIntArrayExtra(@NonNull String name) {
2024         Objects.requireNonNull(name);
2025         return mExtras == null ? EMPTY_INT_ARRAY : mExtras.getIntArray(name);
2026     }
2027 
2028     /**
2029      * Add a new extra name and value to match against. If an extra is included in the filter,
2030      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2031      * match.
2032      *
2033      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2034      * set {@code value}.
2035      *
2036      * @param name the name of the extra to match against.
2037      * @param value the value of the extra to match against.
2038      *
2039      * @hide
2040      */
addExtra(@onNull String name, long value)2041     public final void addExtra(@NonNull String name, long value) {
2042         Objects.requireNonNull(name);
2043         if (mExtras == null) {
2044             mExtras = new PersistableBundle();
2045         }
2046         mExtras.putLong(name, value);
2047     }
2048 
2049     /**
2050      * Return the value of the extra with {@code name} included in the filter.
2051      *
2052      * @return the value that was previously set using {@link #addExtra(String, long)} or
2053      *         {@code 0L} if no value has been set.
2054      * @hide
2055      */
getLongExtra(@onNull String name)2056     public final long getLongExtra(@NonNull String name) {
2057         Objects.requireNonNull(name);
2058         return mExtras == null ? 0L : mExtras.getLong(name);
2059     }
2060 
2061     /**
2062      * Add a new extra name and value to match against. If an extra is included in the filter,
2063      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2064      * match.
2065      *
2066      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2067      * set {@code value}.
2068      *
2069      * @param name the name of the extra to match against.
2070      * @param value the value of the extra to match against.
2071      *
2072      * @hide
2073      */
addExtra(@onNull String name, @NonNull long[] value)2074     public final void addExtra(@NonNull String name, @NonNull long[] value) {
2075         Objects.requireNonNull(name);
2076         Objects.requireNonNull(value);
2077         if (mExtras == null) {
2078             mExtras = new PersistableBundle();
2079         }
2080         mExtras.putLongArray(name, value);
2081     }
2082 
2083     /**
2084      * Return the value of the extra with {@code name} included in the filter.
2085      *
2086      * @return the value that was previously set using {@link #addExtra(String, long[])} or
2087      *         an empty long array if no value has been set.
2088      * @hide
2089      */
2090     @NonNull
getLongArrayExtra(@onNull String name)2091     public final long[] getLongArrayExtra(@NonNull String name) {
2092         Objects.requireNonNull(name);
2093         return mExtras == null ? EMPTY_LONG_ARRAY : mExtras.getLongArray(name);
2094     }
2095 
2096     /**
2097      * Add a new extra name and value to match against. If an extra is included in the filter,
2098      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2099      * match.
2100      *
2101      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2102      * set {@code value}.
2103      *
2104      * @param name the name of the extra to match against.
2105      * @param value the value of the extra to match against.
2106      *
2107      * @hide
2108      */
addExtra(@onNull String name, double value)2109     public final void addExtra(@NonNull String name, double value) {
2110         Objects.requireNonNull(name);
2111         if (mExtras == null) {
2112             mExtras = new PersistableBundle();
2113         }
2114         mExtras.putDouble(name, value);
2115     }
2116 
2117     /**
2118      * Return the value of the extra with {@code name} included in the filter.
2119      *
2120      * @return the value that was previously set using {@link #addExtra(String, double)} or
2121      *         {@code 0.0} if no value has been set.
2122      * @hide
2123      */
2124     @NonNull
getDoubleExtra(@onNull String name)2125     public final double getDoubleExtra(@NonNull String name) {
2126         Objects.requireNonNull(name);
2127         return mExtras == null ? 0.0 : mExtras.getDouble(name);
2128     }
2129 
2130     /**
2131      * Add a new extra name and value to match against. If an extra is included in the filter,
2132      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2133      * match.
2134      *
2135      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2136      * set {@code value}.
2137      *
2138      * @param name the name of the extra to match against.
2139      * @param value the value of the extra to match against.
2140      *
2141      * @hide
2142      */
addExtra(@onNull String name, @NonNull double[] value)2143     public final void addExtra(@NonNull String name, @NonNull double[] value) {
2144         Objects.requireNonNull(name);
2145         Objects.requireNonNull(value);
2146         if (mExtras == null) {
2147             mExtras = new PersistableBundle();
2148         }
2149         mExtras.putDoubleArray(name, value);
2150     }
2151 
2152     /**
2153      * Return the value of the extra with {@code name} included in the filter.
2154      *
2155      * @return the value that was previously set using {@link #addExtra(String, double[])} or
2156      *         an empty double array if no value has been set.
2157      * @hide
2158      */
2159     @NonNull
getDoubleArrayExtra(@onNull String name)2160     public final double[] getDoubleArrayExtra(@NonNull String name) {
2161         Objects.requireNonNull(name);
2162         return mExtras == null ? EMPTY_DOUBLE_ARRAY : mExtras.getDoubleArray(name);
2163     }
2164 
2165     /**
2166      * Add a new extra name and value to match against. If an extra is included in the filter,
2167      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2168      * match.
2169      *
2170      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2171      * set {@code value}.
2172      *
2173      * @param name the name of the extra to match against.
2174      * @param value the value of the extra to match against.
2175      *
2176      * @hide
2177      */
addExtra(@onNull String name, @NonNull String value)2178     public final void addExtra(@NonNull String name, @NonNull String value) {
2179         Objects.requireNonNull(name);
2180         Objects.requireNonNull(value);
2181         if (mExtras == null) {
2182             mExtras = new PersistableBundle();
2183         }
2184         mExtras.putString(name, value);
2185     }
2186 
2187     /**
2188      * Return the value of the extra with {@code name} included in the filter.
2189      *
2190      * @return the value that was previously set using {@link #addExtra(String, String)} or a
2191      *         {@code null} if no value has been set.
2192      * @hide
2193      */
2194     @Nullable
getStringExtra(@onNull String name)2195     public final String getStringExtra(@NonNull String name) {
2196         Objects.requireNonNull(name);
2197         return mExtras == null ? null : mExtras.getString(name);
2198     }
2199 
2200     /**
2201      * Add a new extra name and value to match against. If an extra is included in the filter,
2202      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2203      * match.
2204      *
2205      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2206      * set {@code value}.
2207      *
2208      * @param name the name of the extra to match against.
2209      * @param value the value of the extra to match against.
2210      *
2211      * @hide
2212      */
addExtra(@onNull String name, @NonNull String[] value)2213     public final void addExtra(@NonNull String name, @NonNull String[] value) {
2214         Objects.requireNonNull(name);
2215         Objects.requireNonNull(value);
2216         if (mExtras == null) {
2217             mExtras = new PersistableBundle();
2218         }
2219         mExtras.putStringArray(name, value);
2220     }
2221 
2222     /**
2223      * Return the value of the extra with {@code name} included in the filter.
2224      *
2225      * @return the value that was previously set using {@link #addExtra(String, String[])} or
2226      *         an empty string array if no value has been set.
2227      * @hide
2228      */
2229     @NonNull
getStringArrayExtra(@onNull String name)2230     public final String[] getStringArrayExtra(@NonNull String name) {
2231         Objects.requireNonNull(name);
2232         return mExtras == null ? EMPTY_STRING_ARRAY : mExtras.getStringArray(name);
2233     }
2234 
2235     /**
2236      * Add a new extra name and value to match against. If an extra is included in the filter,
2237      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2238      * match.
2239      *
2240      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2241      * set {@code value}.
2242      *
2243      * @param name the name of the extra to match against.
2244      * @param value the value of the extra to match against.
2245      *
2246      * @hide
2247      */
addExtra(@onNull String name, boolean value)2248     public final void addExtra(@NonNull String name, boolean value) {
2249         Objects.requireNonNull(name);
2250         if (mExtras == null) {
2251             mExtras = new PersistableBundle();
2252         }
2253         mExtras.putBoolean(name, value);
2254     }
2255 
2256     /**
2257      * Return the value of the extra with {@code name} included in the filter.
2258      *
2259      * @return the value that was previously set using {@link #addExtra(String, boolean)} or
2260      *         {@code false} if no value has been set.
2261      * @hide
2262      */
getBooleanExtra(@onNull String name)2263     public final boolean getBooleanExtra(@NonNull String name) {
2264         Objects.requireNonNull(name);
2265         return mExtras == null ? false : mExtras.getBoolean(name);
2266     }
2267 
2268     /**
2269      * Add a new extra name and value to match against. If an extra is included in the filter,
2270      * then an Intent must have an extra with the same {@code name} and {@code value} for it to
2271      * match.
2272      *
2273      * <p> Subsequent calls to this method with the same {@code name} will override any previously
2274      * set {@code value}.
2275      *
2276      * @param name the name of the extra to match against.
2277      * @param value the value of the extra to match against.
2278      *
2279      * @hide
2280      */
addExtra(@onNull String name, @NonNull boolean[] value)2281     public final void addExtra(@NonNull String name, @NonNull boolean[] value) {
2282         Objects.requireNonNull(name);
2283         Objects.requireNonNull(value);
2284         if (mExtras == null) {
2285             mExtras = new PersistableBundle();
2286         }
2287         mExtras.putBooleanArray(name, value);
2288     }
2289 
2290     /**
2291      * Return the value of the extra with {@code name} included in the filter.
2292      *
2293      * @return the value that was previously set using {@link #addExtra(String, boolean[])} or
2294      *         an empty boolean array if no value has been set.
2295      * @hide
2296      */
2297     @NonNull
getBooleanArrayExtra(@onNull String name)2298     public final boolean[] getBooleanArrayExtra(@NonNull String name) {
2299         Objects.requireNonNull(name);
2300         return mExtras == null ? EMPTY_BOOLEAN_ARRAY : mExtras.getBooleanArray(name);
2301     }
2302 
2303     /**
2304      * Returns whether or not an extra with {@code name} is included in the filter.
2305      *
2306      * @return {@code true} if an extra with {@code name} is included in the filter.
2307      *         Otherwise {@code false}.
2308      * @hide
2309      */
hasExtra(@onNull String name)2310     public final boolean hasExtra(@NonNull String name) {
2311         Objects.requireNonNull(name);
2312         return mExtras == null ? false : mExtras.containsKey(name);
2313     }
2314 
2315     /**
2316      * Set the intent extras to match against. For this filter to match an
2317      * intent, it must contain all the {@code extras} set.
2318      *
2319      * <p> Subsequent calls to this method  will override any previously set extras.
2320      *
2321      * @param extras The intent extras to match against.
2322      * @hide
2323      */
setExtras(@onNull PersistableBundle extras)2324     public final void setExtras(@NonNull PersistableBundle extras) {
2325         mExtras = extras;
2326     }
2327 
2328     /**
2329      * Return the intent extras included in this filter.
2330      *
2331      * @return the extras that were previously set using {@link #setExtras(PersistableBundle)} or
2332      *         an empty {@link PersistableBundle} object if no extras were set.
2333      * @hide
2334      */
getExtras()2335     public final @NonNull PersistableBundle getExtras() {
2336         return mExtras == null ? new PersistableBundle() : mExtras;
2337     }
2338 
2339     /**
2340      * Return a {@link Predicate} which tests whether this filter matches the
2341      * given <var>intent</var>.
2342      * <p>
2343      * The intent's type will always be tested using a simple
2344      * {@link Intent#getType()} check. To instead perform a detailed type
2345      * resolution before matching, use
2346      * {@link #asPredicateWithTypeResolution(ContentResolver)}.
2347      */
asPredicate()2348     public @NonNull Predicate<Intent> asPredicate() {
2349         return i -> match(null, i, false, TAG) >= 0;
2350     }
2351 
2352     /**
2353      * Return a {@link Predicate} which tests whether this filter matches the
2354      * given <var>intent</var>.
2355      * <p>
2356      * The intent's type will always be resolved by calling
2357      * {@link Intent#resolveType(ContentResolver)} before matching.
2358      *
2359      * @param resolver to be used when calling
2360      *            {@link Intent#resolveType(ContentResolver)} before matching.
2361      */
asPredicateWithTypeResolution( @onNull ContentResolver resolver)2362     public @NonNull Predicate<Intent> asPredicateWithTypeResolution(
2363             @NonNull ContentResolver resolver) {
2364         Objects.requireNonNull(resolver);
2365         return i -> match(resolver, i, true, TAG) >= 0;
2366     }
2367 
2368     /**
2369      * Test whether this filter matches the given <var>intent</var>.
2370      *
2371      * @param intent The Intent to compare against.
2372      * @param resolve If true, the intent's type will be resolved by calling
2373      *                Intent.resolveType(); otherwise a simple match against
2374      *                Intent.type will be performed.
2375      * @param logTag Tag to use in debugging messages.
2376      *
2377      * @return Returns either a valid match constant (a combination of
2378      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
2379      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
2380      * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
2381      * {@link #NO_MATCH_ACTION} if the action didn't match, or
2382      * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
2383      *
2384      * @see #match(String, String, String, android.net.Uri , Set, String)
2385      */
match(ContentResolver resolver, Intent intent, boolean resolve, String logTag)2386     public final int match(ContentResolver resolver, Intent intent,
2387             boolean resolve, String logTag) {
2388         String type = resolve ? intent.resolveType(resolver) : intent.getType();
2389         return match(intent.getAction(), type, intent.getScheme(),
2390                      intent.getData(), intent.getCategories(), logTag,
2391                      false /* supportWildcards */, null /* ignoreActions */,
2392                      intent.getExtras());
2393     }
2394 
2395     /**
2396      * Test whether this filter matches the given intent data.  A match is
2397      * only successful if the actions and categories in the Intent match
2398      * against the filter, as described in {@link IntentFilter}; in that case,
2399      * the match result returned will be as per {@link #matchData}.
2400      *
2401      * @param action The intent action to match against (Intent.getAction).
2402      * @param type The intent type to match against (Intent.resolveType()).
2403      * @param scheme The data scheme to match against (Intent.getScheme()).
2404      * @param data The data URI to match against (Intent.getData()).
2405      * @param categories The categories to match against
2406      *                   (Intent.getCategories()).
2407      * @param logTag Tag to use in debugging messages.
2408      *
2409      * @return Returns either a valid match constant (a combination of
2410      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
2411      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
2412      * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
2413      * {@link #NO_MATCH_ACTION} if the action didn't match, or
2414      * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
2415      *
2416      * @see #matchData
2417      * @see Intent#getAction
2418      * @see Intent#resolveType
2419      * @see Intent#getScheme
2420      * @see Intent#getData
2421      * @see Intent#getCategories
2422      */
match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag)2423     public final int match(String action, String type, String scheme,
2424             Uri data, Set<String> categories, String logTag) {
2425         return match(action, type, scheme, data, categories, logTag, false /*supportWildcards*/,
2426                 null /*ignoreActions*/);
2427     }
2428 
2429     /**
2430      * Variant of {@link #match(ContentResolver, Intent, boolean, String)} that supports wildcards
2431      * in the action, type, scheme, host and path.
2432      * @param supportWildcards if true, will allow supported parameters to use wildcards to match
2433      *                         this filter
2434      * @param ignoreActions a collection of actions that, if not null should be ignored and not used
2435      *                      if provided as the matching action or the set of actions defined by this
2436      *                      filter
2437      * @hide
2438      */
match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag, boolean supportWildcards, @Nullable Collection<String> ignoreActions)2439     public final int match(String action, String type, String scheme,
2440             Uri data, Set<String> categories, String logTag, boolean supportWildcards,
2441             @Nullable Collection<String> ignoreActions) {
2442         return match(action, type, scheme, data, categories, logTag, supportWildcards,
2443                 ignoreActions, null /* extras */);
2444     }
2445 
2446     /**
2447      * Variant of {@link #match(String, String, String, Uri, Set, String, boolean, Collection)}
2448      * that supports matching the extra values in the intent.
2449      *
2450      * @hide
2451      */
match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag, boolean supportWildcards, @Nullable Collection<String> ignoreActions, @Nullable Bundle extras)2452     public final int match(String action, String type, String scheme,
2453             Uri data, Set<String> categories, String logTag, boolean supportWildcards,
2454             @Nullable Collection<String> ignoreActions, @Nullable Bundle extras) {
2455         if (action != null && !matchAction(action, supportWildcards, ignoreActions)) {
2456             if (false) Log.v(
2457                 logTag, "No matching action " + action + " for " + this);
2458             return NO_MATCH_ACTION;
2459         }
2460 
2461         int dataMatch = matchData(type, scheme, data, supportWildcards);
2462         if (dataMatch < 0) {
2463             if (false) {
2464                 if (dataMatch == NO_MATCH_TYPE) {
2465                     Log.v(logTag, "No matching type " + type
2466                           + " for " + this);
2467                 }
2468                 if (dataMatch == NO_MATCH_DATA) {
2469                     Log.v(logTag, "No matching scheme/path " + data
2470                           + " for " + this);
2471                 }
2472             }
2473             return dataMatch;
2474         }
2475 
2476         String categoryMismatch = matchCategories(categories);
2477         if (categoryMismatch != null) {
2478             if (false) {
2479                 Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);
2480             }
2481             return NO_MATCH_CATEGORY;
2482         }
2483 
2484         String extraMismatch = matchExtras(extras);
2485         if (extraMismatch != null) {
2486             if (false) {
2487                 Log.v(logTag, "Mismatch for extra key " + extraMismatch + " for " + this);
2488             }
2489             return NO_MATCH_EXTRAS;
2490         }
2491 
2492         // It would be nice to treat container activities as more
2493         // important than ones that can be embedded, but this is not the way...
2494         if (false) {
2495             if (categories != null) {
2496                 dataMatch -= mCategories.size() - categories.size();
2497             }
2498         }
2499 
2500         return dataMatch;
2501     }
2502 
2503     /**
2504      * Write the contents of the IntentFilter as an XML stream.
2505      */
writeToXml(XmlSerializer serializer)2506     public void writeToXml(XmlSerializer serializer) throws IOException {
2507 
2508         if (getAutoVerify()) {
2509             serializer.attribute(null, AUTO_VERIFY_STR, Boolean.toString(true));
2510         }
2511 
2512         int N = countActions();
2513         for (int i=0; i<N; i++) {
2514             serializer.startTag(null, ACTION_STR);
2515             serializer.attribute(null, NAME_STR, mActions.valueAt(i));
2516             serializer.endTag(null, ACTION_STR);
2517         }
2518         N = countCategories();
2519         for (int i=0; i<N; i++) {
2520             serializer.startTag(null, CAT_STR);
2521             serializer.attribute(null, NAME_STR, mCategories.get(i));
2522             serializer.endTag(null, CAT_STR);
2523         }
2524         writeDataTypesToXml(serializer);
2525         N = countMimeGroups();
2526         for (int i=0; i<N; i++) {
2527             serializer.startTag(null, GROUP_STR);
2528             serializer.attribute(null, NAME_STR, mMimeGroups.get(i));
2529             serializer.endTag(null, GROUP_STR);
2530         }
2531         N = countDataSchemes();
2532         for (int i=0; i<N; i++) {
2533             serializer.startTag(null, SCHEME_STR);
2534             serializer.attribute(null, NAME_STR, mDataSchemes.get(i));
2535             serializer.endTag(null, SCHEME_STR);
2536         }
2537         N = countDataSchemeSpecificParts();
2538         for (int i=0; i<N; i++) {
2539             serializer.startTag(null, SSP_STR);
2540             PatternMatcher pe = mDataSchemeSpecificParts.get(i);
2541             switch (pe.getType()) {
2542                 case PatternMatcher.PATTERN_LITERAL:
2543                     serializer.attribute(null, LITERAL_STR, pe.getPath());
2544                     break;
2545                 case PatternMatcher.PATTERN_PREFIX:
2546                     serializer.attribute(null, PREFIX_STR, pe.getPath());
2547                     break;
2548                 case PatternMatcher.PATTERN_SIMPLE_GLOB:
2549                     serializer.attribute(null, SGLOB_STR, pe.getPath());
2550                     break;
2551                 case PatternMatcher.PATTERN_ADVANCED_GLOB:
2552                     serializer.attribute(null, AGLOB_STR, pe.getPath());
2553                     break;
2554                 case PatternMatcher.PATTERN_SUFFIX:
2555                     serializer.attribute(null, SUFFIX_STR, pe.getPath());
2556                     break;
2557             }
2558             serializer.endTag(null, SSP_STR);
2559         }
2560         N = countDataAuthorities();
2561         for (int i=0; i<N; i++) {
2562             serializer.startTag(null, AUTH_STR);
2563             AuthorityEntry ae = mDataAuthorities.get(i);
2564             serializer.attribute(null, HOST_STR, ae.getHost());
2565             if (ae.getPort() >= 0) {
2566                 serializer.attribute(null, PORT_STR, Integer.toString(ae.getPort()));
2567             }
2568             serializer.endTag(null, AUTH_STR);
2569         }
2570         N = countDataPaths();
2571         for (int i=0; i<N; i++) {
2572             serializer.startTag(null, PATH_STR);
2573             PatternMatcher pe = mDataPaths.get(i);
2574             switch (pe.getType()) {
2575                 case PatternMatcher.PATTERN_LITERAL:
2576                     serializer.attribute(null, LITERAL_STR, pe.getPath());
2577                     break;
2578                 case PatternMatcher.PATTERN_PREFIX:
2579                     serializer.attribute(null, PREFIX_STR, pe.getPath());
2580                     break;
2581                 case PatternMatcher.PATTERN_SIMPLE_GLOB:
2582                     serializer.attribute(null, SGLOB_STR, pe.getPath());
2583                     break;
2584                 case PatternMatcher.PATTERN_ADVANCED_GLOB:
2585                     serializer.attribute(null, AGLOB_STR, pe.getPath());
2586                     break;
2587                 case PatternMatcher.PATTERN_SUFFIX:
2588                     serializer.attribute(null, SUFFIX_STR, pe.getPath());
2589                     break;
2590             }
2591             serializer.endTag(null, PATH_STR);
2592         }
2593         if (mExtras != null) {
2594             serializer.startTag(null, EXTRAS_STR);
2595             try {
2596                 mExtras.saveToXml(serializer);
2597             } catch (XmlPullParserException e) {
2598                 throw new IllegalStateException("Failed to write extras: " + mExtras.toString(), e);
2599             }
2600             serializer.endTag(null, EXTRAS_STR);
2601         }
2602         if (Flags.relativeReferenceIntentFilters()) {
2603             N = countUriRelativeFilterGroups();
2604             for (int i = 0; i < N; i++) {
2605                 mUriRelativeFilterGroups.get(i).writeToXml(serializer);
2606             }
2607         }
2608     }
2609 
2610     /**
2611      * Write data types (both static and dynamic) to XML.
2612      * In implementation we rely on two facts:
2613      * - {@link #mStaticDataTypes} is subsequence of {@link #mDataTypes}
2614      * - both {@link #mStaticDataTypes} and {@link #mDataTypes} does not contain duplicates
2615      */
writeDataTypesToXml(XmlSerializer serializer)2616     private void writeDataTypesToXml(XmlSerializer serializer) throws IOException {
2617         if (mStaticDataTypes == null) {
2618             return;
2619         }
2620 
2621         int i = 0;
2622         for (String staticType: mStaticDataTypes) {
2623             while (!mDataTypes.get(i).equals(staticType)) {
2624                 writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
2625                 i++;
2626             }
2627 
2628             writeDataTypeToXml(serializer, staticType, STATIC_TYPE_STR);
2629             i++;
2630         }
2631 
2632         while (i < mDataTypes.size()) {
2633             writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
2634             i++;
2635         }
2636     }
2637 
writeDataTypeToXml(XmlSerializer serializer, String type, String tag)2638     private void writeDataTypeToXml(XmlSerializer serializer, String type, String tag)
2639             throws IOException {
2640         serializer.startTag(null, tag);
2641 
2642         if (type.indexOf('/') < 0) {
2643             type = type + "/*";
2644         }
2645 
2646         serializer.attribute(null, NAME_STR, type);
2647         serializer.endTag(null, tag);
2648     }
2649 
readFromXml(XmlPullParser parser)2650     public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
2651             IOException {
2652         String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR);
2653         setAutoVerify(TextUtils.isEmpty(autoVerify) ? false : Boolean.getBoolean(autoVerify));
2654 
2655         int outerDepth = parser.getDepth();
2656         int type;
2657         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
2658                && (type != XmlPullParser.END_TAG
2659                        || parser.getDepth() > outerDepth)) {
2660             if (type == XmlPullParser.END_TAG
2661                     || type == XmlPullParser.TEXT) {
2662                 continue;
2663             }
2664 
2665             String tagName = parser.getName();
2666             if (tagName.equals(ACTION_STR)) {
2667                 String name = parser.getAttributeValue(null, NAME_STR);
2668                 if (name != null) {
2669                     addAction(name);
2670                 }
2671             } else if (tagName.equals(CAT_STR)) {
2672                 String name = parser.getAttributeValue(null, NAME_STR);
2673                 if (name != null) {
2674                     addCategory(name);
2675                 }
2676             } else if (tagName.equals(STATIC_TYPE_STR)) {
2677                 String name = parser.getAttributeValue(null, NAME_STR);
2678                 if (name != null) {
2679                     try {
2680                         addDataType(name);
2681                     } catch (MalformedMimeTypeException e) {
2682                     }
2683                 }
2684             } else if (tagName.equals(TYPE_STR)) {
2685                 String name = parser.getAttributeValue(null, NAME_STR);
2686                 if (name != null) {
2687                     try {
2688                         addDynamicDataType(name);
2689                     } catch (MalformedMimeTypeException e) {
2690                     }
2691                 }
2692             } else if (tagName.equals(GROUP_STR)) {
2693                 String name = parser.getAttributeValue(null, NAME_STR);
2694                 if (name != null) {
2695                     addMimeGroup(name);
2696                 }
2697             } else if (tagName.equals(SCHEME_STR)) {
2698                 String name = parser.getAttributeValue(null, NAME_STR);
2699                 if (name != null) {
2700                     addDataScheme(name);
2701                 }
2702             } else if (tagName.equals(SSP_STR)) {
2703                 String ssp = parser.getAttributeValue(null, LITERAL_STR);
2704                 if (ssp != null) {
2705                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_LITERAL);
2706                 } else if ((ssp=parser.getAttributeValue(null, PREFIX_STR)) != null) {
2707                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_PREFIX);
2708                 } else if ((ssp=parser.getAttributeValue(null, SGLOB_STR)) != null) {
2709                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB);
2710                 } else if ((ssp=parser.getAttributeValue(null, AGLOB_STR)) != null) {
2711                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_ADVANCED_GLOB);
2712                 } else if ((ssp=parser.getAttributeValue(null, SUFFIX_STR)) != null) {
2713                     addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SUFFIX);
2714                 }
2715             } else if (tagName.equals(AUTH_STR)) {
2716                 String host = parser.getAttributeValue(null, HOST_STR);
2717                 String port = parser.getAttributeValue(null, PORT_STR);
2718                 if (host != null) {
2719                     addDataAuthority(host, port);
2720                 }
2721             } else if (tagName.equals(PATH_STR)) {
2722                 String path = parser.getAttributeValue(null, LITERAL_STR);
2723                 if (path != null) {
2724                     addDataPath(path, PatternMatcher.PATTERN_LITERAL);
2725                 } else if ((path=parser.getAttributeValue(null, PREFIX_STR)) != null) {
2726                     addDataPath(path, PatternMatcher.PATTERN_PREFIX);
2727                 } else if ((path=parser.getAttributeValue(null, SGLOB_STR)) != null) {
2728                     addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB);
2729                 } else if ((path=parser.getAttributeValue(null, AGLOB_STR)) != null) {
2730                     addDataPath(path, PatternMatcher.PATTERN_ADVANCED_GLOB);
2731                 } else if ((path=parser.getAttributeValue(null, SUFFIX_STR)) != null) {
2732                     addDataPath(path, PatternMatcher.PATTERN_SUFFIX);
2733                 }
2734             } else if (tagName.equals(EXTRAS_STR)) {
2735                 mExtras = PersistableBundle.restoreFromXml(parser);
2736             } else if (Flags.relativeReferenceIntentFilters()
2737                     && URI_RELATIVE_FILTER_GROUP_STR.equals(tagName)) {
2738                 addUriRelativeFilterGroup(new UriRelativeFilterGroup(parser));
2739             } else {
2740                 Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
2741             }
2742             XmlUtils.skipCurrentTag(parser);
2743         }
2744     }
2745 
2746     /** @hide */
dumpDebug(ProtoOutputStream proto, long fieldId)2747     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
2748         long token = proto.start(fieldId);
2749         if (mActions.size() > 0) {
2750             Iterator<String> it = mActions.iterator();
2751             while (it.hasNext()) {
2752                 proto.write(IntentFilterProto.ACTIONS, it.next());
2753             }
2754         }
2755         if (mCategories != null) {
2756             Iterator<String> it = mCategories.iterator();
2757             while (it.hasNext()) {
2758                 proto.write(IntentFilterProto.CATEGORIES, it.next());
2759             }
2760         }
2761         if (mDataSchemes != null) {
2762             Iterator<String> it = mDataSchemes.iterator();
2763             while (it.hasNext()) {
2764                 proto.write(IntentFilterProto.DATA_SCHEMES, it.next());
2765             }
2766         }
2767         if (mDataSchemeSpecificParts != null) {
2768             Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
2769             while (it.hasNext()) {
2770                 it.next().dumpDebug(proto, IntentFilterProto.DATA_SCHEME_SPECS);
2771             }
2772         }
2773         if (mDataAuthorities != null) {
2774             Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
2775             while (it.hasNext()) {
2776                 it.next().dumpDebug(proto, IntentFilterProto.DATA_AUTHORITIES);
2777             }
2778         }
2779         if (mDataPaths != null) {
2780             Iterator<PatternMatcher> it = mDataPaths.iterator();
2781             while (it.hasNext()) {
2782                 it.next().dumpDebug(proto, IntentFilterProto.DATA_PATHS);
2783             }
2784         }
2785         if (mDataTypes != null) {
2786             Iterator<String> it = mDataTypes.iterator();
2787             while (it.hasNext()) {
2788                 proto.write(IntentFilterProto.DATA_TYPES, it.next());
2789             }
2790         }
2791         if (mMimeGroups != null) {
2792             Iterator<String> it = mMimeGroups.iterator();
2793             while (it.hasNext()) {
2794                 proto.write(IntentFilterProto.MIME_GROUPS, it.next());
2795             }
2796         }
2797         if (mPriority != 0 || hasPartialTypes()) {
2798             proto.write(IntentFilterProto.PRIORITY, mPriority);
2799             proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, hasPartialTypes());
2800         }
2801         proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify());
2802         if (mExtras != null) {
2803             mExtras.dumpDebug(proto, IntentFilterProto.EXTRAS);
2804         }
2805         if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) {
2806             Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator();
2807             while (it.hasNext()) {
2808                 it.next().dumpDebug(proto, IntentFilterProto.URI_RELATIVE_FILTER_GROUPS);
2809             }
2810         }
2811         proto.end(token);
2812     }
2813 
dump(Printer du, String prefix)2814     public void dump(Printer du, String prefix) {
2815         StringBuilder sb = new StringBuilder(256);
2816         if (mActions.size() > 0) {
2817             Iterator<String> it = mActions.iterator();
2818             while (it.hasNext()) {
2819                 sb.setLength(0);
2820                 sb.append(prefix); sb.append("Action: \"");
2821                         sb.append(it.next()); sb.append("\"");
2822                 du.println(sb.toString());
2823             }
2824         }
2825         if (mCategories != null) {
2826             Iterator<String> it = mCategories.iterator();
2827             while (it.hasNext()) {
2828                 sb.setLength(0);
2829                 sb.append(prefix); sb.append("Category: \"");
2830                         sb.append(it.next()); sb.append("\"");
2831                 du.println(sb.toString());
2832             }
2833         }
2834         if (mDataSchemes != null) {
2835             Iterator<String> it = mDataSchemes.iterator();
2836             while (it.hasNext()) {
2837                 sb.setLength(0);
2838                 sb.append(prefix); sb.append("Scheme: \"");
2839                         sb.append(it.next()); sb.append("\"");
2840                 du.println(sb.toString());
2841             }
2842         }
2843         if (mDataSchemeSpecificParts != null) {
2844             Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
2845             while (it.hasNext()) {
2846                 PatternMatcher pe = it.next();
2847                 sb.setLength(0);
2848                 sb.append(prefix); sb.append("Ssp: \"");
2849                         sb.append(pe); sb.append("\"");
2850                 du.println(sb.toString());
2851             }
2852         }
2853         if (mDataAuthorities != null) {
2854             Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
2855             while (it.hasNext()) {
2856                 AuthorityEntry ae = it.next();
2857                 sb.setLength(0);
2858                 sb.append(prefix); sb.append("Authority: \"");
2859                         sb.append(ae.mHost); sb.append("\": ");
2860                         sb.append(ae.mPort);
2861                 if (ae.mWild) sb.append(" WILD");
2862                 du.println(sb.toString());
2863             }
2864         }
2865         if (mDataPaths != null) {
2866             Iterator<PatternMatcher> it = mDataPaths.iterator();
2867             while (it.hasNext()) {
2868                 PatternMatcher pe = it.next();
2869                 sb.setLength(0);
2870                 sb.append(prefix); sb.append("Path: \"");
2871                         sb.append(pe); sb.append("\"");
2872                 du.println(sb.toString());
2873             }
2874         }
2875         if (mUriRelativeFilterGroups != null) {
2876             Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator();
2877             while (it.hasNext()) {
2878                 sb.setLength(0);
2879                 sb.append(prefix); sb.append("UriRelativeFilterGroup: \"");
2880                 sb.append(it.next()); sb.append("\"");
2881                 du.println(sb.toString());
2882             }
2883         }
2884         if (mStaticDataTypes != null) {
2885             Iterator<String> it = mStaticDataTypes.iterator();
2886             while (it.hasNext()) {
2887                 sb.setLength(0);
2888                 sb.append(prefix); sb.append("StaticType: \"");
2889                 sb.append(it.next()); sb.append("\"");
2890                 du.println(sb.toString());
2891             }
2892         }
2893         if (mDataTypes != null) {
2894             Iterator<String> it = mDataTypes.iterator();
2895             while (it.hasNext()) {
2896                 String dataType = it.next();
2897                 if (hasExactStaticDataType(dataType)) {
2898                     continue;
2899                 }
2900 
2901                 sb.setLength(0);
2902                 sb.append(prefix); sb.append("Type: \"");
2903                 sb.append(dataType); sb.append("\"");
2904                 du.println(sb.toString());
2905             }
2906         }
2907         if (mMimeGroups != null) {
2908             Iterator<String> it = mMimeGroups.iterator();
2909             while (it.hasNext()) {
2910                 sb.setLength(0);
2911                 sb.append(prefix); sb.append("MimeGroup: \"");
2912                 sb.append(it.next()); sb.append("\"");
2913                 du.println(sb.toString());
2914             }
2915         }
2916         if (mPriority != 0 || mOrder != 0 || hasPartialTypes()) {
2917             sb.setLength(0);
2918             sb.append(prefix);
2919             sb.append("mPriority="); sb.append(mPriority);
2920             sb.append(", mOrder="); sb.append(mOrder);
2921             sb.append(", mHasStaticPartialTypes="); sb.append(mHasStaticPartialTypes);
2922             sb.append(", mHasDynamicPartialTypes="); sb.append(mHasDynamicPartialTypes);
2923             du.println(sb.toString());
2924         }
2925         if (getAutoVerify()) {
2926             sb.setLength(0);
2927             sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
2928             du.println(sb.toString());
2929         }
2930         if (mExtras != null) {
2931             sb.setLength(0);
2932             sb.append(prefix); sb.append("mExtras="); sb.append(mExtras.toString());
2933             du.println(sb.toString());
2934         }
2935     }
2936 
2937     public static final @android.annotation.NonNull Parcelable.Creator<IntentFilter> CREATOR
2938             = new Parcelable.Creator<IntentFilter>() {
2939         public IntentFilter createFromParcel(Parcel source) {
2940             return new IntentFilter(source);
2941         }
2942 
2943         public IntentFilter[] newArray(int size) {
2944             return new IntentFilter[size];
2945         }
2946     };
2947 
describeContents()2948     public final int describeContents() {
2949         return 0;
2950     }
2951 
writeToParcel(Parcel dest, int flags)2952     public final void writeToParcel(Parcel dest, int flags) {
2953         dest.writeStringArray(mActions.toArray(new String[mActions.size()]));
2954         if (mCategories != null) {
2955             dest.writeInt(1);
2956             dest.writeStringList(mCategories);
2957         } else {
2958             dest.writeInt(0);
2959         }
2960         if (mDataSchemes != null) {
2961             dest.writeInt(1);
2962             dest.writeStringList(mDataSchemes);
2963         } else {
2964             dest.writeInt(0);
2965         }
2966         if (mStaticDataTypes != null) {
2967             dest.writeInt(1);
2968             dest.writeStringList(mStaticDataTypes);
2969         } else {
2970             dest.writeInt(0);
2971         }
2972         if (mDataTypes != null) {
2973             dest.writeInt(1);
2974             dest.writeStringList(mDataTypes);
2975         } else {
2976             dest.writeInt(0);
2977         }
2978         if (mMimeGroups != null) {
2979             dest.writeInt(1);
2980             dest.writeStringList(mMimeGroups);
2981         } else {
2982             dest.writeInt(0);
2983         }
2984         if (mDataSchemeSpecificParts != null) {
2985             final int N = mDataSchemeSpecificParts.size();
2986             dest.writeInt(N);
2987             for (int i=0; i<N; i++) {
2988                 mDataSchemeSpecificParts.get(i).writeToParcel(dest, flags);
2989             }
2990         } else {
2991             dest.writeInt(0);
2992         }
2993         if (mDataAuthorities != null) {
2994             final int N = mDataAuthorities.size();
2995             dest.writeInt(N);
2996             for (int i=0; i<N; i++) {
2997                 mDataAuthorities.get(i).writeToParcel(dest);
2998             }
2999         } else {
3000             dest.writeInt(0);
3001         }
3002         if (mDataPaths != null) {
3003             final int N = mDataPaths.size();
3004             dest.writeInt(N);
3005             for (int i=0; i<N; i++) {
3006                 mDataPaths.get(i).writeToParcel(dest, flags);
3007             }
3008         } else {
3009             dest.writeInt(0);
3010         }
3011         dest.writeInt(mPriority);
3012         dest.writeInt(mHasStaticPartialTypes ? 1 : 0);
3013         dest.writeInt(mHasDynamicPartialTypes ? 1 : 0);
3014         dest.writeInt(getAutoVerify() ? 1 : 0);
3015         dest.writeInt(mInstantAppVisibility);
3016         dest.writeInt(mOrder);
3017         if (mExtras != null) {
3018             dest.writeInt(1);
3019             mExtras.writeToParcel(dest, flags);
3020         } else {
3021             dest.writeInt(0);
3022         }
3023         if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) {
3024             final int N = mUriRelativeFilterGroups.size();
3025             dest.writeInt(N);
3026             for (int i = 0; i < N; i++) {
3027                 mUriRelativeFilterGroups.get(i).writeToParcel(dest, flags);
3028             }
3029         } else {
3030             dest.writeInt(0);
3031         }
3032     }
3033 
3034     /**
3035      * For debugging -- perform a check on the filter, return true if it passed
3036      * or false if it failed.
3037      *
3038      * {@hide}
3039      */
debugCheck()3040     public boolean debugCheck() {
3041         return true;
3042 
3043         // This code looks for intent filters that do not specify data.
3044         /*
3045         if (mActions != null && mActions.size() == 1
3046                 && mActions.contains(Intent.ACTION_MAIN)) {
3047             return true;
3048         }
3049 
3050         if (mDataTypes == null && mDataSchemes == null) {
3051             Log.w("IntentFilter", "QUESTIONABLE INTENT FILTER:");
3052             dump(Log.WARN, "IntentFilter", "  ");
3053             return false;
3054         }
3055 
3056         return true;
3057         */
3058     }
3059 
3060     /**
3061      * Perform a check on data paths and scheme specific parts of the intent filter.
3062      * Return true if it passed.
3063      * @hide
3064      */
checkDataPathAndSchemeSpecificParts()3065     public boolean checkDataPathAndSchemeSpecificParts() {
3066         final int numDataPath = mDataPaths == null
3067                 ? 0 : mDataPaths.size();
3068         final int numDataSchemeSpecificParts = mDataSchemeSpecificParts == null
3069                 ? 0 : mDataSchemeSpecificParts.size();
3070         for (int i = 0; i < numDataPath; i++) {
3071             if (!mDataPaths.get(i).check()) {
3072                 return false;
3073             }
3074         }
3075         for (int i = 0; i < numDataSchemeSpecificParts; i++) {
3076             if (!mDataSchemeSpecificParts.get(i).check()) {
3077                 return false;
3078             }
3079         }
3080         return true;
3081     }
3082 
3083     /** @hide */
IntentFilter(Parcel source)3084     public IntentFilter(Parcel source) {
3085         List<String> actions = new ArrayList<>();
3086         source.readStringList(actions);
3087         mActions = new ArraySet<>(actions);
3088         if (source.readInt() != 0) {
3089             mCategories = new ArrayList<String>();
3090             source.readStringList(mCategories);
3091         }
3092         if (source.readInt() != 0) {
3093             mDataSchemes = new ArrayList<String>();
3094             source.readStringList(mDataSchemes);
3095         }
3096         if (source.readInt() != 0) {
3097             mStaticDataTypes = new ArrayList<String>();
3098             source.readStringList(mStaticDataTypes);
3099         }
3100         if (source.readInt() != 0) {
3101             mDataTypes = new ArrayList<String>();
3102             source.readStringList(mDataTypes);
3103         }
3104         if (source.readInt() != 0) {
3105             mMimeGroups = new ArrayList<String>();
3106             source.readStringList(mMimeGroups);
3107         }
3108         int N = source.readInt();
3109         if (N > 0) {
3110             mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(N);
3111             for (int i=0; i<N; i++) {
3112                 mDataSchemeSpecificParts.add(new PatternMatcher(source));
3113             }
3114         }
3115         N = source.readInt();
3116         if (N > 0) {
3117             mDataAuthorities = new ArrayList<AuthorityEntry>(N);
3118             for (int i=0; i<N; i++) {
3119                 mDataAuthorities.add(new AuthorityEntry(source));
3120             }
3121         }
3122         N = source.readInt();
3123         if (N > 0) {
3124             mDataPaths = new ArrayList<PatternMatcher>(N);
3125             for (int i=0; i<N; i++) {
3126                 mDataPaths.add(new PatternMatcher(source));
3127             }
3128         }
3129         mPriority = source.readInt();
3130         mHasStaticPartialTypes = source.readInt() > 0;
3131         mHasDynamicPartialTypes = source.readInt() > 0;
3132         setAutoVerify(source.readInt() > 0);
3133         setVisibilityToInstantApp(source.readInt());
3134         mOrder = source.readInt();
3135         if (source.readInt() != 0) {
3136             mExtras = PersistableBundle.CREATOR.createFromParcel(source);
3137         }
3138         N = source.readInt();
3139         if (Flags.relativeReferenceIntentFilters() && N > 0) {
3140             mUriRelativeFilterGroups = new ArrayList<UriRelativeFilterGroup>(N);
3141             for (int i = 0; i < N; i++) {
3142                 mUriRelativeFilterGroups.add(new UriRelativeFilterGroup(source));
3143             }
3144         }
3145     }
3146 
hasPartialTypes()3147     private boolean hasPartialTypes() {
3148         return mHasStaticPartialTypes || mHasDynamicPartialTypes;
3149     }
3150 
findMimeType(String type)3151     private final boolean findMimeType(String type) {
3152         final ArrayList<String> t = mDataTypes;
3153 
3154         if (type == null) {
3155             return false;
3156         }
3157 
3158         if (t.contains(type)) {
3159             return true;
3160         }
3161 
3162         // Deal with an Intent wanting to match every type in the IntentFilter.
3163         final int typeLength = type.length();
3164         if (typeLength == 3 && type.equals("*/*")) {
3165             return !t.isEmpty();
3166         }
3167 
3168         // Deal with this IntentFilter wanting to match every Intent type.
3169         if (hasPartialTypes() && t.contains("*")) {
3170             return true;
3171         }
3172 
3173         final int slashpos = type.indexOf('/');
3174         if (slashpos > 0) {
3175             if (hasPartialTypes() && t.contains(type.substring(0, slashpos))) {
3176                 return true;
3177             }
3178             if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') {
3179                 // Need to look through all types for one that matches
3180                 // our base...
3181                 final int numTypes = t.size();
3182                 for (int i = 0; i < numTypes; i++) {
3183                     final String v = t.get(i);
3184                     if (type.regionMatches(0, v, 0, slashpos+1)) {
3185                         return true;
3186                     }
3187                 }
3188             }
3189         }
3190 
3191         return false;
3192     }
3193 
3194     /**
3195      * @hide
3196      */
getHostsList()3197     public ArrayList<String> getHostsList() {
3198         ArrayList<String> result = new ArrayList<>();
3199         Iterator<IntentFilter.AuthorityEntry> it = authoritiesIterator();
3200         if (it != null) {
3201             while (it.hasNext()) {
3202                 IntentFilter.AuthorityEntry entry = it.next();
3203                 result.add(entry.getHost());
3204             }
3205         }
3206         return result;
3207     }
3208 
3209     /**
3210      * @hide
3211      */
getHosts()3212     public String[] getHosts() {
3213         ArrayList<String> list = getHostsList();
3214         return list.toArray(new String[list.size()]);
3215     }
3216 
3217     /**
3218      * @hide
3219      */
filterEquals(IntentFilter f1, IntentFilter f2)3220     public static boolean filterEquals(IntentFilter f1, IntentFilter f2) {
3221         int s1 = f1.countActions();
3222         int s2 = f2.countActions();
3223         if (s1 != s2) {
3224             return false;
3225         }
3226         for (int i=0; i<s1; i++) {
3227             if (!f2.hasAction(f1.getAction(i))) {
3228                 return false;
3229             }
3230         }
3231         s1 = f1.countCategories();
3232         s2 = f2.countCategories();
3233         if (s1 != s2) {
3234             return false;
3235         }
3236         for (int i=0; i<s1; i++) {
3237             if (!f2.hasCategory(f1.getCategory(i))) {
3238                 return false;
3239             }
3240         }
3241         s1 = f1.countDataTypes();
3242         s2 = f2.countDataTypes();
3243         if (s1 != s2) {
3244             return false;
3245         }
3246         for (int i=0; i<s1; i++) {
3247             if (!f2.hasExactDataType(f1.getDataType(i))) {
3248                 return false;
3249             }
3250         }
3251         s1 = f1.countDataSchemes();
3252         s2 = f2.countDataSchemes();
3253         if (s1 != s2) {
3254             return false;
3255         }
3256         for (int i=0; i<s1; i++) {
3257             if (!f2.hasDataScheme(f1.getDataScheme(i))) {
3258                 return false;
3259             }
3260         }
3261         s1 = f1.countDataAuthorities();
3262         s2 = f2.countDataAuthorities();
3263         if (s1 != s2) {
3264             return false;
3265         }
3266         for (int i=0; i<s1; i++) {
3267             if (!f2.hasDataAuthority(f1.getDataAuthority(i))) {
3268                 return false;
3269             }
3270         }
3271         s1 = f1.countDataPaths();
3272         s2 = f2.countDataPaths();
3273         if (s1 != s2) {
3274             return false;
3275         }
3276         for (int i=0; i<s1; i++) {
3277             if (!f2.hasDataPath(f1.getDataPath(i))) {
3278                 return false;
3279             }
3280         }
3281         s1 = f1.countDataSchemeSpecificParts();
3282         s2 = f2.countDataSchemeSpecificParts();
3283         if (s1 != s2) {
3284             return false;
3285         }
3286         for (int i=0; i<s1; i++) {
3287             if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) {
3288                 return false;
3289             }
3290         }
3291         return true;
3292     }
3293 }
3294