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