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