blob: 06c1ecbc020bbfc8aee8cb5db2b7c3b9b6a53e7d [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content;
18
19import org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21import org.xmlpull.v1.XmlSerializer;
22
23
24import java.io.IOException;
25import java.util.ArrayList;
26import java.util.Iterator;
27import java.util.Set;
28
29import android.net.Uri;
30import android.os.Parcel;
31import android.os.Parcelable;
32import android.os.PatternMatcher;
33import android.util.AndroidException;
34import android.util.Config;
35import android.util.Log;
36import android.util.Printer;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080037
38import com.android.internal.util.XmlUtils;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070039
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.
Dianne Hackbornb3cddae2009-04-13 16:54:00 -070090 * <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.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070093 *
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.
Dianne Hackbornb3cddae2009-04-13 16:54:00 -070098 * <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.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700101 *
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.
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700107 * <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 *
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700111 * <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 */
123public 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 *
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700353 * <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 *
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700359 * <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>();
Tom Gibara24847f32008-11-04 02:25:42 +0000370 addAction(action);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700371 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) {
Jeff Brown2c376fc2011-01-28 17:34:01 -0800464 return action != null && mActions.contains(action);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700465 }
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 *
Jeff Brown2c376fc2011-01-28 17:34:01 -0800473 * @return True if the action is listed in the filter.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700474 */
475 public final boolean matchAction(String action) {
Jeff Brown2c376fc2011-01-28 17:34:01 -0800476 return hasAction(action);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700477 }
478
479 /**
480 * Return an iterator over the filter's actions. If there are no actions,
481 * returns null.
482 */
483 public final Iterator<String> actionsIterator() {
484 return mActions != null ? mActions.iterator() : null;
485 }
486
487 /**
488 * Add a new Intent data type to match against. If any types are
489 * included in the filter, then an Intent's data must be <em>either</em>
490 * one of these types <em>or</em> a matching scheme. If no data types
491 * are included, then an Intent will only match if it specifies no data.
492 *
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700493 * <p><em>Note: MIME type matching in the Android framework is
494 * case-sensitive, unlike formal RFC MIME types. As a result,
495 * you should always write your MIME types with lower case letters,
496 * and any MIME types you receive from outside of Android should be
497 * converted to lower case before supplying them here.</em></p>
498 *
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700499 * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
500 * not syntactically correct.
501 *
502 * @param type Name of the data type to match, i.e. "vnd.android.cursor.dir/person".
503 *
504 * @see #matchData
505 */
506 public final void addDataType(String type)
507 throws MalformedMimeTypeException {
508 final int slashpos = type.indexOf('/');
509 final int typelen = type.length();
510 if (slashpos > 0 && typelen >= slashpos+2) {
511 if (mDataTypes == null) mDataTypes = new ArrayList<String>();
512 if (typelen == slashpos+2 && type.charAt(slashpos+1) == '*') {
513 String str = type.substring(0, slashpos);
514 if (!mDataTypes.contains(str)) {
515 mDataTypes.add(str.intern());
516 }
517 mHasPartialTypes = true;
518 } else {
519 if (!mDataTypes.contains(type)) {
520 mDataTypes.add(type.intern());
521 }
522 }
523 return;
524 }
525
526 throw new MalformedMimeTypeException(type);
527 }
528
529 /**
530 * Is the given data type included in the filter? Note that if the filter
531 * does not include any type, false will <em>always</em> be returned.
532 *
533 * @param type The data type to look for.
534 *
535 * @return True if the type is explicitly mentioned in the filter.
536 */
537 public final boolean hasDataType(String type) {
538 return mDataTypes != null && findMimeType(type);
539 }
540
541 /**
542 * Return the number of data types in the filter.
543 */
544 public final int countDataTypes() {
545 return mDataTypes != null ? mDataTypes.size() : 0;
546 }
547
548 /**
549 * Return a data type in the filter.
550 */
551 public final String getDataType(int index) {
552 return mDataTypes.get(index);
553 }
554
555 /**
556 * Return an iterator over the filter's data types.
557 */
558 public final Iterator<String> typesIterator() {
559 return mDataTypes != null ? mDataTypes.iterator() : null;
560 }
561
562 /**
563 * Add a new Intent data scheme to match against. If any schemes are
564 * included in the filter, then an Intent's data must be <em>either</em>
565 * one of these schemes <em>or</em> a matching data type. If no schemes
566 * are included, then an Intent will match only if it includes no data.
567 *
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700568 * <p><em>Note: scheme matching in the Android framework is
569 * case-sensitive, unlike formal RFC schemes. As a result,
570 * you should always write your schemes with lower case letters,
571 * and any schemes you receive from outside of Android should be
572 * converted to lower case before supplying them here.</em></p>
573 *
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700574 * @param scheme Name of the scheme to match, i.e. "http".
575 *
576 * @see #matchData
577 */
578 public final void addDataScheme(String scheme) {
579 if (mDataSchemes == null) mDataSchemes = new ArrayList<String>();
580 if (!mDataSchemes.contains(scheme)) {
581 mDataSchemes.add(scheme.intern());
582 }
583 }
584
585 /**
586 * Return the number of data schemes in the filter.
587 */
588 public final int countDataSchemes() {
589 return mDataSchemes != null ? mDataSchemes.size() : 0;
590 }
591
592 /**
593 * Return a data scheme in the filter.
594 */
595 public final String getDataScheme(int index) {
596 return mDataSchemes.get(index);
597 }
598
599 /**
600 * Is the given data scheme included in the filter? Note that if the
601 * filter does not include any scheme, false will <em>always</em> be
602 * returned.
603 *
604 * @param scheme The data scheme to look for.
605 *
606 * @return True if the scheme is explicitly mentioned in the filter.
607 */
608 public final boolean hasDataScheme(String scheme) {
609 return mDataSchemes != null && mDataSchemes.contains(scheme);
610 }
611
612 /**
613 * Return an iterator over the filter's data schemes.
614 */
615 public final Iterator<String> schemesIterator() {
616 return mDataSchemes != null ? mDataSchemes.iterator() : null;
617 }
618
619 /**
620 * This is an entry for a single authority in the Iterator returned by
621 * {@link #authoritiesIterator()}.
622 */
623 public final static class AuthorityEntry {
624 private final String mOrigHost;
625 private final String mHost;
626 private final boolean mWild;
627 private final int mPort;
628
629 public AuthorityEntry(String host, String port) {
630 mOrigHost = host;
631 mWild = host.length() > 0 && host.charAt(0) == '*';
632 mHost = mWild ? host.substring(1).intern() : host;
633 mPort = port != null ? Integer.parseInt(port) : -1;
634 }
635
636 AuthorityEntry(Parcel src) {
637 mOrigHost = src.readString();
638 mHost = src.readString();
639 mWild = src.readInt() != 0;
640 mPort = src.readInt();
641 }
642
643 void writeToParcel(Parcel dest) {
644 dest.writeString(mOrigHost);
645 dest.writeString(mHost);
646 dest.writeInt(mWild ? 1 : 0);
647 dest.writeInt(mPort);
648 }
649
650 public String getHost() {
651 return mOrigHost;
652 }
653
654 public int getPort() {
655 return mPort;
656 }
657
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700658 /**
659 * Determine whether this AuthorityEntry matches the given data Uri.
660 * <em>Note that this comparison is case-sensitive, unlike formal
661 * RFC host names. You thus should always normalize to lower-case.</em>
662 *
663 * @param data The Uri to match.
664 * @return Returns either {@link IntentFilter#NO_MATCH_DATA},
665 * {@link IntentFilter#MATCH_CATEGORY_PORT}, or
666 * {@link IntentFilter#MATCH_CATEGORY_HOST}.
667 */
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700668 public int match(Uri data) {
669 String host = data.getHost();
670 if (host == null) {
671 return NO_MATCH_DATA;
672 }
673 if (Config.LOGV) Log.v("IntentFilter",
674 "Match host " + host + ": " + mHost);
675 if (mWild) {
676 if (host.length() < mHost.length()) {
677 return NO_MATCH_DATA;
678 }
679 host = host.substring(host.length()-mHost.length());
680 }
681 if (host.compareToIgnoreCase(mHost) != 0) {
682 return NO_MATCH_DATA;
683 }
684 if (mPort >= 0) {
685 if (mPort != data.getPort()) {
686 return NO_MATCH_DATA;
687 }
688 return MATCH_CATEGORY_PORT;
689 }
690 return MATCH_CATEGORY_HOST;
691 }
692 };
693
694 /**
695 * Add a new Intent data authority to match against. The filter must
696 * include one or more schemes (via {@link #addDataScheme}) for the
697 * authority to be considered. If any authorities are
698 * included in the filter, then an Intent's data must match one of
699 * them. If no authorities are included, then only the scheme must match.
700 *
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700701 * <p><em>Note: host name in the Android framework is
702 * case-sensitive, unlike formal RFC host names. As a result,
703 * you should always write your host names with lower case letters,
704 * and any host names you receive from outside of Android should be
705 * converted to lower case before supplying them here.</em></p>
706 *
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700707 * @param host The host part of the authority to match. May start with a
708 * single '*' to wildcard the front of the host name.
709 * @param port Optional port part of the authority to match. If null, any
710 * port is allowed.
711 *
712 * @see #matchData
713 * @see #addDataScheme
714 */
715 public final void addDataAuthority(String host, String port) {
716 if (mDataAuthorities == null) mDataAuthorities =
717 new ArrayList<AuthorityEntry>();
718 if (port != null) port = port.intern();
719 mDataAuthorities.add(new AuthorityEntry(host.intern(), port));
720 }
721
722 /**
723 * Return the number of data authorities in the filter.
724 */
725 public final int countDataAuthorities() {
726 return mDataAuthorities != null ? mDataAuthorities.size() : 0;
727 }
728
729 /**
730 * Return a data authority in the filter.
731 */
732 public final AuthorityEntry getDataAuthority(int index) {
733 return mDataAuthorities.get(index);
734 }
735
736 /**
737 * Is the given data authority included in the filter? Note that if the
738 * filter does not include any authorities, false will <em>always</em> be
739 * returned.
740 *
741 * @param data The data whose authority is being looked for.
742 *
743 * @return Returns true if the data string matches an authority listed in the
744 * filter.
745 */
746 public final boolean hasDataAuthority(Uri data) {
747 return matchDataAuthority(data) >= 0;
748 }
749
750 /**
751 * Return an iterator over the filter's data authorities.
752 */
753 public final Iterator<AuthorityEntry> authoritiesIterator() {
754 return mDataAuthorities != null ? mDataAuthorities.iterator() : null;
755 }
756
757 /**
758 * Add a new Intent data oath to match against. The filter must
759 * include one or more schemes (via {@link #addDataScheme}) <em>and</em>
760 * one or more authorities (via {@link #addDataAuthority}) for the
761 * path to be considered. If any paths are
762 * included in the filter, then an Intent's data must match one of
763 * them. If no paths are included, then only the scheme/authority must
764 * match.
765 *
766 * <p>The path given here can either be a literal that must directly
767 * match or match against a prefix, or it can be a simple globbing pattern.
768 * If the latter, you can use '*' anywhere in the pattern to match zero
769 * or more instances of the previous character, '.' as a wildcard to match
770 * any character, and '\' to escape the next character.
771 *
772 * @param path Either a raw string that must exactly match the file
773 * path, or a simple pattern, depending on <var>type</var>.
774 * @param type Determines how <var>path</var> will be compared to
775 * determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
776 * {@link PatternMatcher#PATTERN_PREFIX}, or
777 * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
778 *
779 * @see #matchData
780 * @see #addDataScheme
781 * @see #addDataAuthority
782 */
783 public final void addDataPath(String path, int type) {
784 if (mDataPaths == null) mDataPaths = new ArrayList<PatternMatcher>();
785 mDataPaths.add(new PatternMatcher(path.intern(), type));
786 }
787
788 /**
789 * Return the number of data paths in the filter.
790 */
791 public final int countDataPaths() {
792 return mDataPaths != null ? mDataPaths.size() : 0;
793 }
794
795 /**
796 * Return a data path in the filter.
797 */
798 public final PatternMatcher getDataPath(int index) {
799 return mDataPaths.get(index);
800 }
801
802 /**
803 * Is the given data path included in the filter? Note that if the
804 * filter does not include any paths, false will <em>always</em> be
805 * returned.
806 *
807 * @param data The data path to look for. This is without the scheme
808 * prefix.
809 *
810 * @return True if the data string matches a path listed in the
811 * filter.
812 */
813 public final boolean hasDataPath(String data) {
814 if (mDataPaths == null) {
815 return false;
816 }
Jeff Brown2c376fc2011-01-28 17:34:01 -0800817 final int numDataPaths = mDataPaths.size();
818 for (int i = 0; i < numDataPaths; i++) {
819 final PatternMatcher pe = mDataPaths.get(i);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700820 if (pe.match(data)) {
821 return true;
822 }
823 }
824 return false;
825 }
826
827 /**
828 * Return an iterator over the filter's data paths.
829 */
830 public final Iterator<PatternMatcher> pathsIterator() {
831 return mDataPaths != null ? mDataPaths.iterator() : null;
832 }
833
834 /**
835 * Match this intent filter against the given Intent data. This ignores
836 * the data scheme -- unlike {@link #matchData}, the authority will match
837 * regardless of whether there is a matching scheme.
838 *
839 * @param data The data whose authority is being looked for.
840 *
841 * @return Returns either {@link #MATCH_CATEGORY_HOST},
842 * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}.
843 */
844 public final int matchDataAuthority(Uri data) {
845 if (mDataAuthorities == null) {
846 return NO_MATCH_DATA;
847 }
Jeff Brown2c376fc2011-01-28 17:34:01 -0800848 final int numDataAuthorities = mDataAuthorities.size();
849 for (int i = 0; i < numDataAuthorities; i++) {
850 final AuthorityEntry ae = mDataAuthorities.get(i);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700851 int match = ae.match(data);
852 if (match >= 0) {
853 return match;
854 }
855 }
856 return NO_MATCH_DATA;
857 }
858
859 /**
860 * Match this filter against an Intent's data (type, scheme and path). If
861 * the filter does not specify any types and does not specify any
862 * schemes/paths, the match will only succeed if the intent does not
863 * also specify a type or data.
864 *
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700865 * <p>Be aware that to match against an authority, you must also specify a base
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700866 * scheme the authority is in. To match against a data path, both a scheme
867 * and authority must be specified. If the filter does not specify any
868 * types or schemes that it matches against, it is considered to be empty
869 * (any authority or data path given is ignored, as if it were empty as
870 * well).
871 *
Dianne Hackbornb3cddae2009-04-13 16:54:00 -0700872 * <p><em>Note: MIME type, Uri scheme, and host name matching in the
873 * Android framework is case-sensitive, unlike the formal RFC definitions.
874 * As a result, you should always write these elements with lower case letters,
875 * and normalize any MIME types or Uris you receive from
876 * outside of Android to ensure these elements are lower case before
877 * supplying them here.</em></p>
878 *
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700879 * @param type The desired data type to look for, as returned by
880 * Intent.resolveType().
881 * @param scheme The desired data scheme to look for, as returned by
882 * Intent.getScheme().
883 * @param data The full data string to match against, as supplied in
884 * Intent.data.
885 *
886 * @return Returns either a valid match constant (a combination of
887 * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
888 * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match
889 * or {@link #NO_MATCH_DATA} if the scheme/path didn't match.
890 *
891 * @see #match
892 */
893 public final int matchData(String type, String scheme, Uri data) {
894 final ArrayList<String> types = mDataTypes;
895 final ArrayList<String> schemes = mDataSchemes;
896 final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
897 final ArrayList<PatternMatcher> paths = mDataPaths;
898
899 int match = MATCH_CATEGORY_EMPTY;
900
901 if (types == null && schemes == null) {
902 return ((type == null && data == null)
903 ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
904 }
905
906 if (schemes != null) {
907 if (schemes.contains(scheme != null ? scheme : "")) {
908 match = MATCH_CATEGORY_SCHEME;
909 } else {
910 return NO_MATCH_DATA;
911 }
912
913 if (authorities != null) {
914 int authMatch = matchDataAuthority(data);
915 if (authMatch >= 0) {
916 if (paths == null) {
917 match = authMatch;
918 } else if (hasDataPath(data.getPath())) {
919 match = MATCH_CATEGORY_PATH;
920 } else {
921 return NO_MATCH_DATA;
922 }
923 } else {
924 return NO_MATCH_DATA;
925 }
926 }
927 } else {
928 // Special case: match either an Intent with no data URI,
929 // or with a scheme: URI. This is to give a convenience for
930 // the common case where you want to deal with data in a
931 // content provider, which is done by type, and we don't want
932 // to force everyone to say they handle content: or file: URIs.
933 if (scheme != null && !"".equals(scheme)
934 && !"content".equals(scheme)
935 && !"file".equals(scheme)) {
936 return NO_MATCH_DATA;
937 }
938 }
939
940 if (types != null) {
941 if (findMimeType(type)) {
942 match = MATCH_CATEGORY_TYPE;
943 } else {
944 return NO_MATCH_TYPE;
945 }
946 } else {
947 // If no MIME types are specified, then we will only match against
948 // an Intent that does not have a MIME type.
949 if (type != null) {
950 return NO_MATCH_TYPE;
951 }
952 }
953
954 return match + MATCH_ADJUSTMENT_NORMAL;
955 }
956
957 /**
958 * Add a new Intent category to match against. The semantics of
959 * categories is the opposite of actions -- an Intent includes the
960 * categories that it requires, all of which must be included in the
961 * filter in order to match. In other words, adding a category to the
962 * filter has no impact on matching unless that category is specified in
963 * the intent.
964 *
965 * @param category Name of category to match, i.e. Intent.CATEGORY_EMBED.
966 */
967 public final void addCategory(String category) {
968 if (mCategories == null) mCategories = new ArrayList<String>();
969 if (!mCategories.contains(category)) {
970 mCategories.add(category.intern());
971 }
972 }
973
974 /**
975 * Return the number of categories in the filter.
976 */
977 public final int countCategories() {
978 return mCategories != null ? mCategories.size() : 0;
979 }
980
981 /**
982 * Return a category in the filter.
983 */
984 public final String getCategory(int index) {
985 return mCategories.get(index);
986 }
987
988 /**
989 * Is the given category included in the filter?
990 *
991 * @param category The category that the filter supports.
992 *
993 * @return True if the category is explicitly mentioned in the filter.
994 */
995 public final boolean hasCategory(String category) {
996 return mCategories != null && mCategories.contains(category);
997 }
998
999 /**
1000 * Return an iterator over the filter's categories.
1001 */
1002 public final Iterator<String> categoriesIterator() {
1003 return mCategories != null ? mCategories.iterator() : null;
1004 }
1005
1006 /**
1007 * Match this filter against an Intent's categories. Each category in
1008 * the Intent must be specified by the filter; if any are not in the
1009 * filter, the match fails.
1010 *
1011 * @param categories The categories included in the intent, as returned by
1012 * Intent.getCategories().
1013 *
1014 * @return If all categories match (success), null; else the name of the
1015 * first category that didn't match.
1016 */
1017 public final String matchCategories(Set<String> categories) {
1018 if (categories == null) {
1019 return null;
1020 }
1021
1022 Iterator<String> it = categories.iterator();
1023
1024 if (mCategories == null) {
1025 return it.hasNext() ? it.next() : null;
1026 }
1027
1028 while (it.hasNext()) {
1029 final String category = it.next();
1030 if (!mCategories.contains(category)) {
1031 return category;
1032 }
1033 }
1034
1035 return null;
1036 }
1037
1038 /**
1039 * Test whether this filter matches the given <var>intent</var>.
1040 *
1041 * @param intent The Intent to compare against.
1042 * @param resolve If true, the intent's type will be resolved by calling
1043 * Intent.resolveType(); otherwise a simple match against
1044 * Intent.type will be performed.
1045 * @param logTag Tag to use in debugging messages.
1046 *
1047 * @return Returns either a valid match constant (a combination of
1048 * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
1049 * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
1050 * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
1051 * {@link #NO_MATCH_ACTION if the action didn't match, or
1052 * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
1053 *
1054 * @return How well the filter matches. Negative if it doesn't match,
1055 * zero or positive positive value if it does with a higher
1056 * value representing a better match.
1057 *
1058 * @see #match(String, String, String, android.net.Uri , Set, String)
1059 */
1060 public final int match(ContentResolver resolver, Intent intent,
1061 boolean resolve, String logTag) {
1062 String type = resolve ? intent.resolveType(resolver) : intent.getType();
1063 return match(intent.getAction(), type, intent.getScheme(),
1064 intent.getData(), intent.getCategories(), logTag);
1065 }
1066
1067 /**
1068 * Test whether this filter matches the given intent data. A match is
1069 * only successful if the actions and categories in the Intent match
1070 * against the filter, as described in {@link IntentFilter}; in that case,
1071 * the match result returned will be as per {@link #matchData}.
1072 *
1073 * @param action The intent action to match against (Intent.getAction).
1074 * @param type The intent type to match against (Intent.resolveType()).
1075 * @param scheme The data scheme to match against (Intent.getScheme()).
1076 * @param data The data URI to match against (Intent.getData()).
1077 * @param categories The categories to match against
1078 * (Intent.getCategories()).
1079 * @param logTag Tag to use in debugging messages.
1080 *
1081 * @return Returns either a valid match constant (a combination of
1082 * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
1083 * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match,
1084 * {@link #NO_MATCH_DATA} if the scheme/path didn't match,
1085 * {@link #NO_MATCH_ACTION if the action didn't match, or
1086 * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match.
1087 *
1088 * @see #matchData
1089 * @see Intent#getAction
1090 * @see Intent#resolveType
1091 * @see Intent#getScheme
1092 * @see Intent#getData
1093 * @see Intent#getCategories
1094 */
1095 public final int match(String action, String type, String scheme,
1096 Uri data, Set<String> categories, String logTag) {
Jeff Brown239f77d2011-02-26 16:03:48 -08001097 if (action != null && !matchAction(action)) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001098 if (Config.LOGV) Log.v(
1099 logTag, "No matching action " + action + " for " + this);
1100 return NO_MATCH_ACTION;
1101 }
1102
1103 int dataMatch = matchData(type, scheme, data);
1104 if (dataMatch < 0) {
1105 if (Config.LOGV) {
1106 if (dataMatch == NO_MATCH_TYPE) {
1107 Log.v(logTag, "No matching type " + type
1108 + " for " + this);
1109 }
1110 if (dataMatch == NO_MATCH_DATA) {
1111 Log.v(logTag, "No matching scheme/path " + data
1112 + " for " + this);
1113 }
1114 }
1115 return dataMatch;
1116 }
1117
Jeff Brown2c376fc2011-01-28 17:34:01 -08001118 String categoryMismatch = matchCategories(categories);
1119 if (categoryMismatch != null) {
1120 if (Config.LOGV) {
1121 Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);
1122 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001123 return NO_MATCH_CATEGORY;
1124 }
1125
1126 // It would be nice to treat container activities as more
1127 // important than ones that can be embedded, but this is not the way...
1128 if (false) {
1129 if (categories != null) {
1130 dataMatch -= mCategories.size() - categories.size();
1131 }
1132 }
1133
1134 return dataMatch;
1135 }
1136
1137 /**
1138 * Write the contents of the IntentFilter as an XML stream.
1139 */
1140 public void writeToXml(XmlSerializer serializer) throws IOException {
1141 int N = countActions();
1142 for (int i=0; i<N; i++) {
1143 serializer.startTag(null, ACTION_STR);
1144 serializer.attribute(null, NAME_STR, mActions.get(i));
1145 serializer.endTag(null, ACTION_STR);
1146 }
1147 N = countCategories();
1148 for (int i=0; i<N; i++) {
1149 serializer.startTag(null, CAT_STR);
1150 serializer.attribute(null, NAME_STR, mCategories.get(i));
1151 serializer.endTag(null, CAT_STR);
1152 }
1153 N = countDataTypes();
1154 for (int i=0; i<N; i++) {
1155 serializer.startTag(null, TYPE_STR);
1156 String type = mDataTypes.get(i);
1157 if (type.indexOf('/') < 0) type = type + "/*";
1158 serializer.attribute(null, NAME_STR, type);
1159 serializer.endTag(null, TYPE_STR);
1160 }
1161 N = countDataSchemes();
1162 for (int i=0; i<N; i++) {
1163 serializer.startTag(null, SCHEME_STR);
1164 serializer.attribute(null, NAME_STR, mDataSchemes.get(i));
1165 serializer.endTag(null, SCHEME_STR);
1166 }
1167 N = countDataAuthorities();
1168 for (int i=0; i<N; i++) {
1169 serializer.startTag(null, AUTH_STR);
1170 AuthorityEntry ae = mDataAuthorities.get(i);
1171 serializer.attribute(null, HOST_STR, ae.getHost());
1172 if (ae.getPort() >= 0) {
1173 serializer.attribute(null, PORT_STR, Integer.toString(ae.getPort()));
1174 }
1175 serializer.endTag(null, AUTH_STR);
1176 }
1177 N = countDataPaths();
1178 for (int i=0; i<N; i++) {
1179 serializer.startTag(null, PATH_STR);
1180 PatternMatcher pe = mDataPaths.get(i);
1181 switch (pe.getType()) {
1182 case PatternMatcher.PATTERN_LITERAL:
1183 serializer.attribute(null, LITERAL_STR, pe.getPath());
1184 break;
1185 case PatternMatcher.PATTERN_PREFIX:
1186 serializer.attribute(null, PREFIX_STR, pe.getPath());
1187 break;
1188 case PatternMatcher.PATTERN_SIMPLE_GLOB:
1189 serializer.attribute(null, SGLOB_STR, pe.getPath());
1190 break;
1191 }
1192 serializer.endTag(null, PATH_STR);
1193 }
1194 }
1195
1196 public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
1197 IOException {
1198 int outerDepth = parser.getDepth();
1199 int type;
1200 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1201 && (type != XmlPullParser.END_TAG
1202 || parser.getDepth() > outerDepth)) {
1203 if (type == XmlPullParser.END_TAG
1204 || type == XmlPullParser.TEXT) {
1205 continue;
1206 }
1207
1208 String tagName = parser.getName();
1209 if (tagName.equals(ACTION_STR)) {
1210 String name = parser.getAttributeValue(null, NAME_STR);
1211 if (name != null) {
1212 addAction(name);
1213 }
1214 } else if (tagName.equals(CAT_STR)) {
1215 String name = parser.getAttributeValue(null, NAME_STR);
1216 if (name != null) {
1217 addCategory(name);
1218 }
1219 } else if (tagName.equals(TYPE_STR)) {
1220 String name = parser.getAttributeValue(null, NAME_STR);
1221 if (name != null) {
1222 try {
1223 addDataType(name);
1224 } catch (MalformedMimeTypeException e) {
1225 }
1226 }
1227 } else if (tagName.equals(SCHEME_STR)) {
1228 String name = parser.getAttributeValue(null, NAME_STR);
1229 if (name != null) {
1230 addDataScheme(name);
1231 }
1232 } else if (tagName.equals(AUTH_STR)) {
1233 String host = parser.getAttributeValue(null, HOST_STR);
1234 String port = parser.getAttributeValue(null, PORT_STR);
1235 if (host != null) {
1236 addDataAuthority(host, port);
1237 }
1238 } else if (tagName.equals(PATH_STR)) {
1239 String path = parser.getAttributeValue(null, LITERAL_STR);
1240 if (path != null) {
1241 addDataPath(path, PatternMatcher.PATTERN_LITERAL);
1242 } else if ((path=parser.getAttributeValue(null, PREFIX_STR)) != null) {
1243 addDataPath(path, PatternMatcher.PATTERN_PREFIX);
1244 } else if ((path=parser.getAttributeValue(null, SGLOB_STR)) != null) {
1245 addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB);
1246 }
1247 } else {
1248 Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
1249 }
1250 XmlUtils.skipCurrentTag(parser);
1251 }
1252 }
1253
1254 public void dump(Printer du, String prefix) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001255 StringBuilder sb = new StringBuilder(256);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001256 if (mActions.size() > 0) {
1257 Iterator<String> it = mActions.iterator();
1258 while (it.hasNext()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001259 sb.setLength(0);
1260 sb.append(prefix); sb.append("Action: \"");
1261 sb.append(it.next()); sb.append("\"");
1262 du.println(sb.toString());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001263 }
1264 }
1265 if (mCategories != null) {
1266 Iterator<String> it = mCategories.iterator();
1267 while (it.hasNext()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001268 sb.setLength(0);
1269 sb.append(prefix); sb.append("Category: \"");
1270 sb.append(it.next()); sb.append("\"");
1271 du.println(sb.toString());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001272 }
1273 }
1274 if (mDataSchemes != null) {
1275 Iterator<String> it = mDataSchemes.iterator();
1276 while (it.hasNext()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001277 sb.setLength(0);
1278 sb.append(prefix); sb.append("Scheme: \"");
1279 sb.append(it.next()); sb.append("\"");
1280 du.println(sb.toString());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001281 }
1282 }
1283 if (mDataAuthorities != null) {
1284 Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
1285 while (it.hasNext()) {
1286 AuthorityEntry ae = it.next();
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001287 sb.setLength(0);
1288 sb.append(prefix); sb.append("Authority: \"");
1289 sb.append(ae.mHost); sb.append("\": ");
1290 sb.append(ae.mPort);
1291 if (ae.mWild) sb.append(" WILD");
1292 du.println(sb.toString());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001293 }
1294 }
1295 if (mDataPaths != null) {
1296 Iterator<PatternMatcher> it = mDataPaths.iterator();
1297 while (it.hasNext()) {
1298 PatternMatcher pe = it.next();
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001299 sb.setLength(0);
1300 sb.append(prefix); sb.append("Path: \"");
1301 sb.append(pe); sb.append("\"");
1302 du.println(sb.toString());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001303 }
1304 }
1305 if (mDataTypes != null) {
1306 Iterator<String> it = mDataTypes.iterator();
1307 while (it.hasNext()) {
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001308 sb.setLength(0);
1309 sb.append(prefix); sb.append("Type: \"");
1310 sb.append(it.next()); sb.append("\"");
1311 du.println(sb.toString());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001312 }
1313 }
Dianne Hackborn1d442e02009-04-20 18:14:05 -07001314 if (mPriority != 0 || mHasPartialTypes) {
1315 sb.setLength(0);
1316 sb.append(prefix); sb.append("mPriority="); sb.append(mPriority);
1317 sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
1318 du.println(sb.toString());
1319 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001320 }
1321
1322 public static final Parcelable.Creator<IntentFilter> CREATOR
1323 = new Parcelable.Creator<IntentFilter>() {
1324 public IntentFilter createFromParcel(Parcel source) {
1325 return new IntentFilter(source);
1326 }
1327
1328 public IntentFilter[] newArray(int size) {
1329 return new IntentFilter[size];
1330 }
1331 };
1332
1333 public final int describeContents() {
1334 return 0;
1335 }
1336
1337 public final void writeToParcel(Parcel dest, int flags) {
1338 dest.writeStringList(mActions);
1339 if (mCategories != null) {
1340 dest.writeInt(1);
1341 dest.writeStringList(mCategories);
1342 } else {
1343 dest.writeInt(0);
1344 }
1345 if (mDataSchemes != null) {
1346 dest.writeInt(1);
1347 dest.writeStringList(mDataSchemes);
1348 } else {
1349 dest.writeInt(0);
1350 }
1351 if (mDataTypes != null) {
1352 dest.writeInt(1);
1353 dest.writeStringList(mDataTypes);
1354 } else {
1355 dest.writeInt(0);
1356 }
1357 if (mDataAuthorities != null) {
1358 final int N = mDataAuthorities.size();
1359 dest.writeInt(N);
1360 for (int i=0; i<N; i++) {
1361 mDataAuthorities.get(i).writeToParcel(dest);
1362 }
1363 } else {
1364 dest.writeInt(0);
1365 }
1366 if (mDataPaths != null) {
1367 final int N = mDataPaths.size();
1368 dest.writeInt(N);
1369 for (int i=0; i<N; i++) {
1370 mDataPaths.get(i).writeToParcel(dest, 0);
1371 }
1372 } else {
1373 dest.writeInt(0);
1374 }
1375 dest.writeInt(mPriority);
1376 dest.writeInt(mHasPartialTypes ? 1 : 0);
1377 }
1378
1379 /**
1380 * For debugging -- perform a check on the filter, return true if it passed
1381 * or false if it failed.
1382 *
1383 * {@hide}
1384 */
1385 public boolean debugCheck() {
1386 return true;
1387
1388 // This code looks for intent filters that do not specify data.
1389 /*
1390 if (mActions != null && mActions.size() == 1
1391 && mActions.contains(Intent.ACTION_MAIN)) {
1392 return true;
1393 }
1394
1395 if (mDataTypes == null && mDataSchemes == null) {
1396 Log.w("IntentFilter", "QUESTIONABLE INTENT FILTER:");
1397 dump(Log.WARN, "IntentFilter", " ");
1398 return false;
1399 }
1400
1401 return true;
1402 */
1403 }
1404
1405 private IntentFilter(Parcel source) {
1406 mActions = new ArrayList<String>();
1407 source.readStringList(mActions);
1408 if (source.readInt() != 0) {
1409 mCategories = new ArrayList<String>();
1410 source.readStringList(mCategories);
1411 }
1412 if (source.readInt() != 0) {
1413 mDataSchemes = new ArrayList<String>();
1414 source.readStringList(mDataSchemes);
1415 }
1416 if (source.readInt() != 0) {
1417 mDataTypes = new ArrayList<String>();
1418 source.readStringList(mDataTypes);
1419 }
1420 int N = source.readInt();
1421 if (N > 0) {
1422 mDataAuthorities = new ArrayList<AuthorityEntry>();
1423 for (int i=0; i<N; i++) {
1424 mDataAuthorities.add(new AuthorityEntry(source));
1425 }
1426 }
1427 N = source.readInt();
1428 if (N > 0) {
1429 mDataPaths = new ArrayList<PatternMatcher>();
1430 for (int i=0; i<N; i++) {
1431 mDataPaths.add(new PatternMatcher(source));
1432 }
1433 }
1434 mPriority = source.readInt();
1435 mHasPartialTypes = source.readInt() > 0;
1436 }
1437
1438 private final boolean findMimeType(String type) {
1439 final ArrayList<String> t = mDataTypes;
1440
1441 if (type == null) {
1442 return false;
1443 }
1444
1445 if (t.contains(type)) {
1446 return true;
1447 }
1448
1449 // Deal with an Intent wanting to match every type in the IntentFilter.
1450 final int typeLength = type.length();
1451 if (typeLength == 3 && type.equals("*/*")) {
1452 return !t.isEmpty();
1453 }
1454
1455 // Deal with this IntentFilter wanting to match every Intent type.
1456 if (mHasPartialTypes && t.contains("*")) {
1457 return true;
1458 }
1459
1460 final int slashpos = type.indexOf('/');
1461 if (slashpos > 0) {
1462 if (mHasPartialTypes && t.contains(type.substring(0, slashpos))) {
1463 return true;
1464 }
1465 if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') {
1466 // Need to look through all types for one that matches
1467 // our base...
Jeff Brown2c376fc2011-01-28 17:34:01 -08001468 final int numTypes = t.size();
1469 for (int i = 0; i < numTypes; i++) {
1470 final String v = t.get(i);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001471 if (type.regionMatches(0, v, 0, slashpos+1)) {
1472 return true;
1473 }
1474 }
1475 }
1476 }
1477
1478 return false;
1479 }
1480}