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