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