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