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