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 org.xmlpull.v1.XmlPullParser;
     20 import org.xmlpull.v1.XmlPullParserException;
     21 import org.xmlpull.v1.XmlSerializer;
     22 
     23 
     24 import java.io.IOException;
     25 import java.util.ArrayList;
     26 import java.util.Iterator;
     27 import java.util.Set;
     28 
     29 import android.net.Uri;
     30 import android.os.Parcel;
     31 import android.os.Parcelable;
     32 import android.os.PatternMatcher;
     33 import android.util.AndroidException;
     34 import android.util.Config;
     35 import android.util.Log;
     36 import android.util.Printer;
     37 
     38 import com.android.internal.util.XmlUtils;
     39 
     40 /**
     41  * Structured description of Intent values to be matched.  An IntentFilter can
     42  * match against actions, categories, and data (either via its type, scheme,
     43  * and/or path) in an Intent.  It also includes a "priority" value which is
     44  * used to order multiple matching filters.
     45  *
     46  * <p>IntentFilter objects are often created in XML as part of a package's
     47  * {@link android.R.styleable#AndroidManifest AndroidManifest.xml} file,
     48  * using {@link android.R.styleable#AndroidManifestIntentFilter intent-filter}
     49  * tags.
     50  *
     51  * <p>There are three Intent characteristics you can filter on: the
     52  * <em>action</em>, <em>data</em>, and <em>categories</em>.  For each of these
     53  * characteristics you can provide
     54  * multiple possible matching values (via {@link #addAction},
     55  * {@link #addDataType}, {@link #addDataScheme} {@link #addDataAuthority},
     56  * {@link #addDataPath}, and {@link #addCategory}, respectively).
     57  * For actions, the field
     58  * will not be tested if no values have been given (treating it as a wildcard);
     59  * if no data characteristics are specified, however, then the filter will
     60  * only match intents that contain no data.
     61  *
     62  * <p>The data characteristic is
     63  * itself divided into three attributes: type, scheme, authority, and path.
     64  * Any that are
     65  * specified must match the contents of the Intent.  If you specify a scheme
     66  * but no type, only Intent that does not have a type (such as mailto:) will
     67  * match; a content: URI will never match because they always have a MIME type
     68  * that is supplied by their content provider.  Specifying a type with no scheme
     69  * has somewhat special meaning: it will match either an Intent with no URI
     70  * field, or an Intent with a content: or file: URI.  If you specify neither,
     71  * then only an Intent with no data or type will match.  To specify an authority,
     72  * you must also specify one or more schemes that it is associated with.
     73  * To specify a path, you also must specify both one or more authorities and
     74  * one or more schemes it is associated with.
     75  *
     76  * <p>A match is based on the following rules.  Note that
     77  * for an IntentFilter to match an Intent, three conditions must hold:
     78  * the <strong>action</strong> and <strong>category</strong> must match, and
     79  * the data (both the <strong>data type</strong> and
     80  * <strong>data scheme+authority+path</strong> if specified) must match.
     81  *
     82  * <p><strong>Action</strong> matches if any of the given values match the
     83  * Intent action, <em>or</em> if no actions were specified in the filter.
     84  *
     85  * <p><strong>Data Type</strong> matches if any of the given values match the
     86  * Intent type.  The Intent
     87  * type is determined by calling {@link Intent#resolveType}.  A wildcard can be
     88  * used for the MIME sub-type, in both the Intent and IntentFilter, so that the
     89  * type "audio/*" will match "audio/mpeg", "audio/aiff", "audio/*", etc.
     90  * <em>Note that MIME type matching here is <b>case sensitive</b>, unlike
     91  * formal RFC MIME types!</em>  You should thus always use lower case letters
     92  * for your MIME types.
     93  *
     94  * <p><strong>Data Scheme</strong> matches if any of the given values match the
     95  * Intent data's scheme.
     96  * The Intent scheme is determined by calling {@link Intent#getData}
     97  * and {@link android.net.Uri#getScheme} on that URI.
     98  * <em>Note that scheme matching here is <b>case sensitive</b>, unlike
     99  * formal RFC schemes!</em>  You should thus always use lower case letters
    100  * for your schemes.
    101  *
    102  * <p><strong>Data Authority</strong> matches if any of the given values match
    103  * the Intent's data authority <em>and</em> one of the data scheme's in the filter
    104  * has matched the Intent, <em>or</em> no authories were supplied in the filter.
    105  * The Intent authority is determined by calling
    106  * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
    107  * <em>Note that authority matching here is <b>case sensitive</b>, unlike
    108  * formal RFC host names!</em>  You should thus always use lower case letters
    109  * for your authority.
    110  *
    111  * <p><strong>Data Path</strong> matches if any of the given values match the
    112  * Intent's data path <em>and</em> both a scheme and authority in the filter
    113  * has matched against the Intent, <em>or</em> no paths were supplied in the
    114  * filter.  The Intent authority is determined by calling
    115  * {@link Intent#getData} and {@link android.net.Uri#getPath} on that URI.
    116  *
    117  * <p><strong>Categories</strong> match if <em>all</em> of the categories in
    118  * the Intent match categories given in the filter.  Extra categories in the
    119  * filter that are not in the Intent will not cause the match to fail.  Note
    120  * that unlike the action, an IntentFilter with no categories
    121  * will only match an Intent that does not have any categories.
    122  */
    123 public class IntentFilter implements Parcelable {
    124     private static final String SGLOB_STR = "sglob";
    125     private static final String PREFIX_STR = "prefix";
    126     private static final String LITERAL_STR = "literal";
    127     private static final String PATH_STR = "path";
    128     private static final String PORT_STR = "port";
    129     private static final String HOST_STR = "host";
    130     private static final String AUTH_STR = "auth";
    131     private static final String SCHEME_STR = "scheme";
    132     private static final String TYPE_STR = "type";
    133     private static final String CAT_STR = "cat";
    134     private static final String NAME_STR = "name";
    135     private static final String ACTION_STR = "action";
    136 
    137     /**
    138      * The filter {@link #setPriority} value at which system high-priority
    139      * receivers are placed; that is, receivers that should execute before
    140      * application code. Applications should never use filters with this or
    141      * higher priorities.
    142      *
    143      * @see #setPriority
    144      */
    145     public static final int SYSTEM_HIGH_PRIORITY = 1000;
    146 
    147     /**
    148      * The filter {@link #setPriority} value at which system low-priority
    149      * receivers are placed; that is, receivers that should execute after
    150      * application code. Applications should never use filters with this or
    151      * lower priorities.
    152      *
    153      * @see #setPriority
    154      */
    155     public static final int SYSTEM_LOW_PRIORITY = -1000;
    156 
    157     /**
    158      * The part of a match constant that describes the category of match
    159      * that occurred.  May be either {@link #MATCH_CATEGORY_EMPTY},
    160      * {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_HOST},
    161      * {@link #MATCH_CATEGORY_PORT},
    162      * {@link #MATCH_CATEGORY_PATH}, or {@link #MATCH_CATEGORY_TYPE}.  Higher
    163      * values indicate a better match.
    164      */
    165     public static final int MATCH_CATEGORY_MASK = 0xfff0000;
    166 
    167     /**
    168      * The part of a match constant that applies a quality adjustment to the
    169      * basic category of match.  The value {@link #MATCH_ADJUSTMENT_NORMAL}
    170      * is no adjustment; higher numbers than that improve the quality, while
    171      * lower numbers reduce it.
    172      */
    173     public static final int MATCH_ADJUSTMENT_MASK = 0x000ffff;
    174 
    175     /**
    176      * Quality adjustment applied to the category of match that signifies
    177      * the default, base value; higher numbers improve the quality while
    178      * lower numbers reduce it.
    179      */
    180     public static final int MATCH_ADJUSTMENT_NORMAL = 0x8000;
    181 
    182     /**
    183      * The filter matched an intent that had no data specified.
    184      */
    185     public static final int MATCH_CATEGORY_EMPTY = 0x0100000;
    186     /**
    187      * The filter matched an intent with the same data URI scheme.
    188      */
    189     public static final int MATCH_CATEGORY_SCHEME = 0x0200000;
    190     /**
    191      * The filter matched an intent with the same data URI scheme and
    192      * authority host.
    193      */
    194     public static final int MATCH_CATEGORY_HOST = 0x0300000;
    195     /**
    196      * The filter matched an intent with the same data URI scheme and
    197      * authority host and port.
    198      */
    199     public static final int MATCH_CATEGORY_PORT = 0x0400000;
    200     /**
    201      * The filter matched an intent with the same data URI scheme,
    202      * authority, and path.
    203      */
    204     public static final int MATCH_CATEGORY_PATH = 0x0500000;
    205     /**
    206      * The filter matched an intent with the same data MIME type.
    207      */
    208     public static final int MATCH_CATEGORY_TYPE = 0x0600000;
    209 
    210     /**
    211      * The filter didn't match due to different MIME types.
    212      */
    213     public static final int NO_MATCH_TYPE = -1;
    214     /**
    215      * The filter didn't match due to different data URIs.
    216      */
    217     public static final int NO_MATCH_DATA = -2;
    218     /**
    219      * The filter didn't match due to different actions.
    220      */
    221     public static final int NO_MATCH_ACTION = -3;
    222     /**
    223      * The filter didn't match because it required one or more categories
    224      * that were not in the Intent.
    225      */
    226     public static final int NO_MATCH_CATEGORY = -4;
    227 
    228     private int mPriority;
    229     private final ArrayList<String> mActions;
    230     private ArrayList<String> mCategories = null;
    231     private ArrayList<String> mDataSchemes = null;
    232     private ArrayList<AuthorityEntry> mDataAuthorities = null;
    233     private ArrayList<PatternMatcher> mDataPaths = null;
    234     private ArrayList<String> mDataTypes = null;
    235     private boolean mHasPartialTypes = false;
    236 
    237     // These functions are the start of more optimized code for managing
    238     // the string sets...  not yet implemented.
    239 
    240     private static int findStringInSet(String[] set, String string,
    241             int[] lengths, int lenPos) {
    242         if (set == null) return -1;
    243         final int N = lengths[lenPos];
    244         for (int i=0; i<N; i++) {
    245             if (set[i].equals(string)) return i;
    246         }
    247         return -1;
    248     }
    249 
    250     private static String[] addStringToSet(String[] set, String string,
    251             int[] lengths, int lenPos) {
    252         if (findStringInSet(set, string, lengths, lenPos) >= 0) return set;
    253         if (set == null) {
    254             set = new String[2];
    255             set[0] = string;
    256             lengths[lenPos] = 1;
    257             return set;
    258         }
    259         final int N = lengths[lenPos];
    260         if (N < set.length) {
    261             set[N] = string;
    262             lengths[lenPos] = N+1;
    263             return set;
    264         }
    265 
    266         String[] newSet = new String[(N*3)/2 + 2];
    267         System.arraycopy(set, 0, newSet, 0, N);
    268         set = newSet;
    269         set[N] = string;
    270         lengths[lenPos] = N+1;
    271         return set;
    272     }
    273 
    274     private static String[] removeStringFromSet(String[] set, String string,
    275             int[] lengths, int lenPos) {
    276         int pos = findStringInSet(set, string, lengths, lenPos);
    277         if (pos < 0) return set;
    278         final int N = lengths[lenPos];
    279         if (N > (set.length/4)) {
    280             int copyLen = N-(pos+1);
    281             if (copyLen > 0) {
    282                 System.arraycopy(set, pos+1, set, pos, copyLen);
    283             }
    284             set[N-1] = null;
    285             lengths[lenPos] = N-1;
    286             return set;
    287         }
    288 
    289         String[] newSet = new String[set.length/3];
    290         if (pos > 0) System.arraycopy(set, 0, newSet, 0, pos);
    291         if ((pos+1) < N) System.arraycopy(set, pos+1, newSet, pos, N-(pos+1));
    292         return newSet;
    293     }
    294 
    295     /**
    296      * This exception is thrown when a given MIME type does not have a valid
    297      * syntax.
    298      */
    299     public static class MalformedMimeTypeException extends AndroidException {
    300         public MalformedMimeTypeException() {
    301         }
    302 
    303         public MalformedMimeTypeException(String name) {
    304             super(name);
    305         }
    306     };
    307 
    308     /**
    309      * Create a new IntentFilter instance with a specified action and MIME
    310      * type, where you know the MIME type is correctly formatted.  This catches
    311      * the {@link MalformedMimeTypeException} exception that the constructor
    312      * can call and turns it into a runtime exception.
    313      *
    314      * @param action The action to match, i.e. Intent.ACTION_VIEW.
    315      * @param dataType The type to match, i.e. "vnd.android.cursor.dir/person".
    316      *
    317      * @return A new IntentFilter for the given action and type.
    318      *
    319      * @see #IntentFilter(String, String)
    320      */
    321     public static IntentFilter create(String action, String dataType) {
    322         try {
    323             return new IntentFilter(action, dataType);
    324         } catch (MalformedMimeTypeException e) {
    325             throw new RuntimeException("Bad MIME type", e);
    326         }
    327     }
    328 
    329     /**
    330      * New empty IntentFilter.
    331      */
    332     public IntentFilter() {
    333         mPriority = 0;
    334         mActions = new ArrayList<String>();
    335     }
    336 
    337     /**
    338      * New IntentFilter that matches a single action with no data.  If
    339      * no data characteristics are subsequently specified, then the
    340      * filter will only match intents that contain no data.
    341      *
    342      * @param action The action to match, i.e. Intent.ACTION_MAIN.
    343      */
    344     public IntentFilter(String action) {
    345         mPriority = 0;
    346         mActions = new ArrayList<String>();
    347         addAction(action);
    348     }
    349 
    350     /**
    351      * New IntentFilter that matches a single action and data type.
    352      *
    353      * <p><em>Note: MIME type matching in the Android framework is
    354      * case-sensitive, unlike formal RFC MIME types.  As a result,
    355      * you should always write your MIME types with lower case letters,
    356      * and any MIME types you receive from outside of Android should be
    357      * converted to lower case before supplying them here.</em></p>
    358      *
    359      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
    360      * not syntactically correct.
    361      *
    362      * @param action The action to match, i.e. Intent.ACTION_VIEW.
    363      * @param dataType The type to match, i.e. "vnd.android.cursor.dir/person".
    364      *
    365      */
    366     public IntentFilter(String action, String dataType)
    367         throws MalformedMimeTypeException {
    368         mPriority = 0;
    369         mActions = new ArrayList<String>();
    370         addAction(action);
    371         addDataType(dataType);
    372     }
    373 
    374     /**
    375      * New IntentFilter containing a copy of an existing filter.
    376      *
    377      * @param o The original filter to copy.
    378      */
    379     public IntentFilter(IntentFilter o) {
    380         mPriority = o.mPriority;
    381         mActions = new ArrayList<String>(o.mActions);
    382         if (o.mCategories != null) {
    383             mCategories = new ArrayList<String>(o.mCategories);
    384         }
    385         if (o.mDataTypes != null) {
    386             mDataTypes = new ArrayList<String>(o.mDataTypes);
    387         }
    388         if (o.mDataSchemes != null) {
    389             mDataSchemes = new ArrayList<String>(o.mDataSchemes);
    390         }
    391         if (o.mDataAuthorities != null) {
    392             mDataAuthorities = new ArrayList<AuthorityEntry>(o.mDataAuthorities);
    393         }
    394         if (o.mDataPaths != null) {
    395             mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
    396         }
    397         mHasPartialTypes = o.mHasPartialTypes;
    398     }
    399 
    400     /**
    401      * Modify priority of this filter.  The default priority is 0. Positive
    402      * values will be before the default, lower values will be after it.
    403      * Applications must use a value that is larger than
    404      * {@link #SYSTEM_LOW_PRIORITY} and smaller than
    405      * {@link #SYSTEM_HIGH_PRIORITY} .
    406      *
    407      * @param priority The new priority value.
    408      *
    409      * @see #getPriority
    410      * @see #SYSTEM_LOW_PRIORITY
    411      * @see #SYSTEM_HIGH_PRIORITY
    412      */
    413     public final void setPriority(int priority) {
    414         mPriority = priority;
    415     }
    416 
    417     /**
    418      * Return the priority of this filter.
    419      *
    420      * @return The priority of the filter.
    421      *
    422      * @see #setPriority
    423      */
    424     public final int getPriority() {
    425         return mPriority;
    426     }
    427 
    428     /**
    429      * Add a new Intent action to match against.  If any actions are included
    430      * in the filter, then an Intent's action must be one of those values for
    431      * it to match.  If no actions are included, the Intent action is ignored.
    432      *
    433      * @param action Name of the action to match, i.e. Intent.ACTION_VIEW.
    434      */
    435     public final void addAction(String action) {
    436         if (!mActions.contains(action)) {
    437             mActions.add(action.intern());
    438         }
    439     }
    440 
    441     /**
    442      * Return the number of actions in the filter.
    443      */
    444     public final int countActions() {
    445         return mActions.size();
    446     }
    447 
    448     /**
    449      * Return an action in the filter.
    450      */
    451     public final String getAction(int index) {
    452         return mActions.get(index);
    453     }
    454 
    455     /**
    456      * Is the given action included in the filter?  Note that if the filter
    457      * does not include any actions, false will <em>always</em> be returned.
    458      *
    459      * @param action The action to look for.
    460      *
    461      * @return True if the action is explicitly mentioned in the filter.
    462      */
    463     public final boolean hasAction(String action) {
    464         return mActions.contains(action);
    465     }
    466 
    467     /**
    468      * Match this filter against an Intent's action.  If the filter does not
    469      * specify any actions, the match will always fail.
    470      *
    471      * @param action The desired action to look for.
    472      *
    473      * @return True if the action is listed in the filter or the filter does
    474      *         not specify any actions.
    475      */
    476     public final boolean matchAction(String action) {
    477         if (action == null || mActions == null || mActions.size() == 0) {
    478             return false;
    479         }
    480         return mActions.contains(action);
    481     }
    482 
    483     /**
    484      * Return an iterator over the filter's actions.  If there are no actions,
    485      * returns null.
    486      */
    487     public final Iterator<String> actionsIterator() {
    488         return mActions != null ? mActions.iterator() : null;
    489     }
    490 
    491     /**
    492      * Add a new Intent data type to match against.  If any types are
    493      * included in the filter, then an Intent's data must be <em>either</em>
    494      * one of these types <em>or</em> a matching scheme.  If no data types
    495      * are included, then an Intent will only match if it specifies no data.
    496      *
    497      * <p><em>Note: MIME type matching in the Android framework is
    498      * case-sensitive, unlike formal RFC MIME types.  As a result,
    499      * you should always write your MIME types with lower case letters,
    500      * and any MIME types you receive from outside of Android should be
    501      * converted to lower case before supplying them here.</em></p>
    502      *
    503      * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
    504      * not syntactically correct.
    505      *
    506      * @param type Name of the data type to match, i.e. "vnd.android.cursor.dir/person".
    507      *
    508      * @see #matchData
    509      */
    510     public final void addDataType(String type)
    511         throws MalformedMimeTypeException {
    512         final int slashpos = type.indexOf('/');
    513         final int typelen = type.length();
    514         if (slashpos > 0 && typelen >= slashpos+2) {
    515             if (mDataTypes == null) mDataTypes = new ArrayList<String>();
    516             if (typelen == slashpos+2 && type.charAt(slashpos+1) == '*') {
    517                 String str = type.substring(0, slashpos);
    518                 if (!mDataTypes.contains(str)) {
    519                     mDataTypes.add(str.intern());
    520                 }
    521                 mHasPartialTypes = true;
    522             } else {
    523                 if (!mDataTypes.contains(type)) {
    524                     mDataTypes.add(type.intern());
    525                 }
    526             }
    527             return;
    528         }
    529 
    530         throw new MalformedMimeTypeException(type);
    531     }
    532 
    533     /**
    534      * Is the given data type included in the filter?  Note that if the filter
    535      * does not include any type, false will <em>always</em> be returned.
    536      *
    537      * @param type The data type to look for.
    538      *
    539      * @return True if the type is explicitly mentioned in the filter.
    540      */
    541     public final boolean hasDataType(String type) {
    542         return mDataTypes != null && findMimeType(type);
    543     }
    544 
    545     /**
    546      * Return the number of data types in the filter.
    547      */
    548     public final int countDataTypes() {
    549         return mDataTypes != null ? mDataTypes.size() : 0;
    550     }
    551 
    552     /**
    553      * Return a data type in the filter.
    554      */
    555     public final String getDataType(int index) {
    556         return mDataTypes.get(index);
    557     }
    558 
    559     /**
    560      * Return an iterator over the filter's data types.
    561      */
    562     public final Iterator<String> typesIterator() {
    563         return mDataTypes != null ? mDataTypes.iterator() : null;
    564     }
    565 
    566     /**
    567      * Add a new Intent data scheme to match against.  If any schemes are
    568      * included in the filter, then an Intent's data must be <em>either</em>
    569      * one of these schemes <em>or</em> a matching data type.  If no schemes
    570      * are included, then an Intent will match only if it includes no data.
    571      *
    572      * <p><em>Note: scheme matching in the Android framework is
    573      * case-sensitive, unlike formal RFC schemes.  As a result,
    574      * you should always write your schemes with lower case letters,
    575      * and any schemes you receive from outside of Android should be
    576      * converted to lower case before supplying them here.</em></p>
    577      *
    578      * @param scheme Name of the scheme to match, i.e. "http".
    579      *
    580      * @see #matchData
    581      */
    582     public final void addDataScheme(String scheme) {
    583         if (mDataSchemes == null) mDataSchemes = new ArrayList<String>();
    584         if (!mDataSchemes.contains(scheme)) {
    585             mDataSchemes.add(scheme.intern());
    586         }
    587     }
    588 
    589     /**
    590      * Return the number of data schemes in the filter.
    591      */
    592     public final int countDataSchemes() {
    593         return mDataSchemes != null ? mDataSchemes.size() : 0;
    594     }
    595 
    596     /**
    597      * Return a data scheme in the filter.
    598      */
    599     public final String getDataScheme(int index) {
    600         return mDataSchemes.get(index);
    601     }
    602 
    603     /**
    604      * Is the given data scheme included in the filter?  Note that if the
    605      * filter does not include any scheme, false will <em>always</em> be
    606      * returned.
    607      *
    608      * @param scheme The data scheme to look for.
    609      *
    610      * @return True if the scheme is explicitly mentioned in the filter.
    611      */
    612     public final boolean hasDataScheme(String scheme) {
    613         return mDataSchemes != null && mDataSchemes.contains(scheme);
    614     }
    615 
    616     /**
    617      * Return an iterator over the filter's data schemes.
    618      */
    619     public final Iterator<String> schemesIterator() {
    620         return mDataSchemes != null ? mDataSchemes.iterator() : null;
    621     }
    622 
    623     /**
    624      * This is an entry for a single authority in the Iterator returned by
    625      * {@link #authoritiesIterator()}.
    626      */
    627     public final static class AuthorityEntry {
    628         private final String mOrigHost;
    629         private final String mHost;
    630         private final boolean mWild;
    631         private final int mPort;
    632 
    633         public AuthorityEntry(String host, String port) {
    634             mOrigHost = host;
    635             mWild = host.length() > 0 && host.charAt(0) == '*';
    636             mHost = mWild ? host.substring(1).intern() : host;
    637             mPort = port != null ? Integer.parseInt(port) : -1;
    638         }
    639 
    640         AuthorityEntry(Parcel src) {
    641             mOrigHost = src.readString();
    642             mHost = src.readString();
    643             mWild = src.readInt() != 0;
    644             mPort = src.readInt();
    645         }
    646 
    647         void writeToParcel(Parcel dest) {
    648             dest.writeString(mOrigHost);
    649             dest.writeString(mHost);
    650             dest.writeInt(mWild ? 1 : 0);
    651             dest.writeInt(mPort);
    652         }
    653 
    654         public String getHost() {
    655             return mOrigHost;
    656         }
    657 
    658         public int getPort() {
    659             return mPort;
    660         }
    661 
    662         /**
    663          * Determine whether this AuthorityEntry matches the given data Uri.
    664          * <em>Note that this comparison is case-sensitive, unlike formal
    665          * RFC host names.  You thus should always normalize to lower-case.</em>
    666          *
    667          * @param data The Uri to match.
    668          * @return Returns either {@link IntentFilter#NO_MATCH_DATA},
    669          * {@link IntentFilter#MATCH_CATEGORY_PORT}, or
    670          * {@link IntentFilter#MATCH_CATEGORY_HOST}.
    671          */
    672         public int match(Uri data) {
    673             String host = data.getHost();
    674             if (host == null) {
    675                 return NO_MATCH_DATA;
    676             }
    677             if (Config.LOGV) Log.v("IntentFilter",
    678                     "Match host " + host + ": " + mHost);
    679             if (mWild) {
    680                 if (host.length() < mHost.length()) {
    681                     return NO_MATCH_DATA;
    682                 }
    683                 host = host.substring(host.length()-mHost.length());
    684             }
    685             if (host.compareToIgnoreCase(mHost) != 0) {
    686                 return NO_MATCH_DATA;
    687             }
    688             if (mPort >= 0) {
    689                 if (mPort != data.getPort()) {
    690                     return NO_MATCH_DATA;
    691                 }
    692                 return MATCH_CATEGORY_PORT;
    693             }
    694             return MATCH_CATEGORY_HOST;
    695         }
    696     };
    697 
    698     /**
    699      * Add a new Intent data authority to match against.  The filter must
    700      * include one or more schemes (via {@link #addDataScheme}) for the
    701      * authority to be considered.  If any authorities are
    702      * included in the filter, then an Intent's data must match one of
    703      * them.  If no authorities are included, then only the scheme must match.
    704      *
    705      * <p><em>Note: host name in the Android framework is
    706      * case-sensitive, unlike formal RFC host names.  As a result,
    707      * you should always write your host names with lower case letters,
    708      * and any host names you receive from outside of Android should be
    709      * converted to lower case before supplying them here.</em></p>
    710      *
    711      * @param host The host part of the authority to match.  May start with a
    712      *             single '*' to wildcard the front of the host name.
    713      * @param port Optional port part of the authority to match.  If null, any
    714      *             port is allowed.
    715      *
    716      * @see #matchData
    717      * @see #addDataScheme
    718      */
    719     public final void addDataAuthority(String host, String port) {
    720         if (mDataAuthorities == null) mDataAuthorities =
    721                 new ArrayList<AuthorityEntry>();
    722         if (port != null) port = port.intern();
    723         mDataAuthorities.add(new AuthorityEntry(host.intern(), port));
    724     }
    725 
    726     /**
    727      * Return the number of data authorities in the filter.
    728      */
    729     public final int countDataAuthorities() {
    730         return mDataAuthorities != null ? mDataAuthorities.size() : 0;
    731     }
    732 
    733     /**
    734      * Return a data authority in the filter.
    735      */
    736     public final AuthorityEntry getDataAuthority(int index) {
    737         return mDataAuthorities.get(index);
    738     }
    739 
    740     /**
    741      * Is the given data authority included in the filter?  Note that if the
    742      * filter does not include any authorities, false will <em>always</em> be
    743      * returned.
    744      *
    745      * @param data The data whose authority is being looked for.
    746      *
    747      * @return Returns true if the data string matches an authority listed in the
    748      *         filter.
    749      */
    750     public final boolean hasDataAuthority(Uri data) {
    751         return matchDataAuthority(data) >= 0;
    752     }
    753 
    754     /**
    755      * Return an iterator over the filter's data authorities.
    756      */
    757     public final Iterator<AuthorityEntry> authoritiesIterator() {
    758         return mDataAuthorities != null ? mDataAuthorities.iterator() : null;
    759     }
    760 
    761     /**
    762      * Add a new Intent data oath to match against.  The filter must
    763      * include one or more schemes (via {@link #addDataScheme}) <em>and</em>
    764      * one or more authorities (via {@link #addDataAuthority}) for the
    765      * path to be considered.  If any paths are
    766      * included in the filter, then an Intent's data must match one of
    767      * them.  If no paths are included, then only the scheme/authority must
    768      * match.
    769      *
    770      * <p>The path given here can either be a literal that must directly
    771      * match or match against a prefix, or it can be a simple globbing pattern.
    772      * If the latter, you can use '*' anywhere in the pattern to match zero
    773      * or more instances of the previous character, '.' as a wildcard to match
    774      * any character, and '\' to escape the next character.
    775      *
    776      * @param path Either a raw string that must exactly match the file
    777      * path, or a simple pattern, depending on <var>type</var>.
    778      * @param type Determines how <var>path</var> will be compared to
    779      * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
    780      * {@link PatternMatcher#PATTERN_PREFIX}, or
    781      * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
    782      *
    783      * @see #matchData
    784      * @see #addDataScheme
    785      * @see #addDataAuthority
    786      */
    787     public final void addDataPath(String path, int type) {
    788         if (mDataPaths == null) mDataPaths = new ArrayList<PatternMatcher>();
    789         mDataPaths.add(new PatternMatcher(path.intern(), type));
    790     }
    791 
    792     /**
    793      * Return the number of data paths in the filter.
    794      */
    795     public final int countDataPaths() {
    796         return mDataPaths != null ? mDataPaths.size() : 0;
    797     }
    798 
    799     /**
    800      * Return a data path in the filter.
    801      */
    802     public final PatternMatcher getDataPath(int index) {
    803         return mDataPaths.get(index);
    804     }
    805 
    806     /**
    807      * Is the given data path included in the filter?  Note that if the
    808      * filter does not include any paths, false will <em>always</em> be
    809      * returned.
    810      *
    811      * @param data The data path to look for.  This is without the scheme
    812      *             prefix.
    813      *
    814      * @return True if the data string matches a path listed in the
    815      *         filter.
    816      */
    817     public final boolean hasDataPath(String data) {
    818         if (mDataPaths == null) {
    819             return false;
    820         }
    821         Iterator<PatternMatcher> i = mDataPaths.iterator();
    822         while (i.hasNext()) {
    823             final PatternMatcher pe = i.next();
    824             if (pe.match(data)) {
    825                 return true;
    826             }
    827         }
    828         return false;
    829     }
    830 
    831     /**
    832      * Return an iterator over the filter's data paths.
    833      */
    834     public final Iterator<PatternMatcher> pathsIterator() {
    835         return mDataPaths != null ? mDataPaths.iterator() : null;
    836     }
    837 
    838     /**
    839      * Match this intent filter against the given Intent data.  This ignores
    840      * the data scheme -- unlike {@link #matchData}, the authority will match
    841      * regardless of whether there is a matching scheme.
    842      *
    843      * @param data The data whose authority is being looked for.
    844      *
    845      * @return Returns either {@link #MATCH_CATEGORY_HOST},
    846      * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}.
    847      */
    848     public final int matchDataAuthority(Uri data) {
    849         if (mDataAuthorities == null) {
    850             return NO_MATCH_DATA;
    851         }
    852         Iterator<AuthorityEntry> i = mDataAuthorities.iterator();
    853         while (i.hasNext()) {
    854             final AuthorityEntry ae = i.next();
    855             int match = ae.match(data);
    856             if (match >= 0) {
    857                 return match;
    858             }
    859         }
    860         return NO_MATCH_DATA;
    861     }
    862 
    863     /**
    864      * Match this filter against an Intent's data (type, scheme and path). If
    865      * the filter does not specify any types and does not specify any
    866      * schemes/paths, the match will only succeed if the intent does not
    867      * also specify a type or data.
    868      *
    869      * <p>Be aware that to match against an authority, you must also specify a base
    870      * scheme the authority is in.  To match against a data path, both a scheme
    871      * and authority must be specified.  If the filter does not specify any
    872      * types or schemes that it matches against, it is considered to be empty
    873      * (any authority or data path given is ignored, as if it were empty as
    874      * well).
    875      *
    876      * <p><em>Note: MIME type, Uri scheme, and host name matching in the
    877      * Android framework is case-sensitive, unlike the formal RFC definitions.
    878      * As a result, you should always write these elements with lower case letters,
    879      * and normalize any MIME types or Uris you receive from
    880      * outside of Android to ensure these elements are lower case before
    881      * supplying them here.</em></p>
    882      *
    883      * @param type The desired data type to look for, as returned by
    884      *             Intent.resolveType().
    885      * @param scheme The desired data scheme to look for, as returned by
    886      *               Intent.getScheme().
    887      * @param data The full data string to match against, as supplied in
    888      *             Intent.data.
    889      *
    890      * @return Returns either a valid match constant (a combination of
    891      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
    892      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match
    893      * or {@link #NO_MATCH_DATA} if the scheme/path didn't match.
    894      *
    895      * @see #match
    896      */
    897     public final int matchData(String type, String scheme, Uri data) {
    898         final ArrayList<String> types = mDataTypes;
    899         final ArrayList<String> schemes = mDataSchemes;
    900         final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
    901         final ArrayList<PatternMatcher> paths = mDataPaths;
    902 
    903         int match = MATCH_CATEGORY_EMPTY;
    904 
    905         if (types == null && schemes == null) {
    906             return ((type == null && data == null)
    907                 ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
    908         }
    909 
    910         if (schemes != null) {
    911             if (schemes.contains(scheme != null ? scheme : "")) {
    912                 match = MATCH_CATEGORY_SCHEME;
    913             } else {
    914                 return NO_MATCH_DATA;
    915             }
    916 
    917             if (authorities != null) {
    918                 int authMatch = matchDataAuthority(data);
    919                 if (authMatch >= 0) {
    920                     if (paths == null) {
    921                         match = authMatch;
    922                     } else if (hasDataPath(data.getPath())) {
    923                         match = MATCH_CATEGORY_PATH;
    924                     } else {
    925                         return NO_MATCH_DATA;
    926                     }
    927                 } else {
    928                     return NO_MATCH_DATA;
    929                 }
    930             }
    931         } else {
    932             // Special case: match either an Intent with no data URI,
    933             // or with a scheme: URI.  This is to give a convenience for
    934             // the common case where you want to deal with data in a
    935             // content provider, which is done by type, and we don't want
    936             // to force everyone to say they handle content: or file: URIs.
    937             if (scheme != null && !"".equals(scheme)
    938                     && !"content".equals(scheme)
    939                     && !"file".equals(scheme)) {
    940                 return NO_MATCH_DATA;
    941             }
    942         }
    943 
    944         if (types != null) {
    945             if (findMimeType(type)) {
    946                 match = MATCH_CATEGORY_TYPE;
    947             } else {
    948                 return NO_MATCH_TYPE;
    949             }
    950         } else {
    951             // If no MIME types are specified, then we will only match against
    952             // an Intent that does not have a MIME type.
    953             if (type != null) {
    954                 return NO_MATCH_TYPE;
    955             }
    956         }
    957 
    958         return match + MATCH_ADJUSTMENT_NORMAL;
    959     }
    960 
    961     /**
    962      * Add a new Intent category to match against.  The semantics of
    963      * categories is the opposite of actions -- an Intent includes the
    964      * categories that it requires, all of which must be included in the
    965      * filter in order to match.  In other words, adding a category to the
    966      * filter has no impact on matching unless that category is specified in
    967      * the intent.
    968      *
    969      * @param category Name of category to match, i.e. Intent.CATEGORY_EMBED.
    970      */
    971     public final void addCategory(String category) {
    972         if (mCategories == null) mCategories = new ArrayList<String>();
    973         if (!mCategories.contains(category)) {
    974             mCategories.add(category.intern());
    975         }
    976     }
    977 
    978     /**
    979      * Return the number of categories in the filter.
    980      */
    981     public final int countCategories() {
    982         return mCategories != null ? mCategories.size() : 0;
    983     }
    984 
    985     /**
    986      * Return a category in the filter.
    987      */
    988     public final String getCategory(int index) {
    989         return mCategories.get(index);
    990     }
    991 
    992     /**
    993      * Is the given category included in the filter?
    994      *
    995      * @param category The category that the filter supports.
    996      *
    997      * @return True if the category is explicitly mentioned in the filter.
    998      */
    999     public final boolean hasCategory(String category) {
   1000         return mCategories != null && mCategories.contains(category);
   1001     }
   1002 
   1003     /**
   1004      * Return an iterator over the filter's categories.
   1005      */
   1006     public final Iterator<String> categoriesIterator() {
   1007         return mCategories != null ? mCategories.iterator() : null;
   1008     }
   1009 
   1010     /**
   1011      * Match this filter against an Intent's categories.  Each category in
   1012      * the Intent must be specified by the filter; if any are not in the
   1013      * filter, the match fails.
   1014      *
   1015      * @param categories The categories included in the intent, as returned by
   1016      *                   Intent.getCategories().
   1017      *
   1018      * @return If all categories match (success), null; else the name of the
   1019      *         first category that didn't match.
   1020      */
   1021     public final String matchCategories(Set<String> categories) {
   1022         if (categories == null) {
   1023             return null;
   1024         }
   1025 
   1026         Iterator<String> it = categories.iterator();
   1027 
   1028         if (mCategories == null) {
   1029             return it.hasNext() ? it.next() : null;
   1030         }
   1031 
   1032         while (it.hasNext()) {
   1033             final String category = it.next();
   1034             if (!mCategories.contains(category)) {
   1035                 return category;
   1036             }
   1037         }
   1038 
   1039         return null;
   1040     }
   1041 
   1042     /**
   1043      * Test whether this filter matches the given <var>intent</var>.
   1044      *
   1045      * @param intent The Intent to compare against.
   1046      * @param resolve If true, the intent's type will be resolved by calling
   1047      *                Intent.resolveType(); otherwise a simple match against
   1048      *                Intent.type will be performed.
   1049      * @param logTag Tag to use in debugging messages.
   1050      *
   1051      * @return Returns either a valid match constant (a combination of
   1052      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
   1053      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
   1054      * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
   1055      * {@link #NO_MATCH_ACTION if the action didn't match, or
   1056      * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
   1057      *
   1058      * @return How well the filter matches.  Negative if it doesn't match,
   1059      *         zero or positive positive value if it does with a higher
   1060      *         value representing a better match.
   1061      *
   1062      * @see #match(String, String, String, android.net.Uri , Set, String)
   1063      */
   1064     public final int match(ContentResolver resolver, Intent intent,
   1065             boolean resolve, String logTag) {
   1066         String type = resolve ? intent.resolveType(resolver) : intent.getType();
   1067         return match(intent.getAction(), type, intent.getScheme(),
   1068                      intent.getData(), intent.getCategories(), logTag);
   1069     }
   1070 
   1071     /**
   1072      * Test whether this filter matches the given intent data.  A match is
   1073      * only successful if the actions and categories in the Intent match
   1074      * against the filter, as described in {@link IntentFilter}; in that case,
   1075      * the match result returned will be as per {@link #matchData}.
   1076      *
   1077      * @param action The intent action to match against (Intent.getAction).
   1078      * @param type The intent type to match against (Intent.resolveType()).
   1079      * @param scheme The data scheme to match against (Intent.getScheme()).
   1080      * @param data The data URI to match against (Intent.getData()).
   1081      * @param categories The categories to match against
   1082      *                   (Intent.getCategories()).
   1083      * @param logTag Tag to use in debugging messages.
   1084      *
   1085      * @return Returns either a valid match constant (a combination of
   1086      * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
   1087      * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
   1088      * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
   1089      * {@link #NO_MATCH_ACTION if the action didn't match, or
   1090      * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
   1091      *
   1092      * @see #matchData
   1093      * @see Intent#getAction
   1094      * @see Intent#resolveType
   1095      * @see Intent#getScheme
   1096      * @see Intent#getData
   1097      * @see Intent#getCategories
   1098      */
   1099     public final int match(String action, String type, String scheme,
   1100             Uri data, Set<String> categories, String logTag) {
   1101         if (action != null && !matchAction(action)) {
   1102             if (Config.LOGV) Log.v(
   1103                 logTag, "No matching action " + action + " for " + this);
   1104             return NO_MATCH_ACTION;
   1105         }
   1106 
   1107         int dataMatch = matchData(type, scheme, data);
   1108         if (dataMatch < 0) {
   1109             if (Config.LOGV) {
   1110                 if (dataMatch == NO_MATCH_TYPE) {
   1111                     Log.v(logTag, "No matching type " + type
   1112                           + " for " + this);
   1113                 }
   1114                 if (dataMatch == NO_MATCH_DATA) {
   1115                     Log.v(logTag, "No matching scheme/path " + data
   1116                           + " for " + this);
   1117                 }
   1118             }
   1119             return dataMatch;
   1120         }
   1121 
   1122         String categoryMatch = matchCategories(categories);
   1123         if (categoryMatch != null) {
   1124             if (Config.LOGV) Log.v(
   1125                 logTag, "No matching category "
   1126                 + categoryMatch + " for " + this);
   1127             return NO_MATCH_CATEGORY;
   1128         }
   1129 
   1130         // It would be nice to treat container activities as more
   1131         // important than ones that can be embedded, but this is not the way...
   1132         if (false) {
   1133             if (categories != null) {
   1134                 dataMatch -= mCategories.size() - categories.size();
   1135             }
   1136         }
   1137 
   1138         return dataMatch;
   1139     }
   1140 
   1141     /**
   1142      * Write the contents of the IntentFilter as an XML stream.
   1143      */
   1144     public void writeToXml(XmlSerializer serializer) throws IOException {
   1145         int N = countActions();
   1146         for (int i=0; i<N; i++) {
   1147             serializer.startTag(null, ACTION_STR);
   1148             serializer.attribute(null, NAME_STR, mActions.get(i));
   1149             serializer.endTag(null, ACTION_STR);
   1150         }
   1151         N = countCategories();
   1152         for (int i=0; i<N; i++) {
   1153             serializer.startTag(null, CAT_STR);
   1154             serializer.attribute(null, NAME_STR, mCategories.get(i));
   1155             serializer.endTag(null, CAT_STR);
   1156         }
   1157         N = countDataTypes();
   1158         for (int i=0; i<N; i++) {
   1159             serializer.startTag(null, TYPE_STR);
   1160             String type = mDataTypes.get(i);
   1161             if (type.indexOf('/') < 0) type = type + "/*";
   1162             serializer.attribute(null, NAME_STR, type);
   1163             serializer.endTag(null, TYPE_STR);
   1164         }
   1165         N = countDataSchemes();
   1166         for (int i=0; i<N; i++) {
   1167             serializer.startTag(null, SCHEME_STR);
   1168             serializer.attribute(null, NAME_STR, mDataSchemes.get(i));
   1169             serializer.endTag(null, SCHEME_STR);
   1170         }
   1171         N = countDataAuthorities();
   1172         for (int i=0; i<N; i++) {
   1173             serializer.startTag(null, AUTH_STR);
   1174             AuthorityEntry ae = mDataAuthorities.get(i);
   1175             serializer.attribute(null, HOST_STR, ae.getHost());
   1176             if (ae.getPort() >= 0) {
   1177                 serializer.attribute(null, PORT_STR, Integer.toString(ae.getPort()));
   1178             }
   1179             serializer.endTag(null, AUTH_STR);
   1180         }
   1181         N = countDataPaths();
   1182         for (int i=0; i<N; i++) {
   1183             serializer.startTag(null, PATH_STR);
   1184             PatternMatcher pe = mDataPaths.get(i);
   1185             switch (pe.getType()) {
   1186                 case PatternMatcher.PATTERN_LITERAL:
   1187                     serializer.attribute(null, LITERAL_STR, pe.getPath());
   1188                     break;
   1189                 case PatternMatcher.PATTERN_PREFIX:
   1190                     serializer.attribute(null, PREFIX_STR, pe.getPath());
   1191                     break;
   1192                 case PatternMatcher.PATTERN_SIMPLE_GLOB:
   1193                     serializer.attribute(null, SGLOB_STR, pe.getPath());
   1194                     break;
   1195             }
   1196             serializer.endTag(null, PATH_STR);
   1197         }
   1198     }
   1199 
   1200     public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
   1201             IOException {
   1202         int outerDepth = parser.getDepth();
   1203         int type;
   1204         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
   1205                && (type != XmlPullParser.END_TAG
   1206                        || parser.getDepth() > outerDepth)) {
   1207             if (type == XmlPullParser.END_TAG
   1208                     || type == XmlPullParser.TEXT) {
   1209                 continue;
   1210             }
   1211 
   1212             String tagName = parser.getName();
   1213             if (tagName.equals(ACTION_STR)) {
   1214                 String name = parser.getAttributeValue(null, NAME_STR);
   1215                 if (name != null) {
   1216                     addAction(name);
   1217                 }
   1218             } else if (tagName.equals(CAT_STR)) {
   1219                 String name = parser.getAttributeValue(null, NAME_STR);
   1220                 if (name != null) {
   1221                     addCategory(name);
   1222                 }
   1223             } else if (tagName.equals(TYPE_STR)) {
   1224                 String name = parser.getAttributeValue(null, NAME_STR);
   1225                 if (name != null) {
   1226                     try {
   1227                         addDataType(name);
   1228                     } catch (MalformedMimeTypeException e) {
   1229                     }
   1230                 }
   1231             } else if (tagName.equals(SCHEME_STR)) {
   1232                 String name = parser.getAttributeValue(null, NAME_STR);
   1233                 if (name != null) {
   1234                     addDataScheme(name);
   1235                 }
   1236             } else if (tagName.equals(AUTH_STR)) {
   1237                 String host = parser.getAttributeValue(null, HOST_STR);
   1238                 String port = parser.getAttributeValue(null, PORT_STR);
   1239                 if (host != null) {
   1240                     addDataAuthority(host, port);
   1241                 }
   1242             } else if (tagName.equals(PATH_STR)) {
   1243                 String path = parser.getAttributeValue(null, LITERAL_STR);
   1244                 if (path != null) {
   1245                     addDataPath(path, PatternMatcher.PATTERN_LITERAL);
   1246                 } else if ((path=parser.getAttributeValue(null, PREFIX_STR)) != null) {
   1247                     addDataPath(path, PatternMatcher.PATTERN_PREFIX);
   1248                 } else if ((path=parser.getAttributeValue(null, SGLOB_STR)) != null) {
   1249                     addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB);
   1250                 }
   1251             } else {
   1252                 Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
   1253             }
   1254             XmlUtils.skipCurrentTag(parser);
   1255         }
   1256     }
   1257 
   1258     public void dump(Printer du, String prefix) {
   1259         StringBuilder sb = new StringBuilder(256);
   1260         if (mActions.size() > 0) {
   1261             Iterator<String> it = mActions.iterator();
   1262             while (it.hasNext()) {
   1263                 sb.setLength(0);
   1264                 sb.append(prefix); sb.append("Action: \"");
   1265                         sb.append(it.next()); sb.append("\"");
   1266                 du.println(sb.toString());
   1267             }
   1268         }
   1269         if (mCategories != null) {
   1270             Iterator<String> it = mCategories.iterator();
   1271             while (it.hasNext()) {
   1272                 sb.setLength(0);
   1273                 sb.append(prefix); sb.append("Category: \"");
   1274                         sb.append(it.next()); sb.append("\"");
   1275                 du.println(sb.toString());
   1276             }
   1277         }
   1278         if (mDataSchemes != null) {
   1279             Iterator<String> it = mDataSchemes.iterator();
   1280             while (it.hasNext()) {
   1281                 sb.setLength(0);
   1282                 sb.append(prefix); sb.append("Scheme: \"");
   1283                         sb.append(it.next()); sb.append("\"");
   1284                 du.println(sb.toString());
   1285             }
   1286         }
   1287         if (mDataAuthorities != null) {
   1288             Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
   1289             while (it.hasNext()) {
   1290                 AuthorityEntry ae = it.next();
   1291                 sb.setLength(0);
   1292                 sb.append(prefix); sb.append("Authority: \"");
   1293                         sb.append(ae.mHost); sb.append("\": ");
   1294                         sb.append(ae.mPort);
   1295                 if (ae.mWild) sb.append(" WILD");
   1296                 du.println(sb.toString());
   1297             }
   1298         }
   1299         if (mDataPaths != null) {
   1300             Iterator<PatternMatcher> it = mDataPaths.iterator();
   1301             while (it.hasNext()) {
   1302                 PatternMatcher pe = it.next();
   1303                 sb.setLength(0);
   1304                 sb.append(prefix); sb.append("Path: \"");
   1305                         sb.append(pe); sb.append("\"");
   1306                 du.println(sb.toString());
   1307             }
   1308         }
   1309         if (mDataTypes != null) {
   1310             Iterator<String> it = mDataTypes.iterator();
   1311             while (it.hasNext()) {
   1312                 sb.setLength(0);
   1313                 sb.append(prefix); sb.append("Type: \"");
   1314                         sb.append(it.next()); sb.append("\"");
   1315                 du.println(sb.toString());
   1316             }
   1317         }
   1318         if (mPriority != 0 || mHasPartialTypes) {
   1319             sb.setLength(0);
   1320             sb.append(prefix); sb.append("mPriority="); sb.append(mPriority);
   1321                     sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
   1322             du.println(sb.toString());
   1323         }
   1324     }
   1325 
   1326     public static final Parcelable.Creator<IntentFilter> CREATOR
   1327             = new Parcelable.Creator<IntentFilter>() {
   1328         public IntentFilter createFromParcel(Parcel source) {
   1329             return new IntentFilter(source);
   1330         }
   1331 
   1332         public IntentFilter[] newArray(int size) {
   1333             return new IntentFilter[size];
   1334         }
   1335     };
   1336 
   1337     public final int describeContents() {
   1338         return 0;
   1339     }
   1340 
   1341     public final void writeToParcel(Parcel dest, int flags) {
   1342         dest.writeStringList(mActions);
   1343         if (mCategories != null) {
   1344             dest.writeInt(1);
   1345             dest.writeStringList(mCategories);
   1346         } else {
   1347             dest.writeInt(0);
   1348         }
   1349         if (mDataSchemes != null) {
   1350             dest.writeInt(1);
   1351             dest.writeStringList(mDataSchemes);
   1352         } else {
   1353             dest.writeInt(0);
   1354         }
   1355         if (mDataTypes != null) {
   1356             dest.writeInt(1);
   1357             dest.writeStringList(mDataTypes);
   1358         } else {
   1359             dest.writeInt(0);
   1360         }
   1361         if (mDataAuthorities != null) {
   1362             final int N = mDataAuthorities.size();
   1363             dest.writeInt(N);
   1364             for (int i=0; i<N; i++) {
   1365                 mDataAuthorities.get(i).writeToParcel(dest);
   1366             }
   1367         } else {
   1368             dest.writeInt(0);
   1369         }
   1370         if (mDataPaths != null) {
   1371             final int N = mDataPaths.size();
   1372             dest.writeInt(N);
   1373             for (int i=0; i<N; i++) {
   1374                 mDataPaths.get(i).writeToParcel(dest, 0);
   1375             }
   1376         } else {
   1377             dest.writeInt(0);
   1378         }
   1379         dest.writeInt(mPriority);
   1380         dest.writeInt(mHasPartialTypes ? 1 : 0);
   1381     }
   1382 
   1383     /**
   1384      * For debugging -- perform a check on the filter, return true if it passed
   1385      * or false if it failed.
   1386      *
   1387      * {@hide}
   1388      */
   1389     public boolean debugCheck() {
   1390         return true;
   1391 
   1392         // This code looks for intent filters that do not specify data.
   1393         /*
   1394         if (mActions != null && mActions.size() == 1
   1395                 && mActions.contains(Intent.ACTION_MAIN)) {
   1396             return true;
   1397         }
   1398 
   1399         if (mDataTypes == null && mDataSchemes == null) {
   1400             Log.w("IntentFilter", "QUESTIONABLE INTENT FILTER:");
   1401             dump(Log.WARN, "IntentFilter", "  ");
   1402             return false;
   1403         }
   1404 
   1405         return true;
   1406         */
   1407     }
   1408 
   1409     private IntentFilter(Parcel source) {
   1410         mActions = new ArrayList<String>();
   1411         source.readStringList(mActions);
   1412         if (source.readInt() != 0) {
   1413             mCategories = new ArrayList<String>();
   1414             source.readStringList(mCategories);
   1415         }
   1416         if (source.readInt() != 0) {
   1417             mDataSchemes = new ArrayList<String>();
   1418             source.readStringList(mDataSchemes);
   1419         }
   1420         if (source.readInt() != 0) {
   1421             mDataTypes = new ArrayList<String>();
   1422             source.readStringList(mDataTypes);
   1423         }
   1424         int N = source.readInt();
   1425         if (N > 0) {
   1426             mDataAuthorities = new ArrayList<AuthorityEntry>();
   1427             for (int i=0; i<N; i++) {
   1428                 mDataAuthorities.add(new AuthorityEntry(source));
   1429             }
   1430         }
   1431         N = source.readInt();
   1432         if (N > 0) {
   1433             mDataPaths = new ArrayList<PatternMatcher>();
   1434             for (int i=0; i<N; i++) {
   1435                 mDataPaths.add(new PatternMatcher(source));
   1436             }
   1437         }
   1438         mPriority = source.readInt();
   1439         mHasPartialTypes = source.readInt() > 0;
   1440     }
   1441 
   1442     private final boolean findMimeType(String type) {
   1443         final ArrayList<String> t = mDataTypes;
   1444 
   1445         if (type == null) {
   1446             return false;
   1447         }
   1448 
   1449         if (t.contains(type)) {
   1450             return true;
   1451         }
   1452 
   1453         // Deal with an Intent wanting to match every type in the IntentFilter.
   1454         final int typeLength = type.length();
   1455         if (typeLength == 3 && type.equals("*/*")) {
   1456             return !t.isEmpty();
   1457         }
   1458 
   1459         // Deal with this IntentFilter wanting to match every Intent type.
   1460         if (mHasPartialTypes && t.contains("*")) {
   1461             return true;
   1462         }
   1463 
   1464         final int slashpos = type.indexOf('/');
   1465         if (slashpos > 0) {
   1466             if (mHasPartialTypes && t.contains(type.substring(0, slashpos))) {
   1467                 return true;
   1468             }
   1469             if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') {
   1470                 // Need to look through all types for one that matches
   1471                 // our base...
   1472                 final Iterator<String> it = t.iterator();
   1473                 while (it.hasNext()) {
   1474                     String v = it.next();
   1475                     if (type.regionMatches(0, v, 0, slashpos+1)) {
   1476                         return true;
   1477                     }
   1478                 }
   1479             }
   1480         }
   1481 
   1482         return false;
   1483     }
   1484 }
   1485