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