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