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