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