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