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