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