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