OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / content / IntentFilter.java
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 }