IntentResolver.java revision f2ac2761276e4972f6463d6818c9f5798bdc9a4d
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 17package com.android.server; 18 19import java.io.PrintWriter; 20import java.util.ArrayList; 21import java.util.Arrays; 22import java.util.Collections; 23import java.util.Comparator; 24import java.util.HashSet; 25import java.util.Iterator; 26import java.util.List; 27import java.util.Map; 28import java.util.Set; 29 30import android.net.Uri; 31import android.util.FastImmutableArraySet; 32import android.util.ArrayMap; 33import android.util.Log; 34import android.util.PrintWriterPrinter; 35import android.util.Slog; 36import android.util.LogPrinter; 37import android.util.Printer; 38 39import android.content.Intent; 40import android.content.IntentFilter; 41import com.android.internal.util.FastPrintWriter; 42 43/** 44 * {@hide} 45 */ 46public abstract class IntentResolver<F extends IntentFilter, R extends Object> { 47 final private static String TAG = "IntentResolver"; 48 final private static boolean DEBUG = false; 49 final private static boolean localLOGV = DEBUG || false; 50 51 public void addFilter(F f) { 52 if (localLOGV) { 53 Slog.v(TAG, "Adding filter: " + f); 54 f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); 55 Slog.v(TAG, " Building Lookup Maps:"); 56 } 57 58 mFilters.add(f); 59 int numS = register_intent_filter(f, f.schemesIterator(), 60 mSchemeToFilter, " Scheme: "); 61 int numT = register_mime_types(f, " Type: "); 62 if (numS == 0 && numT == 0) { 63 register_intent_filter(f, f.actionsIterator(), 64 mActionToFilter, " Action: "); 65 } 66 if (numT != 0) { 67 register_intent_filter(f, f.actionsIterator(), 68 mTypedActionToFilter, " TypedAction: "); 69 } 70 } 71 72 private boolean filterEquals(IntentFilter f1, IntentFilter f2) { 73 int s1 = f1.countActions(); 74 int s2 = f2.countActions(); 75 if (s1 != s2) { 76 return false; 77 } 78 for (int i=0; i<s1; i++) { 79 if (!f2.hasAction(f1.getAction(i))) { 80 return false; 81 } 82 } 83 s1 = f1.countCategories(); 84 s2 = f2.countCategories(); 85 if (s1 != s2) { 86 return false; 87 } 88 for (int i=0; i<s1; i++) { 89 if (!f2.hasCategory(f1.getCategory(i))) { 90 return false; 91 } 92 } 93 s1 = f1.countDataTypes(); 94 s2 = f2.countDataTypes(); 95 if (s1 != s2) { 96 return false; 97 } 98 for (int i=0; i<s1; i++) { 99 if (!f2.hasExactDataType(f1.getDataType(i))) { 100 return false; 101 } 102 } 103 s1 = f1.countDataSchemes(); 104 s2 = f2.countDataSchemes(); 105 if (s1 != s2) { 106 return false; 107 } 108 for (int i=0; i<s1; i++) { 109 if (!f2.hasDataScheme(f1.getDataScheme(i))) { 110 return false; 111 } 112 } 113 s1 = f1.countDataAuthorities(); 114 s2 = f2.countDataAuthorities(); 115 if (s1 != s2) { 116 return false; 117 } 118 for (int i=0; i<s1; i++) { 119 if (!f2.hasDataAuthority(f1.getDataAuthority(i))) { 120 return false; 121 } 122 } 123 s1 = f1.countDataPaths(); 124 s2 = f2.countDataPaths(); 125 if (s1 != s2) { 126 return false; 127 } 128 for (int i=0; i<s1; i++) { 129 if (!f2.hasDataPath(f1.getDataPath(i))) { 130 return false; 131 } 132 } 133 s1 = f1.countDataSchemeSpecificParts(); 134 s2 = f2.countDataSchemeSpecificParts(); 135 if (s1 != s2) { 136 return false; 137 } 138 for (int i=0; i<s1; i++) { 139 if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) { 140 return false; 141 } 142 } 143 return true; 144 } 145 146 private ArrayList<F> collectFilters(F[] array, IntentFilter matching) { 147 ArrayList<F> res = null; 148 if (array != null) { 149 for (int i=0; i<array.length; i++) { 150 F cur = array[i]; 151 if (cur == null) { 152 break; 153 } 154 if (filterEquals(cur, matching)) { 155 if (res == null) { 156 res = new ArrayList<>(); 157 } 158 res.add(cur); 159 } 160 } 161 } 162 return res; 163 } 164 165 public ArrayList<F> findFilters(IntentFilter matching) { 166 if (matching.countDataSchemes() == 1) { 167 // Fast case. 168 return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching); 169 } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) { 170 // Another fast case. 171 return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching); 172 } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0 173 && matching.countActions() == 1) { 174 // Last fast case. 175 return collectFilters(mActionToFilter.get(matching.getAction(0)), matching); 176 } else { 177 ArrayList<F> res = null; 178 for (F cur : mFilters) { 179 if (filterEquals(cur, matching)) { 180 if (res == null) { 181 res = new ArrayList<>(); 182 } 183 res.add(cur); 184 } 185 } 186 return res; 187 } 188 } 189 190 public void removeFilter(F f) { 191 removeFilterInternal(f); 192 mFilters.remove(f); 193 } 194 195 void removeFilterInternal(F f) { 196 if (localLOGV) { 197 Slog.v(TAG, "Removing filter: " + f); 198 f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " "); 199 Slog.v(TAG, " Cleaning Lookup Maps:"); 200 } 201 202 int numS = unregister_intent_filter(f, f.schemesIterator(), 203 mSchemeToFilter, " Scheme: "); 204 int numT = unregister_mime_types(f, " Type: "); 205 if (numS == 0 && numT == 0) { 206 unregister_intent_filter(f, f.actionsIterator(), 207 mActionToFilter, " Action: "); 208 } 209 if (numT != 0) { 210 unregister_intent_filter(f, f.actionsIterator(), 211 mTypedActionToFilter, " TypedAction: "); 212 } 213 } 214 215 boolean dumpMap(PrintWriter out, String titlePrefix, String title, 216 String prefix, Map<String, F[]> map, String packageName, 217 boolean printFilter) { 218 String eprefix = prefix + " "; 219 String fprefix = prefix + " "; 220 boolean printedSomething = false; 221 Printer printer = null; 222 for (Map.Entry<String, F[]> e : map.entrySet()) { 223 F[] a = e.getValue(); 224 final int N = a.length; 225 boolean printedHeader = false; 226 F filter; 227 for (int i=0; i<N && (filter=a[i]) != null; i++) { 228 if (packageName != null && !isPackageForFilter(packageName, filter)) { 229 continue; 230 } 231 if (title != null) { 232 out.print(titlePrefix); out.println(title); 233 title = null; 234 } 235 if (!printedHeader) { 236 out.print(eprefix); out.print(e.getKey()); out.println(":"); 237 printedHeader = true; 238 } 239 printedSomething = true; 240 dumpFilter(out, fprefix, filter); 241 if (printFilter) { 242 if (printer == null) { 243 printer = new PrintWriterPrinter(out); 244 } 245 filter.dump(printer, fprefix + " "); 246 } 247 } 248 } 249 return printedSomething; 250 } 251 252 public boolean dump(PrintWriter out, String title, String prefix, String packageName, 253 boolean printFilter) { 254 String innerPrefix = prefix + " "; 255 String sepPrefix = "\n" + prefix; 256 String curPrefix = title + "\n" + prefix; 257 if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix, 258 mTypeToFilter, packageName, printFilter)) { 259 curPrefix = sepPrefix; 260 } 261 if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix, 262 mBaseTypeToFilter, packageName, printFilter)) { 263 curPrefix = sepPrefix; 264 } 265 if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix, 266 mWildTypeToFilter, packageName, printFilter)) { 267 curPrefix = sepPrefix; 268 } 269 if (dumpMap(out, curPrefix, "Schemes:", innerPrefix, 270 mSchemeToFilter, packageName, printFilter)) { 271 curPrefix = sepPrefix; 272 } 273 if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix, 274 mActionToFilter, packageName, printFilter)) { 275 curPrefix = sepPrefix; 276 } 277 if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix, 278 mTypedActionToFilter, packageName, printFilter)) { 279 curPrefix = sepPrefix; 280 } 281 return curPrefix == sepPrefix; 282 } 283 284 private class IteratorWrapper implements Iterator<F> { 285 private final Iterator<F> mI; 286 private F mCur; 287 288 IteratorWrapper(Iterator<F> it) { 289 mI = it; 290 } 291 292 public boolean hasNext() { 293 return mI.hasNext(); 294 } 295 296 public F next() { 297 return (mCur = mI.next()); 298 } 299 300 public void remove() { 301 if (mCur != null) { 302 removeFilterInternal(mCur); 303 } 304 mI.remove(); 305 } 306 307 } 308 309 /** 310 * Returns an iterator allowing filters to be removed. 311 */ 312 public Iterator<F> filterIterator() { 313 return new IteratorWrapper(mFilters.iterator()); 314 } 315 316 /** 317 * Returns a read-only set of the filters. 318 */ 319 public Set<F> filterSet() { 320 return Collections.unmodifiableSet(mFilters); 321 } 322 323 public List<R> queryIntentFromList(Intent intent, String resolvedType, 324 boolean defaultOnly, ArrayList<F[]> listCut, int userId) { 325 ArrayList<R> resultList = new ArrayList<R>(); 326 327 final boolean debug = localLOGV || 328 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); 329 330 FastImmutableArraySet<String> categories = getFastIntentCategories(intent); 331 final String scheme = intent.getScheme(); 332 int N = listCut.size(); 333 for (int i = 0; i < N; ++i) { 334 buildResolveList(intent, categories, debug, defaultOnly, 335 resolvedType, scheme, listCut.get(i), resultList, userId); 336 } 337 sortResults(resultList); 338 return resultList; 339 } 340 341 public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, 342 int userId) { 343 String scheme = intent.getScheme(); 344 345 ArrayList<R> finalList = new ArrayList<R>(); 346 347 final boolean debug = localLOGV || 348 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); 349 350 if (debug) Slog.v( 351 TAG, "Resolving type=" + resolvedType + " scheme=" + scheme 352 + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent); 353 354 F[] firstTypeCut = null; 355 F[] secondTypeCut = null; 356 F[] thirdTypeCut = null; 357 F[] schemeCut = null; 358 359 // If the intent includes a MIME type, then we want to collect all of 360 // the filters that match that MIME type. 361 if (resolvedType != null) { 362 int slashpos = resolvedType.indexOf('/'); 363 if (slashpos > 0) { 364 final String baseType = resolvedType.substring(0, slashpos); 365 if (!baseType.equals("*")) { 366 if (resolvedType.length() != slashpos+2 367 || resolvedType.charAt(slashpos+1) != '*') { 368 // Not a wild card, so we can just look for all filters that 369 // completely match or wildcards whose base type matches. 370 firstTypeCut = mTypeToFilter.get(resolvedType); 371 if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut)); 372 secondTypeCut = mWildTypeToFilter.get(baseType); 373 if (debug) Slog.v(TAG, "Second type cut: " 374 + Arrays.toString(secondTypeCut)); 375 } else { 376 // We can match anything with our base type. 377 firstTypeCut = mBaseTypeToFilter.get(baseType); 378 if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut)); 379 secondTypeCut = mWildTypeToFilter.get(baseType); 380 if (debug) Slog.v(TAG, "Second type cut: " 381 + Arrays.toString(secondTypeCut)); 382 } 383 // Any */* types always apply, but we only need to do this 384 // if the intent type was not already */*. 385 thirdTypeCut = mWildTypeToFilter.get("*"); 386 if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut)); 387 } else if (intent.getAction() != null) { 388 // The intent specified any type ({@literal *}/*). This 389 // can be a whole heck of a lot of things, so as a first 390 // cut let's use the action instead. 391 firstTypeCut = mTypedActionToFilter.get(intent.getAction()); 392 if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut)); 393 } 394 } 395 } 396 397 // If the intent includes a data URI, then we want to collect all of 398 // the filters that match its scheme (we will further refine matches 399 // on the authority and path by directly matching each resulting filter). 400 if (scheme != null) { 401 schemeCut = mSchemeToFilter.get(scheme); 402 if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut)); 403 } 404 405 // If the intent does not specify any data -- either a MIME type or 406 // a URI -- then we will only be looking for matches against empty 407 // data. 408 if (resolvedType == null && scheme == null && intent.getAction() != null) { 409 firstTypeCut = mActionToFilter.get(intent.getAction()); 410 if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut)); 411 } 412 413 FastImmutableArraySet<String> categories = getFastIntentCategories(intent); 414 if (firstTypeCut != null) { 415 buildResolveList(intent, categories, debug, defaultOnly, 416 resolvedType, scheme, firstTypeCut, finalList, userId); 417 } 418 if (secondTypeCut != null) { 419 buildResolveList(intent, categories, debug, defaultOnly, 420 resolvedType, scheme, secondTypeCut, finalList, userId); 421 } 422 if (thirdTypeCut != null) { 423 buildResolveList(intent, categories, debug, defaultOnly, 424 resolvedType, scheme, thirdTypeCut, finalList, userId); 425 } 426 if (schemeCut != null) { 427 buildResolveList(intent, categories, debug, defaultOnly, 428 resolvedType, scheme, schemeCut, finalList, userId); 429 } 430 sortResults(finalList); 431 432 if (debug) { 433 Slog.v(TAG, "Final result list:"); 434 for (int i=0; i<finalList.size(); i++) { 435 Slog.v(TAG, " " + finalList.get(i)); 436 } 437 } 438 return finalList; 439 } 440 441 /** 442 * Control whether the given filter is allowed to go into the result 443 * list. Mainly intended to prevent adding multiple filters for the 444 * same target object. 445 */ 446 protected boolean allowFilterResult(F filter, List<R> dest) { 447 return true; 448 } 449 450 /** 451 * Returns whether the object associated with the given filter is 452 * "stopped," that is whether it should not be included in the result 453 * if the intent requests to excluded stopped objects. 454 */ 455 protected boolean isFilterStopped(F filter, int userId) { 456 return false; 457 } 458 459 /** 460 * Returns whether this filter is owned by this package. This must be 461 * implemented to provide correct filtering of Intents that have 462 * specified a package name they are to be delivered to. 463 */ 464 protected abstract boolean isPackageForFilter(String packageName, F filter); 465 466 protected abstract F[] newArray(int size); 467 468 @SuppressWarnings("unchecked") 469 protected R newResult(F filter, int match, int userId) { 470 return (R)filter; 471 } 472 473 @SuppressWarnings("unchecked") 474 protected void sortResults(List<R> results) { 475 Collections.sort(results, mResolvePrioritySorter); 476 } 477 478 protected void dumpFilter(PrintWriter out, String prefix, F filter) { 479 out.print(prefix); out.println(filter); 480 } 481 482 private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) { 483 F[] array = map.get(name); 484 if (array == null) { 485 array = newArray(2); 486 map.put(name, array); 487 array[0] = filter; 488 } else { 489 final int N = array.length; 490 int i = N; 491 while (i > 0 && array[i-1] == null) { 492 i--; 493 } 494 if (i < N) { 495 array[i] = filter; 496 } else { 497 F[] newa = newArray((N*3)/2); 498 System.arraycopy(array, 0, newa, 0, N); 499 newa[N] = filter; 500 map.put(name, newa); 501 } 502 } 503 } 504 505 private final int register_mime_types(F filter, String prefix) { 506 final Iterator<String> i = filter.typesIterator(); 507 if (i == null) { 508 return 0; 509 } 510 511 int num = 0; 512 while (i.hasNext()) { 513 String name = i.next(); 514 num++; 515 if (localLOGV) Slog.v(TAG, prefix + name); 516 String baseName = name; 517 final int slashpos = name.indexOf('/'); 518 if (slashpos > 0) { 519 baseName = name.substring(0, slashpos).intern(); 520 } else { 521 name = name + "/*"; 522 } 523 524 addFilter(mTypeToFilter, name, filter); 525 526 if (slashpos > 0) { 527 addFilter(mBaseTypeToFilter, baseName, filter); 528 } else { 529 addFilter(mWildTypeToFilter, baseName, filter); 530 } 531 } 532 533 return num; 534 } 535 536 private final int unregister_mime_types(F filter, String prefix) { 537 final Iterator<String> i = filter.typesIterator(); 538 if (i == null) { 539 return 0; 540 } 541 542 int num = 0; 543 while (i.hasNext()) { 544 String name = i.next(); 545 num++; 546 if (localLOGV) Slog.v(TAG, prefix + name); 547 String baseName = name; 548 final int slashpos = name.indexOf('/'); 549 if (slashpos > 0) { 550 baseName = name.substring(0, slashpos).intern(); 551 } else { 552 name = name + "/*"; 553 } 554 555 remove_all_objects(mTypeToFilter, name, filter); 556 557 if (slashpos > 0) { 558 remove_all_objects(mBaseTypeToFilter, baseName, filter); 559 } else { 560 remove_all_objects(mWildTypeToFilter, baseName, filter); 561 } 562 } 563 return num; 564 } 565 566 private final int register_intent_filter(F filter, Iterator<String> i, 567 ArrayMap<String, F[]> dest, String prefix) { 568 if (i == null) { 569 return 0; 570 } 571 572 int num = 0; 573 while (i.hasNext()) { 574 String name = i.next(); 575 num++; 576 if (localLOGV) Slog.v(TAG, prefix + name); 577 addFilter(dest, name, filter); 578 } 579 return num; 580 } 581 582 private final int unregister_intent_filter(F filter, Iterator<String> i, 583 ArrayMap<String, F[]> dest, String prefix) { 584 if (i == null) { 585 return 0; 586 } 587 588 int num = 0; 589 while (i.hasNext()) { 590 String name = i.next(); 591 num++; 592 if (localLOGV) Slog.v(TAG, prefix + name); 593 remove_all_objects(dest, name, filter); 594 } 595 return num; 596 } 597 598 private final void remove_all_objects(ArrayMap<String, F[]> map, String name, 599 Object object) { 600 F[] array = map.get(name); 601 if (array != null) { 602 int LAST = array.length-1; 603 while (LAST >= 0 && array[LAST] == null) { 604 LAST--; 605 } 606 for (int idx=LAST; idx>=0; idx--) { 607 if (array[idx] == object) { 608 final int remain = LAST - idx; 609 if (remain > 0) { 610 System.arraycopy(array, idx+1, array, idx, remain); 611 } 612 array[LAST] = null; 613 LAST--; 614 } 615 } 616 if (LAST < 0) { 617 map.remove(name); 618 } else if (LAST < (array.length/2)) { 619 F[] newa = newArray(LAST+2); 620 System.arraycopy(array, 0, newa, 0, LAST+1); 621 map.put(name, newa); 622 } 623 } 624 } 625 626 private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) { 627 final Set<String> categories = intent.getCategories(); 628 if (categories == null) { 629 return null; 630 } 631 return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()])); 632 } 633 634 private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, 635 boolean debug, boolean defaultOnly, 636 String resolvedType, String scheme, F[] src, List<R> dest, int userId) { 637 final String action = intent.getAction(); 638 final Uri data = intent.getData(); 639 final String packageName = intent.getPackage(); 640 641 final boolean excludingStopped = intent.isExcludingStopped(); 642 643 final Printer logPrinter; 644 final PrintWriter logPrintWriter; 645 if (debug) { 646 logPrinter = new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM); 647 logPrintWriter = new FastPrintWriter(logPrinter); 648 } else { 649 logPrinter = null; 650 logPrintWriter = null; 651 } 652 653 final int N = src != null ? src.length : 0; 654 boolean hasNonDefaults = false; 655 int i; 656 F filter; 657 for (i=0; i<N && (filter=src[i]) != null; i++) { 658 int match; 659 if (debug) Slog.v(TAG, "Matching against filter " + filter); 660 661 if (excludingStopped && isFilterStopped(filter, userId)) { 662 if (debug) { 663 Slog.v(TAG, " Filter's target is stopped; skipping"); 664 } 665 continue; 666 } 667 668 // Is delivery being limited to filters owned by a particular package? 669 if (packageName != null && !isPackageForFilter(packageName, filter)) { 670 if (debug) { 671 Slog.v(TAG, " Filter is not from package " + packageName + "; skipping"); 672 } 673 continue; 674 } 675 676 // Do we already have this one? 677 if (!allowFilterResult(filter, dest)) { 678 if (debug) { 679 Slog.v(TAG, " Filter's target already added"); 680 } 681 continue; 682 } 683 684 match = filter.match(action, resolvedType, scheme, data, categories, TAG); 685 if (match >= 0) { 686 if (debug) Slog.v(TAG, " Filter matched! match=0x" + 687 Integer.toHexString(match) + " hasDefault=" 688 + filter.hasCategory(Intent.CATEGORY_DEFAULT)); 689 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) { 690 final R oneResult = newResult(filter, match, userId); 691 if (oneResult != null) { 692 dest.add(oneResult); 693 if (debug) { 694 dumpFilter(logPrintWriter, " ", filter); 695 logPrintWriter.flush(); 696 filter.dump(logPrinter, " "); 697 } 698 } 699 } else { 700 hasNonDefaults = true; 701 } 702 } else { 703 if (debug) { 704 String reason; 705 switch (match) { 706 case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; 707 case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; 708 case IntentFilter.NO_MATCH_DATA: reason = "data"; break; 709 case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; 710 default: reason = "unknown reason"; break; 711 } 712 Slog.v(TAG, " Filter did not match: " + reason); 713 } 714 } 715 } 716 717 if (hasNonDefaults) { 718 if (dest.size() == 0) { 719 Slog.w(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT"); 720 } else if (dest.size() > 1) { 721 Slog.w(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT"); 722 } 723 } 724 } 725 726 // Sorts a List of IntentFilter objects into descending priority order. 727 @SuppressWarnings("rawtypes") 728 private static final Comparator mResolvePrioritySorter = new Comparator() { 729 public int compare(Object o1, Object o2) { 730 final int q1 = ((IntentFilter) o1).getPriority(); 731 final int q2 = ((IntentFilter) o2).getPriority(); 732 return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0); 733 } 734 }; 735 736 /** 737 * All filters that have been registered. 738 */ 739 private final HashSet<F> mFilters = new HashSet<F>(); 740 741 /** 742 * All of the MIME types that have been registered, such as "image/jpeg", 743 * "image/*", or "{@literal *}/*". 744 */ 745 private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>(); 746 747 /** 748 * The base names of all of all fully qualified MIME types that have been 749 * registered, such as "image" or "*". Wild card MIME types such as 750 * "image/*" will not be here. 751 */ 752 private final ArrayMap<String, F[]> mBaseTypeToFilter = new ArrayMap<String, F[]>(); 753 754 /** 755 * The base names of all of the MIME types with a sub-type wildcard that 756 * have been registered. For example, a filter with "image/*" will be 757 * included here as "image" but one with "image/jpeg" will not be 758 * included here. This also includes the "*" for the "{@literal *}/*" 759 * MIME type. 760 */ 761 private final ArrayMap<String, F[]> mWildTypeToFilter = new ArrayMap<String, F[]>(); 762 763 /** 764 * All of the URI schemes (such as http) that have been registered. 765 */ 766 private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>(); 767 768 /** 769 * All of the actions that have been registered, but only those that did 770 * not specify data. 771 */ 772 private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>(); 773 774 /** 775 * All of the actions that have been registered and specified a MIME type. 776 */ 777 private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>(); 778} 779