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