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