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