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