IntentFilter.java revision d4a4729c0cac582a2dcec7c8cfb316b81885a0f0
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content; 18 19import 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; 37import com.android.common.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 */ 122public 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 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 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 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 { 299 public MalformedMimeTypeException() { 300 } 301 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 443 public final int countActions() { 444 return mActions.size(); 445 } 446 447 /** 448 * Return an action in the filter. 449 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 547 public final int countDataTypes() { 548 return mDataTypes != null ? mDataTypes.size() : 0; 549 } 550 551 /** 552 * Return a data type in the filter. 553 */ 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 */ 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 */ 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 */ 591 public final int countDataSchemes() { 592 return mDataSchemes != null ? mDataSchemes.size() : 0; 593 } 594 595 /** 596 * Return a data scheme in the filter. 597 */ 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 */ 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 */ 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 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 639 AuthorityEntry(Parcel src) { 640 mOrigHost = src.readString(); 641 mHost = src.readString(); 642 mWild = src.readInt() != 0; 643 mPort = src.readInt(); 644 } 645 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 653 public String getHost() { 654 return mOrigHost; 655 } 656 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 */ 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 */ 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 */ 728 public final int countDataAuthorities() { 729 return mDataAuthorities != null ? mDataAuthorities.size() : 0; 730 } 731 732 /** 733 * Return a data authority in the filter. 734 */ 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 */ 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 */ 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 */ 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 */ 794 public final int countDataPaths() { 795 return mDataPaths != null ? mDataPaths.size() : 0; 796 } 797 798 /** 799 * Return a data path in the filter. 800 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 980 public final int countCategories() { 981 return mCategories != null ? mCategories.size() : 0; 982 } 983 984 /** 985 * Return a category in the filter. 986 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 1336 public final int describeContents() { 1337 return 0; 1338 } 1339 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 */ 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 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 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