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