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