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.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.compat.annotation.ChangeId; 25 import android.compat.annotation.Disabled; 26 import android.compat.annotation.Overridable; 27 import android.compat.annotation.UnsupportedAppUsage; 28 import android.content.pm.Flags; 29 import android.net.Uri; 30 import android.os.Build; 31 import android.os.Bundle; 32 import android.os.Parcel; 33 import android.os.Parcelable; 34 import android.os.PatternMatcher; 35 import android.os.PersistableBundle; 36 import android.text.TextUtils; 37 import android.util.AndroidException; 38 import android.util.ArraySet; 39 import android.util.Log; 40 import android.util.Printer; 41 import android.util.proto.ProtoOutputStream; 42 43 import com.android.internal.util.XmlUtils; 44 45 import org.xmlpull.v1.XmlPullParser; 46 import org.xmlpull.v1.XmlPullParserException; 47 import org.xmlpull.v1.XmlSerializer; 48 49 import java.io.IOException; 50 import java.lang.annotation.Retention; 51 import java.lang.annotation.RetentionPolicy; 52 import java.util.ArrayList; 53 import java.util.Collection; 54 import java.util.Iterator; 55 import java.util.List; 56 import java.util.Objects; 57 import java.util.Set; 58 import java.util.function.BiConsumer; 59 import java.util.function.Predicate; 60 61 /** 62 * Structured description of Intent values to be matched. An IntentFilter can 63 * match against actions, categories, and data (either via its type, scheme, 64 * and/or path) in an Intent. It also includes a "priority" value which is 65 * used to order multiple matching filters. 66 * 67 * <p>IntentFilter objects are often created in XML as part of a package's 68 * {@link android.R.styleable#AndroidManifest AndroidManifest.xml} file, 69 * using {@link android.R.styleable#AndroidManifestIntentFilter intent-filter} 70 * tags. 71 * 72 * <p>There are three Intent characteristics you can filter on: the 73 * <em>action</em>, <em>data</em>, and <em>categories</em>. For each of these 74 * characteristics you can provide 75 * multiple possible matching values (via {@link #addAction}, 76 * {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart}, 77 * {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively). 78 * For actions, if no data characteristics are specified, then the filter will 79 * only match intents that contain no data. 80 * 81 * <p>The data characteristic is 82 * itself divided into three attributes: type, scheme, authority, and path. 83 * Any that are 84 * specified must match the contents of the Intent. If you specify a scheme 85 * but no type, only Intent that does not have a type (such as mailto:) will 86 * match; a content: URI will never match because they always have a MIME type 87 * that is supplied by their content provider. Specifying a type with no scheme 88 * has somewhat special meaning: it will match either an Intent with no URI 89 * field, or an Intent with a content: or file: URI. If you specify neither, 90 * then only an Intent with no data or type will match. To specify an authority, 91 * you must also specify one or more schemes that it is associated with. 92 * To specify a path, you also must specify both one or more authorities and 93 * one or more schemes it is associated with. 94 * 95 * <div class="special reference"> 96 * <h3>Developer Guides</h3> 97 * <p>For information about how to create and resolve intents, read the 98 * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> 99 * developer guide.</p> 100 * </div> 101 * 102 * <h3>Filter Rules</h3> 103 * <p>A match is based on the following rules. Note that 104 * for an IntentFilter to match an Intent, three conditions must hold: 105 * the <strong>action</strong> and <strong>category</strong> must match, and 106 * the data (both the <strong>data type</strong> and 107 * <strong>data scheme+authority+path</strong> if specified) must match 108 * (see {@link #match(ContentResolver, Intent, boolean, String)} for more details 109 * on how the data fields match). 110 * 111 * <p><strong>Action</strong> matches if any of the given values match the 112 * Intent action; if the filter specifies no actions, then it will only match 113 * Intents that do not contain an action. 114 * 115 * <p><strong>Data Type</strong> matches if any of the given values match the 116 * Intent type. The Intent 117 * type is determined by calling {@link Intent#resolveType}. A wildcard can be 118 * used for the MIME sub-type, in both the Intent and IntentFilter, so that the 119 * type "audio/*" will match "audio/mpeg", "audio/aiff", "audio/*", etc. 120 * <em>Note that MIME type matching here is <b>case sensitive</b>, unlike 121 * formal RFC MIME types!</em> You should thus always use lower case letters 122 * for your MIME types. 123 * 124 * <p><strong>Data Scheme</strong> matches if any of the given values match the 125 * Intent data's scheme. 126 * The Intent scheme is determined by calling {@link Intent#getData} 127 * and {@link android.net.Uri#getScheme} on that URI. 128 * <em>Note that scheme matching here is <b>case sensitive</b>, unlike 129 * formal RFC schemes!</em> You should thus always use lower case letters 130 * for your schemes. 131 * 132 * <p><strong>Data Scheme Specific Part</strong> matches if any of the given values match 133 * the Intent's data scheme specific part <em>and</em> one of the data schemes in the filter 134 * has matched the Intent, <em>or</em> no scheme specific parts were supplied in the filter. 135 * The Intent scheme specific part is determined by calling 136 * {@link Intent#getData} and {@link android.net.Uri#getSchemeSpecificPart} on that URI. 137 * <em>Note that scheme specific part matching is <b>case sensitive</b>.</em> 138 * 139 * <p><strong>Data Authority</strong> matches if any of the given values match 140 * the Intent's data authority <em>and</em> one of the data schemes in the filter 141 * has matched the Intent, <em>or</em> no authorities were supplied in the filter. 142 * The Intent authority is determined by calling 143 * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI. 144 * <em>Note that authority matching here is <b>case sensitive</b>, unlike 145 * formal RFC host names!</em> You should thus always use lower case letters 146 * for your authority. 147 * 148 * <p><strong>Data Path</strong> matches if any of the given values match the 149 * Intent's data path <em>and</em> both a scheme and authority in the filter 150 * has matched against the Intent, <em>or</em> no paths were supplied in the 151 * filter. The Intent authority is determined by calling 152 * {@link Intent#getData} and {@link android.net.Uri#getPath} on that URI. 153 * 154 * <p><strong>Categories</strong> match if <em>all</em> of the categories in 155 * the Intent match categories given in the filter. Extra categories in the 156 * filter that are not in the Intent will not cause the match to fail. Note 157 * that unlike the action, an IntentFilter with no categories 158 * will only match an Intent that does not have any categories. 159 */ 160 @android.ravenwood.annotation.RavenwoodKeepWholeClass 161 public class IntentFilter implements Parcelable { 162 private static final String TAG = "IntentFilter"; 163 164 private static final String AGLOB_STR = "aglob"; 165 private static final String SGLOB_STR = "sglob"; 166 private static final String PREFIX_STR = "prefix"; 167 private static final String SUFFIX_STR = "suffix"; 168 private static final String LITERAL_STR = "literal"; 169 private static final String PATH_STR = "path"; 170 private static final String PORT_STR = "port"; 171 private static final String HOST_STR = "host"; 172 private static final String AUTH_STR = "auth"; 173 private static final String SSP_STR = "ssp"; 174 private static final String SCHEME_STR = "scheme"; 175 private static final String STATIC_TYPE_STR = "staticType"; 176 private static final String TYPE_STR = "type"; 177 private static final String GROUP_STR = "group"; 178 private static final String CAT_STR = "cat"; 179 private static final String NAME_STR = "name"; 180 private static final String ACTION_STR = "action"; 181 private static final String AUTO_VERIFY_STR = "autoVerify"; 182 private static final String EXTRAS_STR = "extras"; 183 private static final String URI_RELATIVE_FILTER_GROUP_STR = "uriRelativeFilterGroup"; 184 185 private static final int[] EMPTY_INT_ARRAY = new int[0]; 186 private static final long[] EMPTY_LONG_ARRAY = new long[0]; 187 private static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; 188 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 189 private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; 190 191 /** 192 * An intent with action set as null used to always pass the action test during intent 193 * filter matching. This causes a lot of confusion and unexpected intent matches. 194 * Null action intents should be blocked when the intent sender application targets V or higher. 195 * 196 * @hide 197 */ 198 @ChangeId 199 @Disabled 200 @Overridable 201 public static final long BLOCK_NULL_ACTION_INTENTS = 293560872; 202 203 /** 204 * The filter {@link #setPriority} value at which system high-priority 205 * receivers are placed; that is, receivers that should execute before 206 * application code. Applications should never use filters with this or 207 * higher priorities. 208 * 209 * @see #setPriority 210 */ 211 public static final int SYSTEM_HIGH_PRIORITY = 1000; 212 213 /** 214 * The filter {@link #setPriority} value at which system low-priority 215 * receivers are placed; that is, receivers that should execute after 216 * application code. Applications should never use filters with this or 217 * lower priorities. 218 * 219 * @see #setPriority 220 */ 221 public static final int SYSTEM_LOW_PRIORITY = -1000; 222 223 /** 224 * The part of a match constant that describes the category of match 225 * that occurred. May be either {@link #MATCH_CATEGORY_EMPTY}, 226 * {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_SCHEME_SPECIFIC_PART}, 227 * {@link #MATCH_CATEGORY_HOST}, {@link #MATCH_CATEGORY_PORT}, 228 * {@link #MATCH_CATEGORY_PATH}, or {@link #MATCH_CATEGORY_TYPE}. Higher 229 * values indicate a better match. 230 */ 231 public static final int MATCH_CATEGORY_MASK = 0xfff0000; 232 233 /** 234 * The part of a match constant that applies a quality adjustment to the 235 * basic category of match. The value {@link #MATCH_ADJUSTMENT_NORMAL} 236 * is no adjustment; higher numbers than that improve the quality, while 237 * lower numbers reduce it. 238 */ 239 public static final int MATCH_ADJUSTMENT_MASK = 0x000ffff; 240 241 /** 242 * Quality adjustment applied to the category of match that signifies 243 * the default, base value; higher numbers improve the quality while 244 * lower numbers reduce it. 245 */ 246 public static final int MATCH_ADJUSTMENT_NORMAL = 0x8000; 247 248 /** 249 * The filter matched an intent that had no data specified. 250 */ 251 public static final int MATCH_CATEGORY_EMPTY = 0x0100000; 252 /** 253 * The filter matched an intent with the same data URI scheme. 254 */ 255 public static final int MATCH_CATEGORY_SCHEME = 0x0200000; 256 /** 257 * The filter matched an intent with the same data URI scheme and 258 * authority host. 259 */ 260 public static final int MATCH_CATEGORY_HOST = 0x0300000; 261 /** 262 * The filter matched an intent with the same data URI scheme and 263 * authority host and port. 264 */ 265 public static final int MATCH_CATEGORY_PORT = 0x0400000; 266 /** 267 * The filter matched an intent with the same data URI scheme, 268 * authority, and path. 269 */ 270 public static final int MATCH_CATEGORY_PATH = 0x0500000; 271 /** 272 * The filter matched an intent with the same data URI scheme and 273 * scheme specific part. 274 */ 275 public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0x0580000; 276 /** 277 * The filter matched an intent with the same data MIME type. 278 */ 279 public static final int MATCH_CATEGORY_TYPE = 0x0600000; 280 281 /** 282 * The filter didn't match due to different MIME types. 283 */ 284 public static final int NO_MATCH_TYPE = -1; 285 /** 286 * The filter didn't match due to different data URIs. 287 */ 288 public static final int NO_MATCH_DATA = -2; 289 /** 290 * The filter didn't match due to different actions. 291 */ 292 public static final int NO_MATCH_ACTION = -3; 293 /** 294 * The filter didn't match because it required one or more categories 295 * that were not in the Intent. 296 */ 297 public static final int NO_MATCH_CATEGORY = -4; 298 /** 299 * That filter didn't match due to different extras data. 300 * @hide 301 */ 302 public static final int NO_MATCH_EXTRAS = -5; 303 304 /** 305 * HTTP scheme. 306 * 307 * @see #addDataScheme(String) 308 * @hide 309 */ 310 public static final String SCHEME_HTTP = "http"; 311 /** 312 * HTTPS scheme. 313 * 314 * @see #addDataScheme(String) 315 * @hide 316 */ 317 public static final String SCHEME_HTTPS = "https"; 318 319 /** 320 * Package scheme 321 * 322 * @see #addDataScheme(String) 323 * @hide 324 */ 325 public static final String SCHEME_PACKAGE = "package"; 326 327 /** 328 * The value to indicate a wildcard for incoming match arguments. 329 * @hide 330 */ 331 public static final String WILDCARD = "*"; 332 /** @hide */ 333 public static final String WILDCARD_PATH = "/" + WILDCARD; 334 335 private int mPriority; 336 @UnsupportedAppUsage 337 private int mOrder; 338 @UnsupportedAppUsage 339 private final ArraySet<String> mActions; 340 private ArrayList<String> mCategories = null; 341 private ArrayList<String> mDataSchemes = null; 342 private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null; 343 private ArrayList<AuthorityEntry> mDataAuthorities = null; 344 private ArrayList<PatternMatcher> mDataPaths = null; 345 private ArrayList<UriRelativeFilterGroup> mUriRelativeFilterGroups = null; 346 private ArrayList<String> mStaticDataTypes = null; 347 private ArrayList<String> mDataTypes = null; 348 private ArrayList<String> mMimeGroups = null; 349 private boolean mHasStaticPartialTypes = false; 350 private boolean mHasDynamicPartialTypes = false; 351 private PersistableBundle mExtras = null; 352 353 private static final int STATE_VERIFY_AUTO = 0x00000001; 354 private static final int STATE_NEED_VERIFY = 0x00000010; 355 private static final int STATE_NEED_VERIFY_CHECKED = 0x00000100; 356 private static final int STATE_VERIFIED = 0x00001000; 357 358 private int mVerifyState; 359 /** @hide */ 360 public static final int VISIBILITY_NONE = 0; 361 /** @hide */ 362 public static final int VISIBILITY_EXPLICIT = 1; 363 /** @hide */ 364 public static final int VISIBILITY_IMPLICIT = 2; 365 /** @hide */ 366 @IntDef(prefix = { "VISIBILITY_" }, value = { 367 VISIBILITY_NONE, 368 VISIBILITY_EXPLICIT, 369 VISIBILITY_IMPLICIT, 370 }) 371 @Retention(RetentionPolicy.SOURCE) 372 public @interface InstantAppVisibility {} 373 /** Whether or not the intent filter is visible to instant apps. */ 374 private @InstantAppVisibility int mInstantAppVisibility; 375 // These functions are the start of more optimized code for managing 376 // the string sets... not yet implemented. 377 findStringInSet(String[] set, String string, int[] lengths, int lenPos)378 private static int findStringInSet(String[] set, String string, 379 int[] lengths, int lenPos) { 380 if (set == null) return -1; 381 final int N = lengths[lenPos]; 382 for (int i=0; i<N; i++) { 383 if (set[i].equals(string)) return i; 384 } 385 return -1; 386 } 387 addStringToSet(String[] set, String string, int[] lengths, int lenPos)388 private static String[] addStringToSet(String[] set, String string, 389 int[] lengths, int lenPos) { 390 if (findStringInSet(set, string, lengths, lenPos) >= 0) return set; 391 if (set == null) { 392 set = new String[2]; 393 set[0] = string; 394 lengths[lenPos] = 1; 395 return set; 396 } 397 final int N = lengths[lenPos]; 398 if (N < set.length) { 399 set[N] = string; 400 lengths[lenPos] = N+1; 401 return set; 402 } 403 404 String[] newSet = new String[(N*3)/2 + 2]; 405 System.arraycopy(set, 0, newSet, 0, N); 406 set = newSet; 407 set[N] = string; 408 lengths[lenPos] = N+1; 409 return set; 410 } 411 removeStringFromSet(String[] set, String string, int[] lengths, int lenPos)412 private static String[] removeStringFromSet(String[] set, String string, 413 int[] lengths, int lenPos) { 414 int pos = findStringInSet(set, string, lengths, lenPos); 415 if (pos < 0) return set; 416 final int N = lengths[lenPos]; 417 if (N > (set.length/4)) { 418 int copyLen = N-(pos+1); 419 if (copyLen > 0) { 420 System.arraycopy(set, pos+1, set, pos, copyLen); 421 } 422 set[N-1] = null; 423 lengths[lenPos] = N-1; 424 return set; 425 } 426 427 String[] newSet = new String[set.length/3]; 428 if (pos > 0) System.arraycopy(set, 0, newSet, 0, pos); 429 if ((pos+1) < N) System.arraycopy(set, pos+1, newSet, pos, N-(pos+1)); 430 return newSet; 431 } 432 433 /** 434 * This exception is thrown when a given MIME type does not have a valid 435 * syntax. 436 */ 437 public static class MalformedMimeTypeException extends AndroidException { MalformedMimeTypeException()438 public MalformedMimeTypeException() { 439 } 440 MalformedMimeTypeException(String name)441 public MalformedMimeTypeException(String name) { 442 super(name); 443 } 444 } 445 446 /** 447 * Create a new IntentFilter instance with a specified action and MIME 448 * type, where you know the MIME type is correctly formatted. This catches 449 * the {@link MalformedMimeTypeException} exception that the constructor 450 * can call and turns it into a runtime exception. 451 * 452 * @param action The action to match, such as Intent.ACTION_VIEW. 453 * @param dataType The type to match, such as "vnd.android.cursor.dir/person". 454 * 455 * @return A new IntentFilter for the given action and type. 456 * 457 * @see #IntentFilter(String, String) 458 */ create(String action, String dataType)459 public static IntentFilter create(String action, String dataType) { 460 try { 461 return new IntentFilter(action, dataType); 462 } catch (MalformedMimeTypeException e) { 463 throw new RuntimeException("Bad MIME type", e); 464 } 465 } 466 467 /** 468 * New empty IntentFilter. 469 */ IntentFilter()470 public IntentFilter() { 471 mPriority = 0; 472 mActions = new ArraySet<>(); 473 } 474 475 /** 476 * New IntentFilter that matches a single action with no data. If 477 * no data characteristics are subsequently specified, then the 478 * filter will only match intents that contain no data. 479 * 480 * @param action The action to match, such as Intent.ACTION_MAIN. 481 */ IntentFilter(String action)482 public IntentFilter(String action) { 483 mPriority = 0; 484 mActions = new ArraySet<>(); 485 addAction(action); 486 } 487 488 /** 489 * New IntentFilter that matches a single action and data type. 490 * 491 * <p><em>Note: MIME type matching in the Android framework is 492 * case-sensitive, unlike formal RFC MIME types. As a result, 493 * you should always write your MIME types with lower case letters, 494 * and any MIME types you receive from outside of Android should be 495 * converted to lower case before supplying them here.</em></p> 496 * 497 * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is 498 * not syntactically correct. 499 * 500 * @param action The action to match, such as Intent.ACTION_VIEW. 501 * @param dataType The type to match, such as "vnd.android.cursor.dir/person". 502 * 503 */ IntentFilter(String action, String dataType)504 public IntentFilter(String action, String dataType) 505 throws MalformedMimeTypeException { 506 mPriority = 0; 507 mActions = new ArraySet<>(); 508 addAction(action); 509 addDataType(dataType); 510 } 511 512 /** 513 * New IntentFilter containing a copy of an existing filter. 514 * 515 * @param o The original filter to copy. 516 */ IntentFilter(IntentFilter o)517 public IntentFilter(IntentFilter o) { 518 mPriority = o.mPriority; 519 mOrder = o.mOrder; 520 mActions = new ArraySet<>(o.mActions); 521 if (o.mCategories != null) { 522 mCategories = new ArrayList<String>(o.mCategories); 523 } 524 if (o.mStaticDataTypes != null) { 525 mStaticDataTypes = new ArrayList<String>(o.mStaticDataTypes); 526 } 527 if (o.mDataTypes != null) { 528 mDataTypes = new ArrayList<String>(o.mDataTypes); 529 } 530 if (o.mDataSchemes != null) { 531 mDataSchemes = new ArrayList<String>(o.mDataSchemes); 532 } 533 if (o.mDataSchemeSpecificParts != null) { 534 mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(o.mDataSchemeSpecificParts); 535 } 536 if (o.mDataAuthorities != null) { 537 mDataAuthorities = new ArrayList<AuthorityEntry>(o.mDataAuthorities); 538 } 539 if (o.mDataPaths != null) { 540 mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths); 541 } 542 if (o.mUriRelativeFilterGroups != null) { 543 mUriRelativeFilterGroups = 544 new ArrayList<UriRelativeFilterGroup>(o.mUriRelativeFilterGroups); 545 } 546 if (o.mMimeGroups != null) { 547 mMimeGroups = new ArrayList<String>(o.mMimeGroups); 548 } 549 if (o.mExtras != null) { 550 mExtras = new PersistableBundle(o.mExtras); 551 } 552 mHasStaticPartialTypes = o.mHasStaticPartialTypes; 553 mHasDynamicPartialTypes = o.mHasDynamicPartialTypes; 554 mVerifyState = o.mVerifyState; 555 mInstantAppVisibility = o.mInstantAppVisibility; 556 } 557 558 /** @hide */ toLongString()559 public String toLongString() { 560 // Not implemented directly as toString() due to potential memory regression 561 final StringBuilder sb = new StringBuilder(); 562 sb.append("IntentFilter {"); 563 sb.append(" pri="); 564 sb.append(mPriority); 565 if (countActions() > 0) { 566 sb.append(" act="); 567 sb.append(mActions.toString()); 568 } 569 if (countCategories() > 0) { 570 sb.append(" cat="); 571 sb.append(mCategories.toString()); 572 } 573 if (countDataSchemes() > 0) { 574 sb.append(" sch="); 575 sb.append(mDataSchemes.toString()); 576 } 577 if (Flags.relativeReferenceIntentFilters() && countUriRelativeFilterGroups() > 0) { 578 sb.append(" grp="); 579 sb.append(mUriRelativeFilterGroups.toString()); 580 } 581 sb.append(" }"); 582 return sb.toString(); 583 } 584 585 /** 586 * Modify priority of this filter. This only affects receiver filters. 587 * The priority of activity filters are set in XML and cannot be changed 588 * programmatically. The default priority is 0. Positive values will be 589 * before the default, lower values will be after it. Applications should 590 * use a value that is larger than {@link #SYSTEM_LOW_PRIORITY} and 591 * smaller than {@link #SYSTEM_HIGH_PRIORITY} . 592 * 593 * @param priority The new priority value. 594 * 595 * @see #getPriority 596 * @see #SYSTEM_LOW_PRIORITY 597 * @see #SYSTEM_HIGH_PRIORITY 598 */ setPriority(int priority)599 public final void setPriority(int priority) { 600 mPriority = priority; 601 } 602 603 /** 604 * Return the priority of this filter. 605 * 606 * @return The priority of the filter. 607 * 608 * @see #setPriority 609 */ getPriority()610 public final int getPriority() { 611 return mPriority; 612 } 613 614 /** @hide */ 615 @SystemApi setOrder(int order)616 public final void setOrder(int order) { 617 mOrder = order; 618 } 619 620 /** @hide */ 621 @SystemApi getOrder()622 public final int getOrder() { 623 return mOrder; 624 } 625 626 /** 627 * Set whether this filter will needs to be automatically verified against its data URIs or not. 628 * The default is false. 629 * 630 * The verification would need to happen only and only if the Intent action is 631 * {@link android.content.Intent#ACTION_VIEW} and the Intent category is 632 * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme 633 * is "http" or "https". 634 * 635 * True means that the filter will need to use its data URIs to be verified. 636 * 637 * @param autoVerify The new autoVerify value. 638 * 639 * @see #getAutoVerify() 640 * @see #addAction(String) 641 * @see #getAction(int) 642 * @see #addCategory(String) 643 * @see #getCategory(int) 644 * @see #addDataScheme(String) 645 * @see #getDataScheme(int) 646 * 647 * @hide 648 */ 649 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) setAutoVerify(boolean autoVerify)650 public final void setAutoVerify(boolean autoVerify) { 651 mVerifyState &= ~STATE_VERIFY_AUTO; 652 if (autoVerify) mVerifyState |= STATE_VERIFY_AUTO; 653 } 654 655 /** 656 * Return if this filter will needs to be automatically verified again its data URIs or not. 657 * 658 * @return True if the filter will needs to be automatically verified. False otherwise. 659 * 660 * @see #setAutoVerify(boolean) 661 * 662 * @hide 663 */ getAutoVerify()664 public final boolean getAutoVerify() { 665 return ((mVerifyState & STATE_VERIFY_AUTO) == STATE_VERIFY_AUTO); 666 } 667 668 /** 669 * Return if this filter handle all HTTP or HTTPS data URI or not. This is the 670 * core check for whether a given activity qualifies as a "browser". 671 * 672 * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise. 673 * 674 * This will check if: 675 * 676 * - either the Intent category is {@link android.content.Intent#CATEGORY_APP_BROWSER} 677 * - either the Intent action is {@link android.content.Intent#ACTION_VIEW} and 678 * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent 679 * data scheme is "http" or "https" and that there is no specific host defined. 680 * 681 * @hide 682 */ handleAllWebDataURI()683 public final boolean handleAllWebDataURI() { 684 return hasCategory(Intent.CATEGORY_APP_BROWSER) || 685 (handlesWebUris(false) && countDataAuthorities() == 0); 686 } 687 688 /** 689 * Return if this filter handles HTTP or HTTPS data URIs. 690 * 691 * @return True if the filter handles ACTION_VIEW/CATEGORY_BROWSABLE, 692 * has at least one HTTP or HTTPS data URI pattern defined, and optionally 693 * does not define any non-http/https data URI patterns. 694 * 695 * This will check if the Intent action is {@link android.content.Intent#ACTION_VIEW} and 696 * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent 697 * data scheme is "http" or "https". 698 * 699 * @param onlyWebSchemes When true, requires that the intent filter declare 700 * that it handles *only* http: or https: schemes. This is a requirement for 701 * the intent filter's domain linkage being verifiable. 702 * @hide 703 */ handlesWebUris(boolean onlyWebSchemes)704 public final boolean handlesWebUris(boolean onlyWebSchemes) { 705 // Require ACTION_VIEW, CATEGORY_BROWSEABLE, and at least one scheme 706 if (!hasAction(Intent.ACTION_VIEW) 707 || !hasCategory(Intent.CATEGORY_BROWSABLE) 708 || mDataSchemes == null 709 || mDataSchemes.size() == 0) { 710 return false; 711 } 712 713 // Now allow only the schemes "http" and "https" 714 final int N = mDataSchemes.size(); 715 for (int i = 0; i < N; i++) { 716 final String scheme = mDataSchemes.get(i); 717 final boolean isWebScheme = 718 SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme); 719 if (onlyWebSchemes) { 720 // If we're specifically trying to ensure that there are no non-web schemes 721 // declared in this filter, then if we ever see a non-http/https scheme then 722 // we know it's a failure. 723 if (!isWebScheme) { 724 return false; 725 } 726 } else { 727 // If we see any http/https scheme declaration in this case then the 728 // filter matches what we're looking for. 729 if (isWebScheme) { 730 return true; 731 } 732 } 733 } 734 735 // We get here if: 736 // 1) onlyWebSchemes and no non-web schemes were found, i.e. success; or 737 // 2) !onlyWebSchemes and no http/https schemes were found, i.e. failure. 738 return onlyWebSchemes; 739 } 740 741 /** 742 * Return if this filter needs to be automatically verified again its data URIs or not. 743 * 744 * @return True if the filter needs to be automatically verified. False otherwise. 745 * 746 * This will check if the Intent action is {@link android.content.Intent#ACTION_VIEW} and 747 * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent 748 * data scheme is "http" or "https". 749 * 750 * @see #setAutoVerify(boolean) 751 * 752 * @hide 753 */ needsVerification()754 public final boolean needsVerification() { 755 return getAutoVerify() && handlesWebUris(true); 756 } 757 758 /** 759 * Return if this filter has been verified 760 * 761 * @return true if the filter has been verified or if autoVerify is false. 762 * 763 * @hide 764 */ 765 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) isVerified()766 public final boolean isVerified() { 767 if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) { 768 return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY); 769 } 770 return false; 771 } 772 773 /** 774 * Set if this filter has been verified 775 * 776 * @param verified true if this filter has been verified. False otherwise. 777 * 778 * @hide 779 */ setVerified(boolean verified)780 public void setVerified(boolean verified) { 781 mVerifyState |= STATE_NEED_VERIFY_CHECKED; 782 mVerifyState &= ~STATE_VERIFIED; 783 if (verified) mVerifyState |= STATE_VERIFIED; 784 } 785 786 /** @hide */ setVisibilityToInstantApp(@nstantAppVisibility int visibility)787 public void setVisibilityToInstantApp(@InstantAppVisibility int visibility) { 788 mInstantAppVisibility = visibility; 789 } 790 /** @hide */ getVisibilityToInstantApp()791 public @InstantAppVisibility int getVisibilityToInstantApp() { 792 return mInstantAppVisibility; 793 } 794 /** @hide */ isVisibleToInstantApp()795 public boolean isVisibleToInstantApp() { 796 return mInstantAppVisibility != VISIBILITY_NONE; 797 } 798 /** @hide */ isExplicitlyVisibleToInstantApp()799 public boolean isExplicitlyVisibleToInstantApp() { 800 return mInstantAppVisibility == VISIBILITY_EXPLICIT; 801 } 802 /** @hide */ isImplicitlyVisibleToInstantApp()803 public boolean isImplicitlyVisibleToInstantApp() { 804 return mInstantAppVisibility == VISIBILITY_IMPLICIT; 805 } 806 807 /** 808 * Add a new Intent action to match against. If any actions are included 809 * in the filter, then an Intent's action must be one of those values for 810 * it to match. If no actions are included, the Intent action is ignored. 811 * 812 * @param action Name of the action to match, such as Intent.ACTION_VIEW. 813 */ addAction(String action)814 public final void addAction(String action) { 815 mActions.add(action.intern()); 816 } 817 818 /** 819 * Return the number of actions in the filter. 820 */ countActions()821 public final int countActions() { 822 return mActions.size(); 823 } 824 825 /** 826 * Returns the number of actions in the filter, or {@code 0} if there are no actions. 827 * <p> This method provides a safe alternative to {@link #countActions()}, which 828 * may throw an exception if there are no actions. 829 * @hide 830 */ safeCountActions()831 public final int safeCountActions() { 832 return mActions == null ? 0 : mActions.size(); 833 } 834 835 /** 836 * Return an action in the filter. 837 */ getAction(int index)838 public final String getAction(int index) { 839 return mActions.valueAt(index); 840 } 841 842 /** 843 * Is the given action included in the filter? Note that if the filter 844 * does not include any actions, false will <em>always</em> be returned. 845 * 846 * @param action The action to look for. 847 * 848 * @return True if the action is explicitly mentioned in the filter. 849 */ hasAction(String action)850 public final boolean hasAction(String action) { 851 return action != null && mActions.contains(action); 852 } 853 854 /** 855 * Match this filter against an Intent's action. If the filter does not 856 * specify any actions, the match will always fail. 857 * 858 * @param action The desired action to look for. 859 * 860 * @return True if the action is listed in the filter. 861 */ matchAction(String action)862 public final boolean matchAction(String action) { 863 return matchAction(action, false /*wildcardSupported*/, null /*ignoreActions*/); 864 } 865 866 /** 867 * Variant of {@link #matchAction(String)} that allows a wildcard for the provided action. 868 * @param wildcardSupported if true, will allow action to use wildcards 869 * @param ignoreActions if not null, the set of actions to should not be considered valid while 870 * calculating the match 871 */ matchAction(String action, boolean wildcardSupported, @Nullable Collection<String> ignoreActions)872 private boolean matchAction(String action, boolean wildcardSupported, 873 @Nullable Collection<String> ignoreActions) { 874 if (wildcardSupported && WILDCARD.equals(action)) { 875 if (ignoreActions == null) { 876 return !mActions.isEmpty(); 877 } 878 if (mActions.size() > ignoreActions.size()) { 879 return true; // some actions are definitely not ignored 880 } 881 for (int i = mActions.size() - 1; i >= 0; i--) { 882 if (!ignoreActions.contains(mActions.valueAt(i))) { 883 return true; 884 } 885 } 886 return false; 887 } 888 if (ignoreActions != null && ignoreActions.contains(action)) { 889 return false; 890 } 891 return hasAction(action); 892 } 893 894 /** 895 * Return an iterator over the filter's actions. If there are no actions, 896 * returns null. 897 */ actionsIterator()898 public final Iterator<String> actionsIterator() { 899 return mActions != null ? mActions.iterator() : null; 900 } 901 902 /** 903 * Add a new Intent data type to match against. If any types are 904 * included in the filter, then an Intent's data must be <em>either</em> 905 * one of these types <em>or</em> a matching scheme. If no data types 906 * are included, then an Intent will only match if it specifies no data. 907 * 908 * <p><em>Note: MIME type matching in the Android framework is 909 * case-sensitive, unlike formal RFC MIME types. As a result, 910 * you should always write your MIME types with lower case letters, 911 * and any MIME types you receive from outside of Android should be 912 * converted to lower case before supplying them here.</em></p> 913 * 914 * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is 915 * not syntactically correct. 916 * 917 * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person". 918 * 919 * @see #matchData 920 */ addDataType(String type)921 public final void addDataType(String type) 922 throws MalformedMimeTypeException { 923 processMimeType(type, (internalType, isPartial) -> { 924 if (mDataTypes == null) { 925 mDataTypes = new ArrayList<>(); 926 } 927 if (mStaticDataTypes == null) { 928 mStaticDataTypes = new ArrayList<>(); 929 } 930 931 if (mDataTypes.contains(internalType)) { 932 return; 933 } 934 935 mDataTypes.add(internalType.intern()); 936 mStaticDataTypes.add(internalType.intern()); 937 mHasStaticPartialTypes = mHasStaticPartialTypes || isPartial; 938 }); 939 } 940 941 /** 942 * Add a new Intent data type <em>from MIME group</em> to match against. If any types are 943 * included in the filter, then an Intent's data must be <em>either</em> 944 * one of these types <em>or</em> a matching scheme. If no data types 945 * are included, then an Intent will only match if it specifies no data. 946 * 947 * <p><em>Note: MIME type matching in the Android framework is 948 * case-sensitive, unlike formal RFC MIME types. As a result, 949 * you should always write your MIME types with lower case letters, 950 * and any MIME types you receive from outside of Android should be 951 * converted to lower case before supplying them here.</em></p> 952 * 953 * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is 954 * not syntactically correct. 955 * 956 * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person". 957 * 958 * @see #clearDynamicDataTypes() 959 * @hide 960 */ addDynamicDataType(String type)961 public final void addDynamicDataType(String type) 962 throws MalformedMimeTypeException { 963 processMimeType(type, (internalType, isPartial) -> { 964 if (mDataTypes == null) { 965 mDataTypes = new ArrayList<>(); 966 } 967 968 if (!mDataTypes.contains(internalType)) { 969 mDataTypes.add(internalType.intern()); 970 971 mHasDynamicPartialTypes = mHasDynamicPartialTypes || isPartial; 972 } 973 }); 974 } 975 976 /** 977 * Process mime type - convert to representation used internally and check if type is partial, 978 * and then call provided action 979 */ processMimeType(String type, BiConsumer<String, Boolean> action)980 private void processMimeType(String type, BiConsumer<String, Boolean> action) 981 throws MalformedMimeTypeException { 982 final int slashpos = type.indexOf('/'); 983 final int typelen = type.length(); 984 if (slashpos <= 0 || typelen < slashpos + 2) { 985 throw new MalformedMimeTypeException(type); 986 } 987 988 String internalType = type; 989 boolean isPartialType = false; 990 if (typelen == slashpos + 2 && type.charAt(slashpos + 1) == '*') { 991 internalType = type.substring(0, slashpos); 992 isPartialType = true; 993 } 994 995 action.accept(internalType, isPartialType); 996 } 997 998 /** 999 * Remove all previously added Intent data types from IntentFilter. 1000 * 1001 * @see #addDynamicDataType(String) 1002 * @hide 1003 */ clearDynamicDataTypes()1004 public final void clearDynamicDataTypes() { 1005 if (mDataTypes == null) { 1006 return; 1007 } 1008 1009 if (mStaticDataTypes != null) { 1010 mDataTypes.clear(); 1011 mDataTypes.addAll(mStaticDataTypes); 1012 } else { 1013 mDataTypes = null; 1014 } 1015 1016 mHasDynamicPartialTypes = false; 1017 } 1018 1019 /** 1020 * Return the number of static data types in the filter. 1021 * @hide 1022 */ countStaticDataTypes()1023 public int countStaticDataTypes() { 1024 return mStaticDataTypes != null ? mStaticDataTypes.size() : 0; 1025 } 1026 1027 /** 1028 * Is the given data type included in the filter? Note that if the filter 1029 * does not include any type, false will <em>always</em> be returned. 1030 * 1031 * @param type The data type to look for. 1032 * 1033 * @return True if the type is explicitly mentioned in the filter. 1034 */ hasDataType(String type)1035 public final boolean hasDataType(String type) { 1036 return mDataTypes != null && findMimeType(type); 1037 } 1038 1039 /** @hide */ 1040 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) hasExactDataType(String type)1041 public final boolean hasExactDataType(String type) { 1042 return mDataTypes != null && mDataTypes.contains(type); 1043 } 1044 1045 /** @hide */ hasExactDynamicDataType(String type)1046 public final boolean hasExactDynamicDataType(String type) { 1047 return hasExactDataType(type) && !hasExactStaticDataType(type); 1048 } 1049 1050 /** @hide */ hasExactStaticDataType(String type)1051 public final boolean hasExactStaticDataType(String type) { 1052 return mStaticDataTypes != null && mStaticDataTypes.contains(type); 1053 } 1054 1055 /** 1056 * Return the number of data types in the filter. 1057 */ countDataTypes()1058 public final int countDataTypes() { 1059 return mDataTypes != null ? mDataTypes.size() : 0; 1060 } 1061 1062 /** 1063 * Return a data type in the filter. 1064 */ getDataType(int index)1065 public final String getDataType(int index) { 1066 return mDataTypes.get(index); 1067 } 1068 1069 /** 1070 * Return an iterator over the filter's data types. 1071 */ typesIterator()1072 public final Iterator<String> typesIterator() { 1073 return mDataTypes != null ? mDataTypes.iterator() : null; 1074 } 1075 1076 /** 1077 * Return copy of filter's data types. 1078 * @hide 1079 */ dataTypes()1080 public final List<String> dataTypes() { 1081 return mDataTypes != null ? new ArrayList<>(mDataTypes) : null; 1082 } 1083 1084 /** @hide */ addMimeGroup(String name)1085 public final void addMimeGroup(String name) { 1086 if (mMimeGroups == null) { 1087 mMimeGroups = new ArrayList<>(); 1088 } 1089 if (!mMimeGroups.contains(name)) { 1090 mMimeGroups.add(name); 1091 } 1092 } 1093 1094 /** @hide */ hasMimeGroup(String name)1095 public final boolean hasMimeGroup(String name) { 1096 return mMimeGroups != null && mMimeGroups.contains(name); 1097 } 1098 1099 /** @hide */ getMimeGroup(int index)1100 public final String getMimeGroup(int index) { 1101 return mMimeGroups.get(index); 1102 } 1103 1104 /** @hide */ countMimeGroups()1105 public final int countMimeGroups() { 1106 return mMimeGroups != null ? mMimeGroups.size() : 0; 1107 } 1108 1109 /** @hide */ mimeGroupsIterator()1110 public final Iterator<String> mimeGroupsIterator() { 1111 return mMimeGroups != null ? mMimeGroups.iterator() : null; 1112 } 1113 1114 /** 1115 * Add a new Intent data scheme to match against. If any schemes are 1116 * included in the filter, then an Intent's data must be <em>either</em> 1117 * one of these schemes <em>or</em> a matching data type. If no schemes 1118 * are included, then an Intent will match only if it includes no data. 1119 * 1120 * <p><em>Note: scheme matching in the Android framework is 1121 * case-sensitive, unlike formal RFC schemes. As a result, 1122 * you should always write your schemes with lower case letters, 1123 * and any schemes you receive from outside of Android should be 1124 * converted to lower case before supplying them here.</em></p> 1125 * 1126 * @param scheme Name of the scheme to match, such as "http". 1127 * 1128 * @see #matchData 1129 */ addDataScheme(String scheme)1130 public final void addDataScheme(String scheme) { 1131 if (mDataSchemes == null) mDataSchemes = new ArrayList<String>(); 1132 if (!mDataSchemes.contains(scheme)) { 1133 mDataSchemes.add(scheme.intern()); 1134 } 1135 } 1136 1137 /** 1138 * Return the number of data schemes in the filter. 1139 */ countDataSchemes()1140 public final int countDataSchemes() { 1141 return mDataSchemes != null ? mDataSchemes.size() : 0; 1142 } 1143 1144 /** 1145 * Return a data scheme in the filter. 1146 */ getDataScheme(int index)1147 public final String getDataScheme(int index) { 1148 return mDataSchemes.get(index); 1149 } 1150 1151 /** 1152 * Is the given data scheme included in the filter? Note that if the 1153 * filter does not include any scheme, false will <em>always</em> be 1154 * returned. 1155 * 1156 * @param scheme The data scheme to look for. 1157 * 1158 * @return True if the scheme is explicitly mentioned in the filter. 1159 */ hasDataScheme(String scheme)1160 public final boolean hasDataScheme(String scheme) { 1161 return mDataSchemes != null && mDataSchemes.contains(scheme); 1162 } 1163 1164 /** 1165 * Return an iterator over the filter's data schemes. 1166 */ schemesIterator()1167 public final Iterator<String> schemesIterator() { 1168 return mDataSchemes != null ? mDataSchemes.iterator() : null; 1169 } 1170 1171 /** 1172 * This is an entry for a single authority in the Iterator returned by 1173 * {@link #authoritiesIterator()}. 1174 */ 1175 public final static class AuthorityEntry { 1176 private final String mOrigHost; 1177 private final String mHost; 1178 private final boolean mWild; 1179 private final int mPort; 1180 AuthorityEntry(String host, String port)1181 public AuthorityEntry(String host, String port) { 1182 mOrigHost = host; 1183 mWild = host.length() > 0 && host.charAt(0) == '*'; 1184 mHost = mWild ? host.substring(1).intern() : host; 1185 mPort = port != null ? Integer.parseInt(port) : -1; 1186 } 1187 AuthorityEntry(Parcel src)1188 AuthorityEntry(Parcel src) { 1189 mOrigHost = src.readString(); 1190 mHost = src.readString(); 1191 mWild = src.readInt() != 0; 1192 mPort = src.readInt(); 1193 } 1194 writeToParcel(Parcel dest)1195 void writeToParcel(Parcel dest) { 1196 dest.writeString(mOrigHost); 1197 dest.writeString(mHost); 1198 dest.writeInt(mWild ? 1 : 0); 1199 dest.writeInt(mPort); 1200 } 1201 dumpDebug(ProtoOutputStream proto, long fieldId)1202 void dumpDebug(ProtoOutputStream proto, long fieldId) { 1203 long token = proto.start(fieldId); 1204 // The original host information is already contained in host and wild, no output now. 1205 proto.write(AuthorityEntryProto.HOST, mHost); 1206 proto.write(AuthorityEntryProto.WILD, mWild); 1207 proto.write(AuthorityEntryProto.PORT, mPort); 1208 proto.end(token); 1209 } 1210 getHost()1211 public String getHost() { 1212 return mOrigHost; 1213 } 1214 getPort()1215 public int getPort() { 1216 return mPort; 1217 } 1218 1219 /** @hide */ match(AuthorityEntry other)1220 public boolean match(AuthorityEntry other) { 1221 if (mWild != other.mWild) { 1222 return false; 1223 } 1224 if (!mHost.equals(other.mHost)) { 1225 return false; 1226 } 1227 if (mPort != other.mPort) { 1228 return false; 1229 } 1230 return true; 1231 } 1232 1233 @Override equals(@ullable Object obj)1234 public boolean equals(@Nullable Object obj) { 1235 if (obj instanceof AuthorityEntry) { 1236 final AuthorityEntry other = (AuthorityEntry)obj; 1237 return match(other); 1238 } 1239 return false; 1240 } 1241 1242 /** 1243 * Determine whether this AuthorityEntry matches the given data Uri. 1244 * <em>Note that this comparison is case-sensitive, unlike formal 1245 * RFC host names. You thus should always normalize to lower-case.</em> 1246 * 1247 * @param data The Uri to match. 1248 * @return Returns either {@link IntentFilter#NO_MATCH_DATA}, 1249 * {@link IntentFilter#MATCH_CATEGORY_PORT}, or 1250 * {@link IntentFilter#MATCH_CATEGORY_HOST}. 1251 */ match(Uri data)1252 public int match(Uri data) { 1253 return match(data, false); 1254 } 1255 1256 /** 1257 * Variant of {@link #match(Uri)} that supports wildcards on the scheme, host and 1258 * path of the provided {@link Uri} 1259 * 1260 * @param wildcardSupported if true, will allow parameters to use wildcards 1261 * @hide 1262 */ match(Uri data, boolean wildcardSupported)1263 public int match(Uri data, boolean wildcardSupported) { 1264 String host = data.getHost(); 1265 if (host == null) { 1266 if (wildcardSupported && mWild && mHost.isEmpty()) { 1267 // special case, if no host is provided, but the Authority is wildcard, match 1268 return MATCH_CATEGORY_HOST; 1269 } else { 1270 return NO_MATCH_DATA; 1271 } 1272 } 1273 if (false) Log.v("IntentFilter", 1274 "Match host " + host + ": " + mHost); 1275 if (!wildcardSupported || !WILDCARD.equals(host)) { 1276 if (mWild) { 1277 if (host.length() < mHost.length()) { 1278 return NO_MATCH_DATA; 1279 } 1280 host = host.substring(host.length() - mHost.length()); 1281 } 1282 if (host.compareToIgnoreCase(mHost) != 0) { 1283 return NO_MATCH_DATA; 1284 } 1285 } 1286 // if we're dealing with wildcard support, we ignore ports 1287 if (!wildcardSupported && mPort >= 0) { 1288 if (mPort != data.getPort()) { 1289 return NO_MATCH_DATA; 1290 } 1291 return MATCH_CATEGORY_PORT; 1292 } 1293 return MATCH_CATEGORY_HOST; 1294 } 1295 } 1296 1297 /** 1298 * Add a new Intent data "scheme specific part" to match against. The filter must 1299 * include one or more schemes (via {@link #addDataScheme}) for the 1300 * scheme specific part to be considered. If any scheme specific parts are 1301 * included in the filter, then an Intent's data must match one of 1302 * them. If no scheme specific parts are included, then only the scheme must match. 1303 * 1304 * <p>The "scheme specific part" that this matches against is the string returned 1305 * by {@link android.net.Uri#getSchemeSpecificPart() Uri.getSchemeSpecificPart}. 1306 * For Uris that contain a path, this kind of matching is not generally of interest, 1307 * since {@link #addDataAuthority(String, String)} and 1308 * {@link #addDataPath(String, int)} can provide a better mechanism for matching 1309 * them. However, for Uris that do not contain a path, the authority and path 1310 * are empty, so this is the only way to match against the non-scheme part.</p> 1311 * 1312 * @param ssp Either a raw string that must exactly match the scheme specific part 1313 * path, or a simple pattern, depending on <var>type</var>. 1314 * @param type Determines how <var>ssp</var> will be compared to 1315 * determine a match: either {@link PatternMatcher#PATTERN_LITERAL}, 1316 * {@link PatternMatcher#PATTERN_PREFIX}, 1317 * {@link PatternMatcher#PATTERN_SUFFIX}, or 1318 * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}. 1319 * 1320 * @see #matchData 1321 * @see #addDataScheme 1322 */ addDataSchemeSpecificPart(String ssp, int type)1323 public final void addDataSchemeSpecificPart(String ssp, int type) { 1324 addDataSchemeSpecificPart(new PatternMatcher(ssp, type)); 1325 } 1326 1327 /** @hide */ addDataSchemeSpecificPart(PatternMatcher ssp)1328 public final void addDataSchemeSpecificPart(PatternMatcher ssp) { 1329 if (mDataSchemeSpecificParts == null) { 1330 mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(); 1331 } 1332 mDataSchemeSpecificParts.add(ssp); 1333 } 1334 1335 /** 1336 * Return the number of data scheme specific parts in the filter. 1337 */ countDataSchemeSpecificParts()1338 public final int countDataSchemeSpecificParts() { 1339 return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.size() : 0; 1340 } 1341 1342 /** 1343 * Return a data scheme specific part in the filter. 1344 */ getDataSchemeSpecificPart(int index)1345 public final PatternMatcher getDataSchemeSpecificPart(int index) { 1346 return mDataSchemeSpecificParts.get(index); 1347 } 1348 1349 /** 1350 * Is the given data scheme specific part included in the filter? Note that if the 1351 * filter does not include any scheme specific parts, false will <em>always</em> be 1352 * returned. 1353 * 1354 * @param data The scheme specific part that is being looked for. 1355 * 1356 * @return Returns true if the data string matches a scheme specific part listed in the 1357 * filter. 1358 */ hasDataSchemeSpecificPart(String data)1359 public final boolean hasDataSchemeSpecificPart(String data) { 1360 return hasDataSchemeSpecificPart(data, false); 1361 } 1362 1363 /** 1364 * Variant of {@link #hasDataSchemeSpecificPart(String)} that supports wildcards on the provided 1365 * ssp. 1366 * @param supportWildcards if true, will allow parameters to use wildcards 1367 */ hasDataSchemeSpecificPart(String data, boolean supportWildcards)1368 private boolean hasDataSchemeSpecificPart(String data, boolean supportWildcards) { 1369 if (mDataSchemeSpecificParts == null) { 1370 return false; 1371 } 1372 if (supportWildcards && WILDCARD.equals(data) && mDataSchemeSpecificParts.size() > 0) { 1373 return true; 1374 } 1375 final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size(); 1376 for (int i = 0; i < numDataSchemeSpecificParts; i++) { 1377 final PatternMatcher pe = mDataSchemeSpecificParts.get(i); 1378 if (pe.match(data)) { 1379 return true; 1380 } 1381 } 1382 return false; 1383 } 1384 1385 /** @hide */ 1386 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) hasDataSchemeSpecificPart(PatternMatcher ssp)1387 public final boolean hasDataSchemeSpecificPart(PatternMatcher ssp) { 1388 if (mDataSchemeSpecificParts == null) { 1389 return false; 1390 } 1391 final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size(); 1392 for (int i = 0; i < numDataSchemeSpecificParts; i++) { 1393 final PatternMatcher pe = mDataSchemeSpecificParts.get(i); 1394 if (pe.getType() == ssp.getType() && pe.getPath().equals(ssp.getPath())) { 1395 return true; 1396 } 1397 } 1398 return false; 1399 } 1400 1401 /** 1402 * Return an iterator over the filter's data scheme specific parts. 1403 */ schemeSpecificPartsIterator()1404 public final Iterator<PatternMatcher> schemeSpecificPartsIterator() { 1405 return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.iterator() : null; 1406 } 1407 1408 /** 1409 * Add a new Intent data authority to match against. The filter must 1410 * include one or more schemes (via {@link #addDataScheme}) for the 1411 * authority to be considered. If any authorities are 1412 * included in the filter, then an Intent's data must match one of 1413 * them. If no authorities are included, then only the scheme must match. 1414 * 1415 * <p><em>Note: host name in the Android framework is 1416 * case-sensitive, unlike formal RFC host names. As a result, 1417 * you should always write your host names with lower case letters, 1418 * and any host names you receive from outside of Android should be 1419 * converted to lower case before supplying them here.</em></p> 1420 * 1421 * @param host The host part of the authority to match. May start with a 1422 * single '*' to wildcard the front of the host name. 1423 * @param port Optional port part of the authority to match. If null, any 1424 * port is allowed. 1425 * 1426 * @see #matchData 1427 * @see #addDataScheme 1428 */ addDataAuthority(String host, String port)1429 public final void addDataAuthority(String host, String port) { 1430 if (port != null) port = port.intern(); 1431 addDataAuthority(new AuthorityEntry(host.intern(), port)); 1432 } 1433 1434 /** @hide */ addDataAuthority(AuthorityEntry ent)1435 public final void addDataAuthority(AuthorityEntry ent) { 1436 if (mDataAuthorities == null) mDataAuthorities = 1437 new ArrayList<AuthorityEntry>(); 1438 mDataAuthorities.add(ent); 1439 } 1440 1441 /** 1442 * Return the number of data authorities in the filter. 1443 */ countDataAuthorities()1444 public final int countDataAuthorities() { 1445 return mDataAuthorities != null ? mDataAuthorities.size() : 0; 1446 } 1447 1448 /** 1449 * Return a data authority in the filter. 1450 */ getDataAuthority(int index)1451 public final AuthorityEntry getDataAuthority(int index) { 1452 return mDataAuthorities.get(index); 1453 } 1454 1455 /** 1456 * Is the given data authority included in the filter? Note that if the 1457 * filter does not include any authorities, false will <em>always</em> be 1458 * returned. 1459 * 1460 * @param data The data whose authority is being looked for. 1461 * 1462 * @return Returns true if the data string matches an authority listed in the 1463 * filter. 1464 */ hasDataAuthority(Uri data)1465 public final boolean hasDataAuthority(Uri data) { 1466 return matchDataAuthority(data) >= 0; 1467 } 1468 1469 /** @hide */ 1470 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) hasDataAuthority(AuthorityEntry auth)1471 public final boolean hasDataAuthority(AuthorityEntry auth) { 1472 if (mDataAuthorities == null) { 1473 return false; 1474 } 1475 final int numDataAuthorities = mDataAuthorities.size(); 1476 for (int i = 0; i < numDataAuthorities; i++) { 1477 if (mDataAuthorities.get(i).match(auth)) { 1478 return true; 1479 } 1480 } 1481 return false; 1482 } 1483 1484 /** 1485 * Return an iterator over the filter's data authorities. 1486 */ authoritiesIterator()1487 public final Iterator<AuthorityEntry> authoritiesIterator() { 1488 return mDataAuthorities != null ? mDataAuthorities.iterator() : null; 1489 } 1490 1491 /** 1492 * Add a new Intent data path to match against. The filter must 1493 * include one or more schemes (via {@link #addDataScheme}) <em>and</em> 1494 * one or more authorities (via {@link #addDataAuthority}) for the 1495 * path to be considered. If any paths are 1496 * included in the filter, then an Intent's data must match one of 1497 * them. If no paths are included, then only the scheme/authority must 1498 * match. 1499 * 1500 * <p>The path given here can either be a literal that must directly 1501 * match or match against a prefix, or it can be a simple globbing pattern. 1502 * If the latter, you can use '*' anywhere in the pattern to match zero 1503 * or more instances of the previous character, '.' as a wildcard to match 1504 * any character, and '\' to escape the next character. 1505 * 1506 * @param path Either a raw string that must exactly match the file 1507 * path, or a simple pattern, depending on <var>type</var>. 1508 * @param type Determines how <var>path</var> will be compared to 1509 * determine a match: either {@link PatternMatcher#PATTERN_LITERAL}, 1510 * {@link PatternMatcher#PATTERN_PREFIX}, 1511 * {@link PatternMatcher#PATTERN_SUFFIX}, or 1512 * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}. 1513 * 1514 * @see #matchData 1515 * @see #addDataScheme 1516 * @see #addDataAuthority 1517 */ addDataPath(String path, int type)1518 public final void addDataPath(String path, int type) { 1519 addDataPath(new PatternMatcher(path.intern(), type)); 1520 } 1521 1522 /** @hide */ addDataPath(PatternMatcher path)1523 public final void addDataPath(PatternMatcher path) { 1524 if (mDataPaths == null) mDataPaths = new ArrayList<PatternMatcher>(); 1525 mDataPaths.add(path); 1526 } 1527 1528 /** 1529 * Return the number of data paths in the filter. 1530 */ countDataPaths()1531 public final int countDataPaths() { 1532 return mDataPaths != null ? mDataPaths.size() : 0; 1533 } 1534 1535 /** 1536 * Return a data path in the filter. 1537 */ getDataPath(int index)1538 public final PatternMatcher getDataPath(int index) { 1539 return mDataPaths.get(index); 1540 } 1541 1542 /** 1543 * Is the given data path included in the filter? Note that if the 1544 * filter does not include any paths, false will <em>always</em> be 1545 * returned. 1546 * 1547 * @param data The data path to look for. This is without the scheme 1548 * prefix. 1549 * 1550 * @return True if the data string matches a path listed in the 1551 * filter. 1552 */ hasDataPath(String data)1553 public final boolean hasDataPath(String data) { 1554 return hasDataPath(data, false); 1555 } 1556 1557 /** 1558 * Variant of {@link #hasDataPath(String)} that supports wildcards on the provided scheme, host, 1559 * and path. 1560 * @param wildcardSupported if true, will allow parameters to use wildcards 1561 */ hasDataPath(String data, boolean wildcardSupported)1562 private boolean hasDataPath(String data, boolean wildcardSupported) { 1563 if (mDataPaths == null) { 1564 return false; 1565 } 1566 if (wildcardSupported && WILDCARD_PATH.equals(data)) { 1567 return true; 1568 } 1569 final int numDataPaths = mDataPaths.size(); 1570 for (int i = 0; i < numDataPaths; i++) { 1571 final PatternMatcher pe = mDataPaths.get(i); 1572 if (pe.match(data)) { 1573 return true; 1574 } 1575 } 1576 return false; 1577 } 1578 1579 /** @hide */ 1580 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) hasDataPath(PatternMatcher path)1581 public final boolean hasDataPath(PatternMatcher path) { 1582 if (mDataPaths == null) { 1583 return false; 1584 } 1585 final int numDataPaths = mDataPaths.size(); 1586 for (int i = 0; i < numDataPaths; i++) { 1587 final PatternMatcher pe = mDataPaths.get(i); 1588 if (pe.getType() == path.getType() && pe.getPath().equals(path.getPath())) { 1589 return true; 1590 } 1591 } 1592 return false; 1593 } 1594 1595 /** 1596 * Return an iterator over the filter's data paths. 1597 */ pathsIterator()1598 public final Iterator<PatternMatcher> pathsIterator() { 1599 return mDataPaths != null ? mDataPaths.iterator() : null; 1600 } 1601 1602 /** 1603 * Add a new URI relative filter group to match against the Intent data. The 1604 * intent filter must include one or more schemes (via {@link #addDataScheme}) 1605 * <em>and</em> one or more authorities (via {@link #addDataAuthority}) for 1606 * the group to be considered. 1607 * 1608 * <p>Groups will be matched in the order they were added and matching will only 1609 * be done if no data paths match or if none are included. If both data paths and 1610 * groups are not included, then only the scheme/authority must match.</p> 1611 * 1612 * @param group A {@link UriRelativeFilterGroup} to match the URI. 1613 * 1614 * @see UriRelativeFilterGroup 1615 * @see #matchData 1616 * @see #addDataScheme 1617 * @see #addDataAuthority 1618 */ 1619 @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) addUriRelativeFilterGroup(@onNull UriRelativeFilterGroup group)1620 public final void addUriRelativeFilterGroup(@NonNull UriRelativeFilterGroup group) { 1621 Objects.requireNonNull(group); 1622 if (mUriRelativeFilterGroups == null) { 1623 mUriRelativeFilterGroups = new ArrayList<>(); 1624 } 1625 mUriRelativeFilterGroups.add(group); 1626 } 1627 1628 /** 1629 * Return the number of URI relative filter groups in the intent filter. 1630 */ 1631 @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) countUriRelativeFilterGroups()1632 public final int countUriRelativeFilterGroups() { 1633 return mUriRelativeFilterGroups == null ? 0 : mUriRelativeFilterGroups.size(); 1634 } 1635 1636 /** 1637 * Return a URI relative filter group in the intent filter. 1638 * 1639 * <p>Note: use of this method will result in a NullPointerException 1640 * if no groups exists for this intent filter.</p> 1641 * 1642 * @param index index of the element to return 1643 * @throws IndexOutOfBoundsException if index is out of range 1644 */ 1645 @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) 1646 @NonNull getUriRelativeFilterGroup(int index)1647 public final UriRelativeFilterGroup getUriRelativeFilterGroup(int index) { 1648 return mUriRelativeFilterGroups.get(index); 1649 } 1650 1651 /** 1652 * Removes all existing URI relative filter groups in the intent filter. 1653 */ 1654 @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) clearUriRelativeFilterGroups()1655 public final void clearUriRelativeFilterGroups() { 1656 mUriRelativeFilterGroups = null; 1657 } 1658 1659 /** 1660 * Match this intent filter against the given Intent data. This ignores 1661 * the data scheme -- unlike {@link #matchData}, the authority will match 1662 * regardless of whether there is a matching scheme. 1663 * 1664 * @param data The data whose authority is being looked for. 1665 * 1666 * @return Returns either {@link #MATCH_CATEGORY_HOST}, 1667 * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}. 1668 */ matchDataAuthority(Uri data)1669 public final int matchDataAuthority(Uri data) { 1670 return matchDataAuthority(data, false); 1671 } 1672 1673 /** 1674 * Variant of {@link #matchDataAuthority(Uri)} that allows wildcard matching of the provided 1675 * authority. 1676 * 1677 * @param wildcardSupported if true, will allow parameters to use wildcards 1678 * @hide 1679 */ matchDataAuthority(Uri data, boolean wildcardSupported)1680 public final int matchDataAuthority(Uri data, boolean wildcardSupported) { 1681 if (data == null || mDataAuthorities == null) { 1682 return NO_MATCH_DATA; 1683 } 1684 final int numDataAuthorities = mDataAuthorities.size(); 1685 for (int i = 0; i < numDataAuthorities; i++) { 1686 final AuthorityEntry ae = mDataAuthorities.get(i); 1687 int match = ae.match(data, wildcardSupported); 1688 if (match >= 0) { 1689 return match; 1690 } 1691 } 1692 return NO_MATCH_DATA; 1693 } 1694 1695 /** 1696 * Match this filter against an Intent's data (type, scheme and path). If 1697 * the filter does not specify any types and does not specify any 1698 * schemes/paths, the match will only succeed if the intent does not 1699 * also specify a type or data. If the filter does not specify any schemes, 1700 * it will implicitly match intents with no scheme, or the schemes "content:" 1701 * or "file:" (basically performing a MIME-type only match). If the filter 1702 * does not specify any MIME types, the Intent also must not specify a MIME 1703 * type. 1704 * 1705 * <p>Be aware that to match against an authority, you must also specify a base 1706 * scheme the authority is in. To match against a data path, both a scheme 1707 * and authority must be specified. If the filter does not specify any 1708 * types or schemes that it matches against, it is considered to be empty 1709 * (any authority or data path given is ignored, as if it were empty as 1710 * well). 1711 * 1712 * <p><em>Note: MIME type, Uri scheme, and host name matching in the 1713 * Android framework is case-sensitive, unlike the formal RFC definitions. 1714 * As a result, you should always write these elements with lower case letters, 1715 * and normalize any MIME types or Uris you receive from 1716 * outside of Android to ensure these elements are lower case before 1717 * supplying them here.</em></p> 1718 * 1719 * @param type The desired data type to look for, as returned by 1720 * Intent.resolveType(). 1721 * @param scheme The desired data scheme to look for, as returned by 1722 * Intent.getScheme(). 1723 * @param data The full data string to match against, as supplied in 1724 * Intent.data. 1725 * 1726 * @return Returns either a valid match constant (a combination of 1727 * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}), 1728 * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match 1729 * or {@link #NO_MATCH_DATA} if the scheme/path didn't match. 1730 * 1731 * @see #match 1732 */ matchData(String type, String scheme, Uri data)1733 public final int matchData(String type, String scheme, Uri data) { 1734 return matchData(type, scheme, data, false); 1735 } 1736 1737 /** 1738 * Variant of {@link #matchData(String, String, Uri)} that allows wildcard matching 1739 * of the provided type, scheme, host and path parameters. 1740 * @param wildcardSupported if true, will allow parameters to use wildcards 1741 */ matchData(String type, String scheme, Uri data, boolean wildcardSupported)1742 private int matchData(String type, String scheme, Uri data, boolean wildcardSupported) { 1743 final boolean wildcardWithMimegroups = wildcardSupported && countMimeGroups() != 0; 1744 final List<String> types = mDataTypes; 1745 final ArrayList<String> schemes = mDataSchemes; 1746 1747 int match = MATCH_CATEGORY_EMPTY; 1748 1749 if (!wildcardWithMimegroups && types == null && schemes == null) { 1750 return ((type == null && data == null) 1751 ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA); 1752 } 1753 1754 if (schemes != null) { 1755 if (schemes.contains(scheme != null ? scheme : "") 1756 || wildcardSupported && WILDCARD.equals(scheme)) { 1757 match = MATCH_CATEGORY_SCHEME; 1758 } else { 1759 return NO_MATCH_DATA; 1760 } 1761 1762 final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts; 1763 if (schemeSpecificParts != null && data != null) { 1764 match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart(), wildcardSupported) 1765 ? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA; 1766 } 1767 if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) { 1768 // If there isn't any matching ssp, we need to match an authority. 1769 final ArrayList<AuthorityEntry> authorities = mDataAuthorities; 1770 if (authorities != null) { 1771 int authMatch = matchDataAuthority(data, wildcardSupported); 1772 if (authMatch >= 0) { 1773 final ArrayList<PatternMatcher> paths = mDataPaths; 1774 final ArrayList<UriRelativeFilterGroup> groups = mUriRelativeFilterGroups; 1775 if (Flags.relativeReferenceIntentFilters()) { 1776 if (paths == null && groups == null) { 1777 match = authMatch; 1778 } else if (hasDataPath(data.getPath(), wildcardSupported) 1779 || matchRelRefGroups(data)) { 1780 match = MATCH_CATEGORY_PATH; 1781 } else { 1782 return NO_MATCH_DATA; 1783 } 1784 } else { 1785 if (paths == null) { 1786 match = authMatch; 1787 } else if (hasDataPath(data.getPath(), wildcardSupported)) { 1788 match = MATCH_CATEGORY_PATH; 1789 } else { 1790 return NO_MATCH_DATA; 1791 } 1792 } 1793 } else { 1794 return NO_MATCH_DATA; 1795 } 1796 } 1797 } 1798 // If neither an ssp nor an authority matched, we're done. 1799 if (match == NO_MATCH_DATA) { 1800 return NO_MATCH_DATA; 1801 } 1802 } else { 1803 // Special case: match either an Intent with no data URI, 1804 // or with a scheme: URI. This is to give a convenience for 1805 // the common case where you want to deal with data in a 1806 // content provider, which is done by type, and we don't want 1807 // to force everyone to say they handle content: or file: URIs. 1808 if (scheme != null && !"".equals(scheme) 1809 && !"content".equals(scheme) 1810 && !"file".equals(scheme) 1811 && !(wildcardSupported && WILDCARD.equals(scheme))) { 1812 return NO_MATCH_DATA; 1813 } 1814 } 1815 1816 if (wildcardWithMimegroups) { 1817 return MATCH_CATEGORY_TYPE; 1818 } else if (types != null) { 1819 if (findMimeType(type)) { 1820 match = MATCH_CATEGORY_TYPE; 1821 } else { 1822 return NO_MATCH_TYPE; 1823 } 1824 } else { 1825 // If no MIME types are specified, then we will only match against 1826 // an Intent that does not have a MIME type. 1827 if (type != null) { 1828 return NO_MATCH_TYPE; 1829 } 1830 } 1831 1832 return match + MATCH_ADJUSTMENT_NORMAL; 1833 } 1834 matchRelRefGroups(Uri data)1835 private boolean matchRelRefGroups(Uri data) { 1836 if (mUriRelativeFilterGroups == null) { 1837 return false; 1838 } 1839 return UriRelativeFilterGroup.matchGroupsToUri(mUriRelativeFilterGroups, data); 1840 } 1841 1842 /** 1843 * Add a new Intent category to match against. The semantics of 1844 * categories is the opposite of actions -- an Intent includes the 1845 * categories that it requires, all of which must be included in the 1846 * filter in order to match. In other words, adding a category to the 1847 * filter has no impact on matching unless that category is specified in 1848 * the intent. 1849 * 1850 * @param category Name of category to match, such as Intent.CATEGORY_EMBED. 1851 */ addCategory(String category)1852 public final void addCategory(String category) { 1853 if (mCategories == null) mCategories = new ArrayList<String>(); 1854 if (!mCategories.contains(category)) { 1855 mCategories.add(category.intern()); 1856 } 1857 } 1858 1859 /** 1860 * Return the number of categories in the filter. 1861 */ countCategories()1862 public final int countCategories() { 1863 return mCategories != null ? mCategories.size() : 0; 1864 } 1865 1866 /** 1867 * Return a category in the filter. 1868 */ getCategory(int index)1869 public final String getCategory(int index) { 1870 return mCategories.get(index); 1871 } 1872 1873 /** 1874 * Is the given category included in the filter? 1875 * 1876 * @param category The category that the filter supports. 1877 * 1878 * @return True if the category is explicitly mentioned in the filter. 1879 */ hasCategory(String category)1880 public final boolean hasCategory(String category) { 1881 return mCategories != null && mCategories.contains(category); 1882 } 1883 1884 /** 1885 * Return an iterator over the filter's categories. 1886 * 1887 * @return Iterator if this filter has categories or {@code null} if none. 1888 */ categoriesIterator()1889 public final Iterator<String> categoriesIterator() { 1890 return mCategories != null ? mCategories.iterator() : null; 1891 } 1892 1893 /** 1894 * Match this filter against an Intent's categories. Each category in 1895 * the Intent must be specified by the filter; if any are not in the 1896 * filter, the match fails. 1897 * 1898 * @param categories The categories included in the intent, as returned by 1899 * Intent.getCategories(). 1900 * 1901 * @return If all categories match (success), null; else the name of the 1902 * first category that didn't match. 1903 */ matchCategories(Set<String> categories)1904 public final String matchCategories(Set<String> categories) { 1905 if (categories == null) { 1906 return null; 1907 } 1908 1909 Iterator<String> it = categories.iterator(); 1910 1911 if (mCategories == null) { 1912 return it.hasNext() ? it.next() : null; 1913 } 1914 1915 while (it.hasNext()) { 1916 final String category = it.next(); 1917 if (!mCategories.contains(category)) { 1918 return category; 1919 } 1920 } 1921 1922 return null; 1923 } 1924 1925 /** 1926 * Match this filter against an Intent's extras. An intent must 1927 * have all the extras specified by the filter with the same values, 1928 * for the match to succeed. 1929 * 1930 * <p> An intent con have other extras in addition to those specified 1931 * by the filter and it would not affect whether the match succeeds or not. 1932 * 1933 * @param extras The extras included in the intent, as returned by 1934 * Intent.getExtras(). 1935 * 1936 * @return If all extras match (success), null; else the name of the 1937 * first extra that didn't match. 1938 * 1939 * @hide 1940 */ matchExtras(@ullable Bundle extras)1941 private String matchExtras(@Nullable Bundle extras) { 1942 if (mExtras == null) { 1943 return null; 1944 } 1945 final Set<String> keys = mExtras.keySet(); 1946 for (String key : keys) { 1947 if (extras == null) { 1948 return key; 1949 } 1950 final Object value = mExtras.get(key); 1951 final Object otherValue = extras.get(key); 1952 if (otherValue == null || value.getClass() != otherValue.getClass() 1953 || !Objects.deepEquals(value, otherValue)) { 1954 return key; 1955 } 1956 } 1957 return null; 1958 } 1959 1960 /** 1961 * Add a new extra name and value to match against. If an extra is included in the filter, 1962 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 1963 * match. 1964 * 1965 * <p> Subsequent calls to this method with the same {@code name} will override any previously 1966 * set {@code value}. 1967 * 1968 * @param name the name of the extra to match against. 1969 * @param value the value of the extra to match against. 1970 * 1971 * @hide 1972 */ addExtra(@onNull String name, int value)1973 public final void addExtra(@NonNull String name, int value) { 1974 Objects.requireNonNull(name); 1975 if (mExtras == null) { 1976 mExtras = new PersistableBundle(); 1977 } 1978 mExtras.putInt(name, value); 1979 } 1980 1981 /** 1982 * Return the value of the extra with {@code name} included in the filter. 1983 * 1984 * @return the value that was previously set using {@link #addExtra(String, int)} or 1985 * {@code 0} if no value has been set. 1986 * @hide 1987 */ getIntExtra(@onNull String name)1988 public final int getIntExtra(@NonNull String name) { 1989 Objects.requireNonNull(name); 1990 return mExtras == null ? 0 : mExtras.getInt(name); 1991 } 1992 1993 /** 1994 * Add a new extra name and value to match against. If an extra is included in the filter, 1995 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 1996 * match. 1997 * 1998 * <p> Subsequent calls to this method with the same {@code name} will override any previously 1999 * set {@code value}. 2000 * 2001 * @param name the name of the extra to match against. 2002 * @param value the value of the extra to match against. 2003 * 2004 * @hide 2005 */ addExtra(@onNull String name, @NonNull int[] value)2006 public final void addExtra(@NonNull String name, @NonNull int[] value) { 2007 Objects.requireNonNull(name); 2008 Objects.requireNonNull(value); 2009 if (mExtras == null) { 2010 mExtras = new PersistableBundle(); 2011 } 2012 mExtras.putIntArray(name, value); 2013 } 2014 2015 /** 2016 * Return the value of the extra with {@code name} included in the filter. 2017 * 2018 * @return the value that was previously set using {@link #addExtra(String, int[])} or 2019 * an empty int array if no value has been set. 2020 * @hide 2021 */ 2022 @NonNull getIntArrayExtra(@onNull String name)2023 public final int[] getIntArrayExtra(@NonNull String name) { 2024 Objects.requireNonNull(name); 2025 return mExtras == null ? EMPTY_INT_ARRAY : mExtras.getIntArray(name); 2026 } 2027 2028 /** 2029 * Add a new extra name and value to match against. If an extra is included in the filter, 2030 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2031 * match. 2032 * 2033 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2034 * set {@code value}. 2035 * 2036 * @param name the name of the extra to match against. 2037 * @param value the value of the extra to match against. 2038 * 2039 * @hide 2040 */ addExtra(@onNull String name, long value)2041 public final void addExtra(@NonNull String name, long value) { 2042 Objects.requireNonNull(name); 2043 if (mExtras == null) { 2044 mExtras = new PersistableBundle(); 2045 } 2046 mExtras.putLong(name, value); 2047 } 2048 2049 /** 2050 * Return the value of the extra with {@code name} included in the filter. 2051 * 2052 * @return the value that was previously set using {@link #addExtra(String, long)} or 2053 * {@code 0L} if no value has been set. 2054 * @hide 2055 */ getLongExtra(@onNull String name)2056 public final long getLongExtra(@NonNull String name) { 2057 Objects.requireNonNull(name); 2058 return mExtras == null ? 0L : mExtras.getLong(name); 2059 } 2060 2061 /** 2062 * Add a new extra name and value to match against. If an extra is included in the filter, 2063 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2064 * match. 2065 * 2066 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2067 * set {@code value}. 2068 * 2069 * @param name the name of the extra to match against. 2070 * @param value the value of the extra to match against. 2071 * 2072 * @hide 2073 */ addExtra(@onNull String name, @NonNull long[] value)2074 public final void addExtra(@NonNull String name, @NonNull long[] value) { 2075 Objects.requireNonNull(name); 2076 Objects.requireNonNull(value); 2077 if (mExtras == null) { 2078 mExtras = new PersistableBundle(); 2079 } 2080 mExtras.putLongArray(name, value); 2081 } 2082 2083 /** 2084 * Return the value of the extra with {@code name} included in the filter. 2085 * 2086 * @return the value that was previously set using {@link #addExtra(String, long[])} or 2087 * an empty long array if no value has been set. 2088 * @hide 2089 */ 2090 @NonNull getLongArrayExtra(@onNull String name)2091 public final long[] getLongArrayExtra(@NonNull String name) { 2092 Objects.requireNonNull(name); 2093 return mExtras == null ? EMPTY_LONG_ARRAY : mExtras.getLongArray(name); 2094 } 2095 2096 /** 2097 * Add a new extra name and value to match against. If an extra is included in the filter, 2098 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2099 * match. 2100 * 2101 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2102 * set {@code value}. 2103 * 2104 * @param name the name of the extra to match against. 2105 * @param value the value of the extra to match against. 2106 * 2107 * @hide 2108 */ addExtra(@onNull String name, double value)2109 public final void addExtra(@NonNull String name, double value) { 2110 Objects.requireNonNull(name); 2111 if (mExtras == null) { 2112 mExtras = new PersistableBundle(); 2113 } 2114 mExtras.putDouble(name, value); 2115 } 2116 2117 /** 2118 * Return the value of the extra with {@code name} included in the filter. 2119 * 2120 * @return the value that was previously set using {@link #addExtra(String, double)} or 2121 * {@code 0.0} if no value has been set. 2122 * @hide 2123 */ 2124 @NonNull getDoubleExtra(@onNull String name)2125 public final double getDoubleExtra(@NonNull String name) { 2126 Objects.requireNonNull(name); 2127 return mExtras == null ? 0.0 : mExtras.getDouble(name); 2128 } 2129 2130 /** 2131 * Add a new extra name and value to match against. If an extra is included in the filter, 2132 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2133 * match. 2134 * 2135 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2136 * set {@code value}. 2137 * 2138 * @param name the name of the extra to match against. 2139 * @param value the value of the extra to match against. 2140 * 2141 * @hide 2142 */ addExtra(@onNull String name, @NonNull double[] value)2143 public final void addExtra(@NonNull String name, @NonNull double[] value) { 2144 Objects.requireNonNull(name); 2145 Objects.requireNonNull(value); 2146 if (mExtras == null) { 2147 mExtras = new PersistableBundle(); 2148 } 2149 mExtras.putDoubleArray(name, value); 2150 } 2151 2152 /** 2153 * Return the value of the extra with {@code name} included in the filter. 2154 * 2155 * @return the value that was previously set using {@link #addExtra(String, double[])} or 2156 * an empty double array if no value has been set. 2157 * @hide 2158 */ 2159 @NonNull getDoubleArrayExtra(@onNull String name)2160 public final double[] getDoubleArrayExtra(@NonNull String name) { 2161 Objects.requireNonNull(name); 2162 return mExtras == null ? EMPTY_DOUBLE_ARRAY : mExtras.getDoubleArray(name); 2163 } 2164 2165 /** 2166 * Add a new extra name and value to match against. If an extra is included in the filter, 2167 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2168 * match. 2169 * 2170 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2171 * set {@code value}. 2172 * 2173 * @param name the name of the extra to match against. 2174 * @param value the value of the extra to match against. 2175 * 2176 * @hide 2177 */ addExtra(@onNull String name, @NonNull String value)2178 public final void addExtra(@NonNull String name, @NonNull String value) { 2179 Objects.requireNonNull(name); 2180 Objects.requireNonNull(value); 2181 if (mExtras == null) { 2182 mExtras = new PersistableBundle(); 2183 } 2184 mExtras.putString(name, value); 2185 } 2186 2187 /** 2188 * Return the value of the extra with {@code name} included in the filter. 2189 * 2190 * @return the value that was previously set using {@link #addExtra(String, String)} or a 2191 * {@code null} if no value has been set. 2192 * @hide 2193 */ 2194 @Nullable getStringExtra(@onNull String name)2195 public final String getStringExtra(@NonNull String name) { 2196 Objects.requireNonNull(name); 2197 return mExtras == null ? null : mExtras.getString(name); 2198 } 2199 2200 /** 2201 * Add a new extra name and value to match against. If an extra is included in the filter, 2202 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2203 * match. 2204 * 2205 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2206 * set {@code value}. 2207 * 2208 * @param name the name of the extra to match against. 2209 * @param value the value of the extra to match against. 2210 * 2211 * @hide 2212 */ addExtra(@onNull String name, @NonNull String[] value)2213 public final void addExtra(@NonNull String name, @NonNull String[] value) { 2214 Objects.requireNonNull(name); 2215 Objects.requireNonNull(value); 2216 if (mExtras == null) { 2217 mExtras = new PersistableBundle(); 2218 } 2219 mExtras.putStringArray(name, value); 2220 } 2221 2222 /** 2223 * Return the value of the extra with {@code name} included in the filter. 2224 * 2225 * @return the value that was previously set using {@link #addExtra(String, String[])} or 2226 * an empty string array if no value has been set. 2227 * @hide 2228 */ 2229 @NonNull getStringArrayExtra(@onNull String name)2230 public final String[] getStringArrayExtra(@NonNull String name) { 2231 Objects.requireNonNull(name); 2232 return mExtras == null ? EMPTY_STRING_ARRAY : mExtras.getStringArray(name); 2233 } 2234 2235 /** 2236 * Add a new extra name and value to match against. If an extra is included in the filter, 2237 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2238 * match. 2239 * 2240 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2241 * set {@code value}. 2242 * 2243 * @param name the name of the extra to match against. 2244 * @param value the value of the extra to match against. 2245 * 2246 * @hide 2247 */ addExtra(@onNull String name, boolean value)2248 public final void addExtra(@NonNull String name, boolean value) { 2249 Objects.requireNonNull(name); 2250 if (mExtras == null) { 2251 mExtras = new PersistableBundle(); 2252 } 2253 mExtras.putBoolean(name, value); 2254 } 2255 2256 /** 2257 * Return the value of the extra with {@code name} included in the filter. 2258 * 2259 * @return the value that was previously set using {@link #addExtra(String, boolean)} or 2260 * {@code false} if no value has been set. 2261 * @hide 2262 */ getBooleanExtra(@onNull String name)2263 public final boolean getBooleanExtra(@NonNull String name) { 2264 Objects.requireNonNull(name); 2265 return mExtras == null ? false : mExtras.getBoolean(name); 2266 } 2267 2268 /** 2269 * Add a new extra name and value to match against. If an extra is included in the filter, 2270 * then an Intent must have an extra with the same {@code name} and {@code value} for it to 2271 * match. 2272 * 2273 * <p> Subsequent calls to this method with the same {@code name} will override any previously 2274 * set {@code value}. 2275 * 2276 * @param name the name of the extra to match against. 2277 * @param value the value of the extra to match against. 2278 * 2279 * @hide 2280 */ addExtra(@onNull String name, @NonNull boolean[] value)2281 public final void addExtra(@NonNull String name, @NonNull boolean[] value) { 2282 Objects.requireNonNull(name); 2283 Objects.requireNonNull(value); 2284 if (mExtras == null) { 2285 mExtras = new PersistableBundle(); 2286 } 2287 mExtras.putBooleanArray(name, value); 2288 } 2289 2290 /** 2291 * Return the value of the extra with {@code name} included in the filter. 2292 * 2293 * @return the value that was previously set using {@link #addExtra(String, boolean[])} or 2294 * an empty boolean array if no value has been set. 2295 * @hide 2296 */ 2297 @NonNull getBooleanArrayExtra(@onNull String name)2298 public final boolean[] getBooleanArrayExtra(@NonNull String name) { 2299 Objects.requireNonNull(name); 2300 return mExtras == null ? EMPTY_BOOLEAN_ARRAY : mExtras.getBooleanArray(name); 2301 } 2302 2303 /** 2304 * Returns whether or not an extra with {@code name} is included in the filter. 2305 * 2306 * @return {@code true} if an extra with {@code name} is included in the filter. 2307 * Otherwise {@code false}. 2308 * @hide 2309 */ hasExtra(@onNull String name)2310 public final boolean hasExtra(@NonNull String name) { 2311 Objects.requireNonNull(name); 2312 return mExtras == null ? false : mExtras.containsKey(name); 2313 } 2314 2315 /** 2316 * Set the intent extras to match against. For this filter to match an 2317 * intent, it must contain all the {@code extras} set. 2318 * 2319 * <p> Subsequent calls to this method will override any previously set extras. 2320 * 2321 * @param extras The intent extras to match against. 2322 * @hide 2323 */ setExtras(@onNull PersistableBundle extras)2324 public final void setExtras(@NonNull PersistableBundle extras) { 2325 mExtras = extras; 2326 } 2327 2328 /** 2329 * Return the intent extras included in this filter. 2330 * 2331 * @return the extras that were previously set using {@link #setExtras(PersistableBundle)} or 2332 * an empty {@link PersistableBundle} object if no extras were set. 2333 * @hide 2334 */ getExtras()2335 public final @NonNull PersistableBundle getExtras() { 2336 return mExtras == null ? new PersistableBundle() : mExtras; 2337 } 2338 2339 /** 2340 * Return a {@link Predicate} which tests whether this filter matches the 2341 * given <var>intent</var>. 2342 * <p> 2343 * The intent's type will always be tested using a simple 2344 * {@link Intent#getType()} check. To instead perform a detailed type 2345 * resolution before matching, use 2346 * {@link #asPredicateWithTypeResolution(ContentResolver)}. 2347 */ asPredicate()2348 public @NonNull Predicate<Intent> asPredicate() { 2349 return i -> match(null, i, false, TAG) >= 0; 2350 } 2351 2352 /** 2353 * Return a {@link Predicate} which tests whether this filter matches the 2354 * given <var>intent</var>. 2355 * <p> 2356 * The intent's type will always be resolved by calling 2357 * {@link Intent#resolveType(ContentResolver)} before matching. 2358 * 2359 * @param resolver to be used when calling 2360 * {@link Intent#resolveType(ContentResolver)} before matching. 2361 */ asPredicateWithTypeResolution( @onNull ContentResolver resolver)2362 public @NonNull Predicate<Intent> asPredicateWithTypeResolution( 2363 @NonNull ContentResolver resolver) { 2364 Objects.requireNonNull(resolver); 2365 return i -> match(resolver, i, true, TAG) >= 0; 2366 } 2367 2368 /** 2369 * Test whether this filter matches the given <var>intent</var>. 2370 * 2371 * @param intent The Intent to compare against. 2372 * @param resolve If true, the intent's type will be resolved by calling 2373 * Intent.resolveType(); otherwise a simple match against 2374 * Intent.type will be performed. 2375 * @param logTag Tag to use in debugging messages. 2376 * 2377 * @return Returns either a valid match constant (a combination of 2378 * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}), 2379 * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match, 2380 * {@link #NO_MATCH_DATA} if the scheme/path didn't match, 2381 * {@link #NO_MATCH_ACTION} if the action didn't match, or 2382 * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match. 2383 * 2384 * @see #match(String, String, String, android.net.Uri , Set, String) 2385 */ match(ContentResolver resolver, Intent intent, boolean resolve, String logTag)2386 public final int match(ContentResolver resolver, Intent intent, 2387 boolean resolve, String logTag) { 2388 String type = resolve ? intent.resolveType(resolver) : intent.getType(); 2389 return match(intent.getAction(), type, intent.getScheme(), 2390 intent.getData(), intent.getCategories(), logTag, 2391 false /* supportWildcards */, null /* ignoreActions */, 2392 intent.getExtras()); 2393 } 2394 2395 /** 2396 * Test whether this filter matches the given intent data. A match is 2397 * only successful if the actions and categories in the Intent match 2398 * against the filter, as described in {@link IntentFilter}; in that case, 2399 * the match result returned will be as per {@link #matchData}. 2400 * 2401 * @param action The intent action to match against (Intent.getAction). 2402 * @param type The intent type to match against (Intent.resolveType()). 2403 * @param scheme The data scheme to match against (Intent.getScheme()). 2404 * @param data The data URI to match against (Intent.getData()). 2405 * @param categories The categories to match against 2406 * (Intent.getCategories()). 2407 * @param logTag Tag to use in debugging messages. 2408 * 2409 * @return Returns either a valid match constant (a combination of 2410 * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}), 2411 * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match, 2412 * {@link #NO_MATCH_DATA} if the scheme/path didn't match, 2413 * {@link #NO_MATCH_ACTION} if the action didn't match, or 2414 * {@link #NO_MATCH_CATEGORY} if one or more categories didn't match. 2415 * 2416 * @see #matchData 2417 * @see Intent#getAction 2418 * @see Intent#resolveType 2419 * @see Intent#getScheme 2420 * @see Intent#getData 2421 * @see Intent#getCategories 2422 */ match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag)2423 public final int match(String action, String type, String scheme, 2424 Uri data, Set<String> categories, String logTag) { 2425 return match(action, type, scheme, data, categories, logTag, false /*supportWildcards*/, 2426 null /*ignoreActions*/); 2427 } 2428 2429 /** 2430 * Variant of {@link #match(ContentResolver, Intent, boolean, String)} that supports wildcards 2431 * in the action, type, scheme, host and path. 2432 * @param supportWildcards if true, will allow supported parameters to use wildcards to match 2433 * this filter 2434 * @param ignoreActions a collection of actions that, if not null should be ignored and not used 2435 * if provided as the matching action or the set of actions defined by this 2436 * filter 2437 * @hide 2438 */ match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag, boolean supportWildcards, @Nullable Collection<String> ignoreActions)2439 public final int match(String action, String type, String scheme, 2440 Uri data, Set<String> categories, String logTag, boolean supportWildcards, 2441 @Nullable Collection<String> ignoreActions) { 2442 return match(action, type, scheme, data, categories, logTag, supportWildcards, 2443 ignoreActions, null /* extras */); 2444 } 2445 2446 /** 2447 * Variant of {@link #match(String, String, String, Uri, Set, String, boolean, Collection)} 2448 * that supports matching the extra values in the intent. 2449 * 2450 * @hide 2451 */ match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag, boolean supportWildcards, @Nullable Collection<String> ignoreActions, @Nullable Bundle extras)2452 public final int match(String action, String type, String scheme, 2453 Uri data, Set<String> categories, String logTag, boolean supportWildcards, 2454 @Nullable Collection<String> ignoreActions, @Nullable Bundle extras) { 2455 if (action != null && !matchAction(action, supportWildcards, ignoreActions)) { 2456 if (false) Log.v( 2457 logTag, "No matching action " + action + " for " + this); 2458 return NO_MATCH_ACTION; 2459 } 2460 2461 int dataMatch = matchData(type, scheme, data, supportWildcards); 2462 if (dataMatch < 0) { 2463 if (false) { 2464 if (dataMatch == NO_MATCH_TYPE) { 2465 Log.v(logTag, "No matching type " + type 2466 + " for " + this); 2467 } 2468 if (dataMatch == NO_MATCH_DATA) { 2469 Log.v(logTag, "No matching scheme/path " + data 2470 + " for " + this); 2471 } 2472 } 2473 return dataMatch; 2474 } 2475 2476 String categoryMismatch = matchCategories(categories); 2477 if (categoryMismatch != null) { 2478 if (false) { 2479 Log.v(logTag, "No matching category " + categoryMismatch + " for " + this); 2480 } 2481 return NO_MATCH_CATEGORY; 2482 } 2483 2484 String extraMismatch = matchExtras(extras); 2485 if (extraMismatch != null) { 2486 if (false) { 2487 Log.v(logTag, "Mismatch for extra key " + extraMismatch + " for " + this); 2488 } 2489 return NO_MATCH_EXTRAS; 2490 } 2491 2492 // It would be nice to treat container activities as more 2493 // important than ones that can be embedded, but this is not the way... 2494 if (false) { 2495 if (categories != null) { 2496 dataMatch -= mCategories.size() - categories.size(); 2497 } 2498 } 2499 2500 return dataMatch; 2501 } 2502 2503 /** 2504 * Write the contents of the IntentFilter as an XML stream. 2505 */ writeToXml(XmlSerializer serializer)2506 public void writeToXml(XmlSerializer serializer) throws IOException { 2507 2508 if (getAutoVerify()) { 2509 serializer.attribute(null, AUTO_VERIFY_STR, Boolean.toString(true)); 2510 } 2511 2512 int N = countActions(); 2513 for (int i=0; i<N; i++) { 2514 serializer.startTag(null, ACTION_STR); 2515 serializer.attribute(null, NAME_STR, mActions.valueAt(i)); 2516 serializer.endTag(null, ACTION_STR); 2517 } 2518 N = countCategories(); 2519 for (int i=0; i<N; i++) { 2520 serializer.startTag(null, CAT_STR); 2521 serializer.attribute(null, NAME_STR, mCategories.get(i)); 2522 serializer.endTag(null, CAT_STR); 2523 } 2524 writeDataTypesToXml(serializer); 2525 N = countMimeGroups(); 2526 for (int i=0; i<N; i++) { 2527 serializer.startTag(null, GROUP_STR); 2528 serializer.attribute(null, NAME_STR, mMimeGroups.get(i)); 2529 serializer.endTag(null, GROUP_STR); 2530 } 2531 N = countDataSchemes(); 2532 for (int i=0; i<N; i++) { 2533 serializer.startTag(null, SCHEME_STR); 2534 serializer.attribute(null, NAME_STR, mDataSchemes.get(i)); 2535 serializer.endTag(null, SCHEME_STR); 2536 } 2537 N = countDataSchemeSpecificParts(); 2538 for (int i=0; i<N; i++) { 2539 serializer.startTag(null, SSP_STR); 2540 PatternMatcher pe = mDataSchemeSpecificParts.get(i); 2541 switch (pe.getType()) { 2542 case PatternMatcher.PATTERN_LITERAL: 2543 serializer.attribute(null, LITERAL_STR, pe.getPath()); 2544 break; 2545 case PatternMatcher.PATTERN_PREFIX: 2546 serializer.attribute(null, PREFIX_STR, pe.getPath()); 2547 break; 2548 case PatternMatcher.PATTERN_SIMPLE_GLOB: 2549 serializer.attribute(null, SGLOB_STR, pe.getPath()); 2550 break; 2551 case PatternMatcher.PATTERN_ADVANCED_GLOB: 2552 serializer.attribute(null, AGLOB_STR, pe.getPath()); 2553 break; 2554 case PatternMatcher.PATTERN_SUFFIX: 2555 serializer.attribute(null, SUFFIX_STR, pe.getPath()); 2556 break; 2557 } 2558 serializer.endTag(null, SSP_STR); 2559 } 2560 N = countDataAuthorities(); 2561 for (int i=0; i<N; i++) { 2562 serializer.startTag(null, AUTH_STR); 2563 AuthorityEntry ae = mDataAuthorities.get(i); 2564 serializer.attribute(null, HOST_STR, ae.getHost()); 2565 if (ae.getPort() >= 0) { 2566 serializer.attribute(null, PORT_STR, Integer.toString(ae.getPort())); 2567 } 2568 serializer.endTag(null, AUTH_STR); 2569 } 2570 N = countDataPaths(); 2571 for (int i=0; i<N; i++) { 2572 serializer.startTag(null, PATH_STR); 2573 PatternMatcher pe = mDataPaths.get(i); 2574 switch (pe.getType()) { 2575 case PatternMatcher.PATTERN_LITERAL: 2576 serializer.attribute(null, LITERAL_STR, pe.getPath()); 2577 break; 2578 case PatternMatcher.PATTERN_PREFIX: 2579 serializer.attribute(null, PREFIX_STR, pe.getPath()); 2580 break; 2581 case PatternMatcher.PATTERN_SIMPLE_GLOB: 2582 serializer.attribute(null, SGLOB_STR, pe.getPath()); 2583 break; 2584 case PatternMatcher.PATTERN_ADVANCED_GLOB: 2585 serializer.attribute(null, AGLOB_STR, pe.getPath()); 2586 break; 2587 case PatternMatcher.PATTERN_SUFFIX: 2588 serializer.attribute(null, SUFFIX_STR, pe.getPath()); 2589 break; 2590 } 2591 serializer.endTag(null, PATH_STR); 2592 } 2593 if (mExtras != null) { 2594 serializer.startTag(null, EXTRAS_STR); 2595 try { 2596 mExtras.saveToXml(serializer); 2597 } catch (XmlPullParserException e) { 2598 throw new IllegalStateException("Failed to write extras: " + mExtras.toString(), e); 2599 } 2600 serializer.endTag(null, EXTRAS_STR); 2601 } 2602 if (Flags.relativeReferenceIntentFilters()) { 2603 N = countUriRelativeFilterGroups(); 2604 for (int i = 0; i < N; i++) { 2605 mUriRelativeFilterGroups.get(i).writeToXml(serializer); 2606 } 2607 } 2608 } 2609 2610 /** 2611 * Write data types (both static and dynamic) to XML. 2612 * In implementation we rely on two facts: 2613 * - {@link #mStaticDataTypes} is subsequence of {@link #mDataTypes} 2614 * - both {@link #mStaticDataTypes} and {@link #mDataTypes} does not contain duplicates 2615 */ writeDataTypesToXml(XmlSerializer serializer)2616 private void writeDataTypesToXml(XmlSerializer serializer) throws IOException { 2617 if (mStaticDataTypes == null) { 2618 return; 2619 } 2620 2621 int i = 0; 2622 for (String staticType: mStaticDataTypes) { 2623 while (!mDataTypes.get(i).equals(staticType)) { 2624 writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR); 2625 i++; 2626 } 2627 2628 writeDataTypeToXml(serializer, staticType, STATIC_TYPE_STR); 2629 i++; 2630 } 2631 2632 while (i < mDataTypes.size()) { 2633 writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR); 2634 i++; 2635 } 2636 } 2637 writeDataTypeToXml(XmlSerializer serializer, String type, String tag)2638 private void writeDataTypeToXml(XmlSerializer serializer, String type, String tag) 2639 throws IOException { 2640 serializer.startTag(null, tag); 2641 2642 if (type.indexOf('/') < 0) { 2643 type = type + "/*"; 2644 } 2645 2646 serializer.attribute(null, NAME_STR, type); 2647 serializer.endTag(null, tag); 2648 } 2649 readFromXml(XmlPullParser parser)2650 public void readFromXml(XmlPullParser parser) throws XmlPullParserException, 2651 IOException { 2652 String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR); 2653 setAutoVerify(TextUtils.isEmpty(autoVerify) ? false : Boolean.getBoolean(autoVerify)); 2654 2655 int outerDepth = parser.getDepth(); 2656 int type; 2657 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 2658 && (type != XmlPullParser.END_TAG 2659 || parser.getDepth() > outerDepth)) { 2660 if (type == XmlPullParser.END_TAG 2661 || type == XmlPullParser.TEXT) { 2662 continue; 2663 } 2664 2665 String tagName = parser.getName(); 2666 if (tagName.equals(ACTION_STR)) { 2667 String name = parser.getAttributeValue(null, NAME_STR); 2668 if (name != null) { 2669 addAction(name); 2670 } 2671 } else if (tagName.equals(CAT_STR)) { 2672 String name = parser.getAttributeValue(null, NAME_STR); 2673 if (name != null) { 2674 addCategory(name); 2675 } 2676 } else if (tagName.equals(STATIC_TYPE_STR)) { 2677 String name = parser.getAttributeValue(null, NAME_STR); 2678 if (name != null) { 2679 try { 2680 addDataType(name); 2681 } catch (MalformedMimeTypeException e) { 2682 } 2683 } 2684 } else if (tagName.equals(TYPE_STR)) { 2685 String name = parser.getAttributeValue(null, NAME_STR); 2686 if (name != null) { 2687 try { 2688 addDynamicDataType(name); 2689 } catch (MalformedMimeTypeException e) { 2690 } 2691 } 2692 } else if (tagName.equals(GROUP_STR)) { 2693 String name = parser.getAttributeValue(null, NAME_STR); 2694 if (name != null) { 2695 addMimeGroup(name); 2696 } 2697 } else if (tagName.equals(SCHEME_STR)) { 2698 String name = parser.getAttributeValue(null, NAME_STR); 2699 if (name != null) { 2700 addDataScheme(name); 2701 } 2702 } else if (tagName.equals(SSP_STR)) { 2703 String ssp = parser.getAttributeValue(null, LITERAL_STR); 2704 if (ssp != null) { 2705 addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_LITERAL); 2706 } else if ((ssp=parser.getAttributeValue(null, PREFIX_STR)) != null) { 2707 addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_PREFIX); 2708 } else if ((ssp=parser.getAttributeValue(null, SGLOB_STR)) != null) { 2709 addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB); 2710 } else if ((ssp=parser.getAttributeValue(null, AGLOB_STR)) != null) { 2711 addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_ADVANCED_GLOB); 2712 } else if ((ssp=parser.getAttributeValue(null, SUFFIX_STR)) != null) { 2713 addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SUFFIX); 2714 } 2715 } else if (tagName.equals(AUTH_STR)) { 2716 String host = parser.getAttributeValue(null, HOST_STR); 2717 String port = parser.getAttributeValue(null, PORT_STR); 2718 if (host != null) { 2719 addDataAuthority(host, port); 2720 } 2721 } else if (tagName.equals(PATH_STR)) { 2722 String path = parser.getAttributeValue(null, LITERAL_STR); 2723 if (path != null) { 2724 addDataPath(path, PatternMatcher.PATTERN_LITERAL); 2725 } else if ((path=parser.getAttributeValue(null, PREFIX_STR)) != null) { 2726 addDataPath(path, PatternMatcher.PATTERN_PREFIX); 2727 } else if ((path=parser.getAttributeValue(null, SGLOB_STR)) != null) { 2728 addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB); 2729 } else if ((path=parser.getAttributeValue(null, AGLOB_STR)) != null) { 2730 addDataPath(path, PatternMatcher.PATTERN_ADVANCED_GLOB); 2731 } else if ((path=parser.getAttributeValue(null, SUFFIX_STR)) != null) { 2732 addDataPath(path, PatternMatcher.PATTERN_SUFFIX); 2733 } 2734 } else if (tagName.equals(EXTRAS_STR)) { 2735 mExtras = PersistableBundle.restoreFromXml(parser); 2736 } else if (Flags.relativeReferenceIntentFilters() 2737 && URI_RELATIVE_FILTER_GROUP_STR.equals(tagName)) { 2738 addUriRelativeFilterGroup(new UriRelativeFilterGroup(parser)); 2739 } else { 2740 Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName); 2741 } 2742 XmlUtils.skipCurrentTag(parser); 2743 } 2744 } 2745 2746 /** @hide */ dumpDebug(ProtoOutputStream proto, long fieldId)2747 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 2748 long token = proto.start(fieldId); 2749 if (mActions.size() > 0) { 2750 Iterator<String> it = mActions.iterator(); 2751 while (it.hasNext()) { 2752 proto.write(IntentFilterProto.ACTIONS, it.next()); 2753 } 2754 } 2755 if (mCategories != null) { 2756 Iterator<String> it = mCategories.iterator(); 2757 while (it.hasNext()) { 2758 proto.write(IntentFilterProto.CATEGORIES, it.next()); 2759 } 2760 } 2761 if (mDataSchemes != null) { 2762 Iterator<String> it = mDataSchemes.iterator(); 2763 while (it.hasNext()) { 2764 proto.write(IntentFilterProto.DATA_SCHEMES, it.next()); 2765 } 2766 } 2767 if (mDataSchemeSpecificParts != null) { 2768 Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator(); 2769 while (it.hasNext()) { 2770 it.next().dumpDebug(proto, IntentFilterProto.DATA_SCHEME_SPECS); 2771 } 2772 } 2773 if (mDataAuthorities != null) { 2774 Iterator<AuthorityEntry> it = mDataAuthorities.iterator(); 2775 while (it.hasNext()) { 2776 it.next().dumpDebug(proto, IntentFilterProto.DATA_AUTHORITIES); 2777 } 2778 } 2779 if (mDataPaths != null) { 2780 Iterator<PatternMatcher> it = mDataPaths.iterator(); 2781 while (it.hasNext()) { 2782 it.next().dumpDebug(proto, IntentFilterProto.DATA_PATHS); 2783 } 2784 } 2785 if (mDataTypes != null) { 2786 Iterator<String> it = mDataTypes.iterator(); 2787 while (it.hasNext()) { 2788 proto.write(IntentFilterProto.DATA_TYPES, it.next()); 2789 } 2790 } 2791 if (mMimeGroups != null) { 2792 Iterator<String> it = mMimeGroups.iterator(); 2793 while (it.hasNext()) { 2794 proto.write(IntentFilterProto.MIME_GROUPS, it.next()); 2795 } 2796 } 2797 if (mPriority != 0 || hasPartialTypes()) { 2798 proto.write(IntentFilterProto.PRIORITY, mPriority); 2799 proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, hasPartialTypes()); 2800 } 2801 proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify()); 2802 if (mExtras != null) { 2803 mExtras.dumpDebug(proto, IntentFilterProto.EXTRAS); 2804 } 2805 if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) { 2806 Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator(); 2807 while (it.hasNext()) { 2808 it.next().dumpDebug(proto, IntentFilterProto.URI_RELATIVE_FILTER_GROUPS); 2809 } 2810 } 2811 proto.end(token); 2812 } 2813 dump(Printer du, String prefix)2814 public void dump(Printer du, String prefix) { 2815 StringBuilder sb = new StringBuilder(256); 2816 if (mActions.size() > 0) { 2817 Iterator<String> it = mActions.iterator(); 2818 while (it.hasNext()) { 2819 sb.setLength(0); 2820 sb.append(prefix); sb.append("Action: \""); 2821 sb.append(it.next()); sb.append("\""); 2822 du.println(sb.toString()); 2823 } 2824 } 2825 if (mCategories != null) { 2826 Iterator<String> it = mCategories.iterator(); 2827 while (it.hasNext()) { 2828 sb.setLength(0); 2829 sb.append(prefix); sb.append("Category: \""); 2830 sb.append(it.next()); sb.append("\""); 2831 du.println(sb.toString()); 2832 } 2833 } 2834 if (mDataSchemes != null) { 2835 Iterator<String> it = mDataSchemes.iterator(); 2836 while (it.hasNext()) { 2837 sb.setLength(0); 2838 sb.append(prefix); sb.append("Scheme: \""); 2839 sb.append(it.next()); sb.append("\""); 2840 du.println(sb.toString()); 2841 } 2842 } 2843 if (mDataSchemeSpecificParts != null) { 2844 Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator(); 2845 while (it.hasNext()) { 2846 PatternMatcher pe = it.next(); 2847 sb.setLength(0); 2848 sb.append(prefix); sb.append("Ssp: \""); 2849 sb.append(pe); sb.append("\""); 2850 du.println(sb.toString()); 2851 } 2852 } 2853 if (mDataAuthorities != null) { 2854 Iterator<AuthorityEntry> it = mDataAuthorities.iterator(); 2855 while (it.hasNext()) { 2856 AuthorityEntry ae = it.next(); 2857 sb.setLength(0); 2858 sb.append(prefix); sb.append("Authority: \""); 2859 sb.append(ae.mHost); sb.append("\": "); 2860 sb.append(ae.mPort); 2861 if (ae.mWild) sb.append(" WILD"); 2862 du.println(sb.toString()); 2863 } 2864 } 2865 if (mDataPaths != null) { 2866 Iterator<PatternMatcher> it = mDataPaths.iterator(); 2867 while (it.hasNext()) { 2868 PatternMatcher pe = it.next(); 2869 sb.setLength(0); 2870 sb.append(prefix); sb.append("Path: \""); 2871 sb.append(pe); sb.append("\""); 2872 du.println(sb.toString()); 2873 } 2874 } 2875 if (mUriRelativeFilterGroups != null) { 2876 Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator(); 2877 while (it.hasNext()) { 2878 sb.setLength(0); 2879 sb.append(prefix); sb.append("UriRelativeFilterGroup: \""); 2880 sb.append(it.next()); sb.append("\""); 2881 du.println(sb.toString()); 2882 } 2883 } 2884 if (mStaticDataTypes != null) { 2885 Iterator<String> it = mStaticDataTypes.iterator(); 2886 while (it.hasNext()) { 2887 sb.setLength(0); 2888 sb.append(prefix); sb.append("StaticType: \""); 2889 sb.append(it.next()); sb.append("\""); 2890 du.println(sb.toString()); 2891 } 2892 } 2893 if (mDataTypes != null) { 2894 Iterator<String> it = mDataTypes.iterator(); 2895 while (it.hasNext()) { 2896 String dataType = it.next(); 2897 if (hasExactStaticDataType(dataType)) { 2898 continue; 2899 } 2900 2901 sb.setLength(0); 2902 sb.append(prefix); sb.append("Type: \""); 2903 sb.append(dataType); sb.append("\""); 2904 du.println(sb.toString()); 2905 } 2906 } 2907 if (mMimeGroups != null) { 2908 Iterator<String> it = mMimeGroups.iterator(); 2909 while (it.hasNext()) { 2910 sb.setLength(0); 2911 sb.append(prefix); sb.append("MimeGroup: \""); 2912 sb.append(it.next()); sb.append("\""); 2913 du.println(sb.toString()); 2914 } 2915 } 2916 if (mPriority != 0 || mOrder != 0 || hasPartialTypes()) { 2917 sb.setLength(0); 2918 sb.append(prefix); 2919 sb.append("mPriority="); sb.append(mPriority); 2920 sb.append(", mOrder="); sb.append(mOrder); 2921 sb.append(", mHasStaticPartialTypes="); sb.append(mHasStaticPartialTypes); 2922 sb.append(", mHasDynamicPartialTypes="); sb.append(mHasDynamicPartialTypes); 2923 du.println(sb.toString()); 2924 } 2925 if (getAutoVerify()) { 2926 sb.setLength(0); 2927 sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify()); 2928 du.println(sb.toString()); 2929 } 2930 if (mExtras != null) { 2931 sb.setLength(0); 2932 sb.append(prefix); sb.append("mExtras="); sb.append(mExtras.toString()); 2933 du.println(sb.toString()); 2934 } 2935 } 2936 2937 public static final @android.annotation.NonNull Parcelable.Creator<IntentFilter> CREATOR 2938 = new Parcelable.Creator<IntentFilter>() { 2939 public IntentFilter createFromParcel(Parcel source) { 2940 return new IntentFilter(source); 2941 } 2942 2943 public IntentFilter[] newArray(int size) { 2944 return new IntentFilter[size]; 2945 } 2946 }; 2947 describeContents()2948 public final int describeContents() { 2949 return 0; 2950 } 2951 writeToParcel(Parcel dest, int flags)2952 public final void writeToParcel(Parcel dest, int flags) { 2953 dest.writeStringArray(mActions.toArray(new String[mActions.size()])); 2954 if (mCategories != null) { 2955 dest.writeInt(1); 2956 dest.writeStringList(mCategories); 2957 } else { 2958 dest.writeInt(0); 2959 } 2960 if (mDataSchemes != null) { 2961 dest.writeInt(1); 2962 dest.writeStringList(mDataSchemes); 2963 } else { 2964 dest.writeInt(0); 2965 } 2966 if (mStaticDataTypes != null) { 2967 dest.writeInt(1); 2968 dest.writeStringList(mStaticDataTypes); 2969 } else { 2970 dest.writeInt(0); 2971 } 2972 if (mDataTypes != null) { 2973 dest.writeInt(1); 2974 dest.writeStringList(mDataTypes); 2975 } else { 2976 dest.writeInt(0); 2977 } 2978 if (mMimeGroups != null) { 2979 dest.writeInt(1); 2980 dest.writeStringList(mMimeGroups); 2981 } else { 2982 dest.writeInt(0); 2983 } 2984 if (mDataSchemeSpecificParts != null) { 2985 final int N = mDataSchemeSpecificParts.size(); 2986 dest.writeInt(N); 2987 for (int i=0; i<N; i++) { 2988 mDataSchemeSpecificParts.get(i).writeToParcel(dest, flags); 2989 } 2990 } else { 2991 dest.writeInt(0); 2992 } 2993 if (mDataAuthorities != null) { 2994 final int N = mDataAuthorities.size(); 2995 dest.writeInt(N); 2996 for (int i=0; i<N; i++) { 2997 mDataAuthorities.get(i).writeToParcel(dest); 2998 } 2999 } else { 3000 dest.writeInt(0); 3001 } 3002 if (mDataPaths != null) { 3003 final int N = mDataPaths.size(); 3004 dest.writeInt(N); 3005 for (int i=0; i<N; i++) { 3006 mDataPaths.get(i).writeToParcel(dest, flags); 3007 } 3008 } else { 3009 dest.writeInt(0); 3010 } 3011 dest.writeInt(mPriority); 3012 dest.writeInt(mHasStaticPartialTypes ? 1 : 0); 3013 dest.writeInt(mHasDynamicPartialTypes ? 1 : 0); 3014 dest.writeInt(getAutoVerify() ? 1 : 0); 3015 dest.writeInt(mInstantAppVisibility); 3016 dest.writeInt(mOrder); 3017 if (mExtras != null) { 3018 dest.writeInt(1); 3019 mExtras.writeToParcel(dest, flags); 3020 } else { 3021 dest.writeInt(0); 3022 } 3023 if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) { 3024 final int N = mUriRelativeFilterGroups.size(); 3025 dest.writeInt(N); 3026 for (int i = 0; i < N; i++) { 3027 mUriRelativeFilterGroups.get(i).writeToParcel(dest, flags); 3028 } 3029 } else { 3030 dest.writeInt(0); 3031 } 3032 } 3033 3034 /** 3035 * For debugging -- perform a check on the filter, return true if it passed 3036 * or false if it failed. 3037 * 3038 * {@hide} 3039 */ debugCheck()3040 public boolean debugCheck() { 3041 return true; 3042 3043 // This code looks for intent filters that do not specify data. 3044 /* 3045 if (mActions != null && mActions.size() == 1 3046 && mActions.contains(Intent.ACTION_MAIN)) { 3047 return true; 3048 } 3049 3050 if (mDataTypes == null && mDataSchemes == null) { 3051 Log.w("IntentFilter", "QUESTIONABLE INTENT FILTER:"); 3052 dump(Log.WARN, "IntentFilter", " "); 3053 return false; 3054 } 3055 3056 return true; 3057 */ 3058 } 3059 3060 /** 3061 * Perform a check on data paths and scheme specific parts of the intent filter. 3062 * Return true if it passed. 3063 * @hide 3064 */ checkDataPathAndSchemeSpecificParts()3065 public boolean checkDataPathAndSchemeSpecificParts() { 3066 final int numDataPath = mDataPaths == null 3067 ? 0 : mDataPaths.size(); 3068 final int numDataSchemeSpecificParts = mDataSchemeSpecificParts == null 3069 ? 0 : mDataSchemeSpecificParts.size(); 3070 for (int i = 0; i < numDataPath; i++) { 3071 if (!mDataPaths.get(i).check()) { 3072 return false; 3073 } 3074 } 3075 for (int i = 0; i < numDataSchemeSpecificParts; i++) { 3076 if (!mDataSchemeSpecificParts.get(i).check()) { 3077 return false; 3078 } 3079 } 3080 return true; 3081 } 3082 3083 /** @hide */ IntentFilter(Parcel source)3084 public IntentFilter(Parcel source) { 3085 List<String> actions = new ArrayList<>(); 3086 source.readStringList(actions); 3087 mActions = new ArraySet<>(actions); 3088 if (source.readInt() != 0) { 3089 mCategories = new ArrayList<String>(); 3090 source.readStringList(mCategories); 3091 } 3092 if (source.readInt() != 0) { 3093 mDataSchemes = new ArrayList<String>(); 3094 source.readStringList(mDataSchemes); 3095 } 3096 if (source.readInt() != 0) { 3097 mStaticDataTypes = new ArrayList<String>(); 3098 source.readStringList(mStaticDataTypes); 3099 } 3100 if (source.readInt() != 0) { 3101 mDataTypes = new ArrayList<String>(); 3102 source.readStringList(mDataTypes); 3103 } 3104 if (source.readInt() != 0) { 3105 mMimeGroups = new ArrayList<String>(); 3106 source.readStringList(mMimeGroups); 3107 } 3108 int N = source.readInt(); 3109 if (N > 0) { 3110 mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(N); 3111 for (int i=0; i<N; i++) { 3112 mDataSchemeSpecificParts.add(new PatternMatcher(source)); 3113 } 3114 } 3115 N = source.readInt(); 3116 if (N > 0) { 3117 mDataAuthorities = new ArrayList<AuthorityEntry>(N); 3118 for (int i=0; i<N; i++) { 3119 mDataAuthorities.add(new AuthorityEntry(source)); 3120 } 3121 } 3122 N = source.readInt(); 3123 if (N > 0) { 3124 mDataPaths = new ArrayList<PatternMatcher>(N); 3125 for (int i=0; i<N; i++) { 3126 mDataPaths.add(new PatternMatcher(source)); 3127 } 3128 } 3129 mPriority = source.readInt(); 3130 mHasStaticPartialTypes = source.readInt() > 0; 3131 mHasDynamicPartialTypes = source.readInt() > 0; 3132 setAutoVerify(source.readInt() > 0); 3133 setVisibilityToInstantApp(source.readInt()); 3134 mOrder = source.readInt(); 3135 if (source.readInt() != 0) { 3136 mExtras = PersistableBundle.CREATOR.createFromParcel(source); 3137 } 3138 N = source.readInt(); 3139 if (Flags.relativeReferenceIntentFilters() && N > 0) { 3140 mUriRelativeFilterGroups = new ArrayList<UriRelativeFilterGroup>(N); 3141 for (int i = 0; i < N; i++) { 3142 mUriRelativeFilterGroups.add(new UriRelativeFilterGroup(source)); 3143 } 3144 } 3145 } 3146 hasPartialTypes()3147 private boolean hasPartialTypes() { 3148 return mHasStaticPartialTypes || mHasDynamicPartialTypes; 3149 } 3150 findMimeType(String type)3151 private final boolean findMimeType(String type) { 3152 final ArrayList<String> t = mDataTypes; 3153 3154 if (type == null) { 3155 return false; 3156 } 3157 3158 if (t.contains(type)) { 3159 return true; 3160 } 3161 3162 // Deal with an Intent wanting to match every type in the IntentFilter. 3163 final int typeLength = type.length(); 3164 if (typeLength == 3 && type.equals("*/*")) { 3165 return !t.isEmpty(); 3166 } 3167 3168 // Deal with this IntentFilter wanting to match every Intent type. 3169 if (hasPartialTypes() && t.contains("*")) { 3170 return true; 3171 } 3172 3173 final int slashpos = type.indexOf('/'); 3174 if (slashpos > 0) { 3175 if (hasPartialTypes() && t.contains(type.substring(0, slashpos))) { 3176 return true; 3177 } 3178 if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') { 3179 // Need to look through all types for one that matches 3180 // our base... 3181 final int numTypes = t.size(); 3182 for (int i = 0; i < numTypes; i++) { 3183 final String v = t.get(i); 3184 if (type.regionMatches(0, v, 0, slashpos+1)) { 3185 return true; 3186 } 3187 } 3188 } 3189 } 3190 3191 return false; 3192 } 3193 3194 /** 3195 * @hide 3196 */ getHostsList()3197 public ArrayList<String> getHostsList() { 3198 ArrayList<String> result = new ArrayList<>(); 3199 Iterator<IntentFilter.AuthorityEntry> it = authoritiesIterator(); 3200 if (it != null) { 3201 while (it.hasNext()) { 3202 IntentFilter.AuthorityEntry entry = it.next(); 3203 result.add(entry.getHost()); 3204 } 3205 } 3206 return result; 3207 } 3208 3209 /** 3210 * @hide 3211 */ getHosts()3212 public String[] getHosts() { 3213 ArrayList<String> list = getHostsList(); 3214 return list.toArray(new String[list.size()]); 3215 } 3216 3217 /** 3218 * @hide 3219 */ filterEquals(IntentFilter f1, IntentFilter f2)3220 public static boolean filterEquals(IntentFilter f1, IntentFilter f2) { 3221 int s1 = f1.countActions(); 3222 int s2 = f2.countActions(); 3223 if (s1 != s2) { 3224 return false; 3225 } 3226 for (int i=0; i<s1; i++) { 3227 if (!f2.hasAction(f1.getAction(i))) { 3228 return false; 3229 } 3230 } 3231 s1 = f1.countCategories(); 3232 s2 = f2.countCategories(); 3233 if (s1 != s2) { 3234 return false; 3235 } 3236 for (int i=0; i<s1; i++) { 3237 if (!f2.hasCategory(f1.getCategory(i))) { 3238 return false; 3239 } 3240 } 3241 s1 = f1.countDataTypes(); 3242 s2 = f2.countDataTypes(); 3243 if (s1 != s2) { 3244 return false; 3245 } 3246 for (int i=0; i<s1; i++) { 3247 if (!f2.hasExactDataType(f1.getDataType(i))) { 3248 return false; 3249 } 3250 } 3251 s1 = f1.countDataSchemes(); 3252 s2 = f2.countDataSchemes(); 3253 if (s1 != s2) { 3254 return false; 3255 } 3256 for (int i=0; i<s1; i++) { 3257 if (!f2.hasDataScheme(f1.getDataScheme(i))) { 3258 return false; 3259 } 3260 } 3261 s1 = f1.countDataAuthorities(); 3262 s2 = f2.countDataAuthorities(); 3263 if (s1 != s2) { 3264 return false; 3265 } 3266 for (int i=0; i<s1; i++) { 3267 if (!f2.hasDataAuthority(f1.getDataAuthority(i))) { 3268 return false; 3269 } 3270 } 3271 s1 = f1.countDataPaths(); 3272 s2 = f2.countDataPaths(); 3273 if (s1 != s2) { 3274 return false; 3275 } 3276 for (int i=0; i<s1; i++) { 3277 if (!f2.hasDataPath(f1.getDataPath(i))) { 3278 return false; 3279 } 3280 } 3281 s1 = f1.countDataSchemeSpecificParts(); 3282 s2 = f2.countDataSchemeSpecificParts(); 3283 if (s1 != s2) { 3284 return false; 3285 } 3286 for (int i=0; i<s1; i++) { 3287 if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) { 3288 return false; 3289 } 3290 } 3291 return true; 3292 } 3293 } 3294