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 com.android.server; 18 19 import java.io.PrintWriter; 20 import java.util.ArrayList; 21 import java.util.Arrays; 22 import java.util.Collections; 23 import java.util.Comparator; 24 import java.util.Iterator; 25 import java.util.List; 26 import java.util.Set; 27 28 import android.net.Uri; 29 import android.util.FastImmutableArraySet; 30 import android.util.ArrayMap; 31 import android.util.ArraySet; 32 import android.util.Log; 33 import android.util.MutableInt; 34 import android.util.PrintWriterPrinter; 35 import android.util.Slog; 36 import android.util.LogPrinter; 37 import android.util.Printer; 38 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import com.android.internal.util.FastPrintWriter; 42 43 /** 44 * {@hide} 45 */ 46 public 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, ArrayMap<String, F[]> map, String packageName, 217 boolean printFilter, boolean collapseDuplicates) { 218 final String eprefix = prefix + " "; 219 final String fprefix = prefix + " "; 220 final ArrayMap<Object, MutableInt> found = new ArrayMap<>(); 221 boolean printedSomething = false; 222 Printer printer = null; 223 for (int mapi=0; mapi<map.size(); mapi++) { 224 F[] a = map.valueAt(mapi); 225 final int N = a.length; 226 boolean printedHeader = false; 227 F filter; 228 if (collapseDuplicates) { 229 found.clear(); 230 for (int i=0; i<N && (filter=a[i]) != null; i++) { 231 if (packageName != null && !isPackageForFilter(packageName, filter)) { 232 continue; 233 } 234 Object label = filterToLabel(filter); 235 int index = found.indexOfKey(label); 236 if (index < 0) { 237 found.put(label, new MutableInt(1)); 238 } else { 239 found.valueAt(index).value++; 240 } 241 } 242 for (int i=0; i<found.size(); i++) { 243 if (title != null) { 244 out.print(titlePrefix); out.println(title); 245 title = null; 246 } 247 if (!printedHeader) { 248 out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":"); 249 printedHeader = true; 250 } 251 printedSomething = true; 252 dumpFilterLabel(out, fprefix, found.keyAt(i), found.valueAt(i).value); 253 } 254 } else { 255 for (int i=0; i<N && (filter=a[i]) != null; i++) { 256 if (packageName != null && !isPackageForFilter(packageName, filter)) { 257 continue; 258 } 259 if (title != null) { 260 out.print(titlePrefix); out.println(title); 261 title = null; 262 } 263 if (!printedHeader) { 264 out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":"); 265 printedHeader = true; 266 } 267 printedSomething = true; 268 dumpFilter(out, fprefix, filter); 269 if (printFilter) { 270 if (printer == null) { 271 printer = new PrintWriterPrinter(out); 272 } 273 filter.dump(printer, fprefix + " "); 274 } 275 } 276 } 277 } 278 return printedSomething; 279 } 280 281 public boolean dump(PrintWriter out, String title, String prefix, String packageName, 282 boolean printFilter, boolean collapseDuplicates) { 283 String innerPrefix = prefix + " "; 284 String sepPrefix = "\n" + prefix; 285 String curPrefix = title + "\n" + prefix; 286 if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix, 287 mTypeToFilter, packageName, printFilter, collapseDuplicates)) { 288 curPrefix = sepPrefix; 289 } 290 if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix, 291 mBaseTypeToFilter, packageName, printFilter, collapseDuplicates)) { 292 curPrefix = sepPrefix; 293 } 294 if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix, 295 mWildTypeToFilter, packageName, printFilter, collapseDuplicates)) { 296 curPrefix = sepPrefix; 297 } 298 if (dumpMap(out, curPrefix, "Schemes:", innerPrefix, 299 mSchemeToFilter, packageName, printFilter, collapseDuplicates)) { 300 curPrefix = sepPrefix; 301 } 302 if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix, 303 mActionToFilter, packageName, printFilter, collapseDuplicates)) { 304 curPrefix = sepPrefix; 305 } 306 if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix, 307 mTypedActionToFilter, packageName, printFilter, collapseDuplicates)) { 308 curPrefix = sepPrefix; 309 } 310 return curPrefix == sepPrefix; 311 } 312 313 private class IteratorWrapper implements Iterator<F> { 314 private final Iterator<F> mI; 315 private F mCur; 316 317 IteratorWrapper(Iterator<F> it) { 318 mI = it; 319 } 320 321 public boolean hasNext() { 322 return mI.hasNext(); 323 } 324 325 public F next() { 326 return (mCur = mI.next()); 327 } 328 329 public void remove() { 330 if (mCur != null) { 331 removeFilterInternal(mCur); 332 } 333 mI.remove(); 334 } 335 336 } 337 338 /** 339 * Returns an iterator allowing filters to be removed. 340 */ 341 public Iterator<F> filterIterator() { 342 return new IteratorWrapper(mFilters.iterator()); 343 } 344 345 /** 346 * Returns a read-only set of the filters. 347 */ 348 public Set<F> filterSet() { 349 return Collections.unmodifiableSet(mFilters); 350 } 351 352 public List<R> queryIntentFromList(Intent intent, String resolvedType, 353 boolean defaultOnly, ArrayList<F[]> listCut, int userId) { 354 ArrayList<R> resultList = new ArrayList<R>(); 355 356 final boolean debug = localLOGV || 357 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); 358 359 FastImmutableArraySet<String> categories = getFastIntentCategories(intent); 360 final String scheme = intent.getScheme(); 361 int N = listCut.size(); 362 for (int i = 0; i < N; ++i) { 363 buildResolveList(intent, categories, debug, defaultOnly, 364 resolvedType, scheme, listCut.get(i), resultList, userId); 365 } 366 sortResults(resultList); 367 return resultList; 368 } 369 370 public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, 371 int userId) { 372 String scheme = intent.getScheme(); 373 374 ArrayList<R> finalList = new ArrayList<R>(); 375 376 final boolean debug = localLOGV || 377 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); 378 379 if (debug) Slog.v( 380 TAG, "Resolving type=" + resolvedType + " scheme=" + scheme 381 + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent); 382 383 F[] firstTypeCut = null; 384 F[] secondTypeCut = null; 385 F[] thirdTypeCut = null; 386 F[] schemeCut = null; 387 388 // If the intent includes a MIME type, then we want to collect all of 389 // the filters that match that MIME type. 390 if (resolvedType != null) { 391 int slashpos = resolvedType.indexOf('/'); 392 if (slashpos > 0) { 393 final String baseType = resolvedType.substring(0, slashpos); 394 if (!baseType.equals("*")) { 395 if (resolvedType.length() != slashpos+2 396 || resolvedType.charAt(slashpos+1) != '*') { 397 // Not a wild card, so we can just look for all filters that 398 // completely match or wildcards whose base type matches. 399 firstTypeCut = mTypeToFilter.get(resolvedType); 400 if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut)); 401 secondTypeCut = mWildTypeToFilter.get(baseType); 402 if (debug) Slog.v(TAG, "Second type cut: " 403 + Arrays.toString(secondTypeCut)); 404 } else { 405 // We can match anything with our base type. 406 firstTypeCut = mBaseTypeToFilter.get(baseType); 407 if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut)); 408 secondTypeCut = mWildTypeToFilter.get(baseType); 409 if (debug) Slog.v(TAG, "Second type cut: " 410 + Arrays.toString(secondTypeCut)); 411 } 412 // Any */* types always apply, but we only need to do this 413 // if the intent type was not already */*. 414 thirdTypeCut = mWildTypeToFilter.get("*"); 415 if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut)); 416 } else if (intent.getAction() != null) { 417 // The intent specified any type ({@literal *}/*). This 418 // can be a whole heck of a lot of things, so as a first 419 // cut let's use the action instead. 420 firstTypeCut = mTypedActionToFilter.get(intent.getAction()); 421 if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut)); 422 } 423 } 424 } 425 426 // If the intent includes a data URI, then we want to collect all of 427 // the filters that match its scheme (we will further refine matches 428 // on the authority and path by directly matching each resulting filter). 429 if (scheme != null) { 430 schemeCut = mSchemeToFilter.get(scheme); 431 if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut)); 432 } 433 434 // If the intent does not specify any data -- either a MIME type or 435 // a URI -- then we will only be looking for matches against empty 436 // data. 437 if (resolvedType == null && scheme == null && intent.getAction() != null) { 438 firstTypeCut = mActionToFilter.get(intent.getAction()); 439 if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut)); 440 } 441 442 FastImmutableArraySet<String> categories = getFastIntentCategories(intent); 443 if (firstTypeCut != null) { 444 buildResolveList(intent, categories, debug, defaultOnly, 445 resolvedType, scheme, firstTypeCut, finalList, userId); 446 } 447 if (secondTypeCut != null) { 448 buildResolveList(intent, categories, debug, defaultOnly, 449 resolvedType, scheme, secondTypeCut, finalList, userId); 450 } 451 if (thirdTypeCut != null) { 452 buildResolveList(intent, categories, debug, defaultOnly, 453 resolvedType, scheme, thirdTypeCut, finalList, userId); 454 } 455 if (schemeCut != null) { 456 buildResolveList(intent, categories, debug, defaultOnly, 457 resolvedType, scheme, schemeCut, finalList, userId); 458 } 459 sortResults(finalList); 460 461 if (debug) { 462 Slog.v(TAG, "Final result list:"); 463 for (int i=0; i<finalList.size(); i++) { 464 Slog.v(TAG, " " + finalList.get(i)); 465 } 466 } 467 return finalList; 468 } 469 470 /** 471 * Control whether the given filter is allowed to go into the result 472 * list. Mainly intended to prevent adding multiple filters for the 473 * same target object. 474 */ 475 protected boolean allowFilterResult(F filter, List<R> dest) { 476 return true; 477 } 478 479 /** 480 * Returns whether the object associated with the given filter is 481 * "stopped," that is whether it should not be included in the result 482 * if the intent requests to excluded stopped objects. 483 */ 484 protected boolean isFilterStopped(F filter, int userId) { 485 return false; 486 } 487 488 /** 489 * Returns whether this filter is owned by this package. This must be 490 * implemented to provide correct filtering of Intents that have 491 * specified a package name they are to be delivered to. 492 */ 493 protected abstract boolean isPackageForFilter(String packageName, F filter); 494 495 protected abstract F[] newArray(int size); 496 497 @SuppressWarnings("unchecked") 498 protected R newResult(F filter, int match, int userId) { 499 return (R)filter; 500 } 501 502 @SuppressWarnings("unchecked") 503 protected void sortResults(List<R> results) { 504 Collections.sort(results, mResolvePrioritySorter); 505 } 506 507 protected void dumpFilter(PrintWriter out, String prefix, F filter) { 508 out.print(prefix); out.println(filter); 509 } 510 511 protected Object filterToLabel(F filter) { 512 return "IntentFilter"; 513 } 514 515 protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) { 516 out.print(prefix); out.print(label); out.print(": "); out.println(count); 517 } 518 519 private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) { 520 F[] array = map.get(name); 521 if (array == null) { 522 array = newArray(2); 523 map.put(name, array); 524 array[0] = filter; 525 } else { 526 final int N = array.length; 527 int i = N; 528 while (i > 0 && array[i-1] == null) { 529 i--; 530 } 531 if (i < N) { 532 array[i] = filter; 533 } else { 534 F[] newa = newArray((N*3)/2); 535 System.arraycopy(array, 0, newa, 0, N); 536 newa[N] = filter; 537 map.put(name, newa); 538 } 539 } 540 } 541 542 private final int register_mime_types(F filter, String prefix) { 543 final Iterator<String> i = filter.typesIterator(); 544 if (i == null) { 545 return 0; 546 } 547 548 int num = 0; 549 while (i.hasNext()) { 550 String name = i.next(); 551 num++; 552 if (localLOGV) Slog.v(TAG, prefix + name); 553 String baseName = name; 554 final int slashpos = name.indexOf('/'); 555 if (slashpos > 0) { 556 baseName = name.substring(0, slashpos).intern(); 557 } else { 558 name = name + "/*"; 559 } 560 561 addFilter(mTypeToFilter, name, filter); 562 563 if (slashpos > 0) { 564 addFilter(mBaseTypeToFilter, baseName, filter); 565 } else { 566 addFilter(mWildTypeToFilter, baseName, filter); 567 } 568 } 569 570 return num; 571 } 572 573 private final int unregister_mime_types(F filter, String prefix) { 574 final Iterator<String> i = filter.typesIterator(); 575 if (i == null) { 576 return 0; 577 } 578 579 int num = 0; 580 while (i.hasNext()) { 581 String name = i.next(); 582 num++; 583 if (localLOGV) Slog.v(TAG, prefix + name); 584 String baseName = name; 585 final int slashpos = name.indexOf('/'); 586 if (slashpos > 0) { 587 baseName = name.substring(0, slashpos).intern(); 588 } else { 589 name = name + "/*"; 590 } 591 592 remove_all_objects(mTypeToFilter, name, filter); 593 594 if (slashpos > 0) { 595 remove_all_objects(mBaseTypeToFilter, baseName, filter); 596 } else { 597 remove_all_objects(mWildTypeToFilter, baseName, filter); 598 } 599 } 600 return num; 601 } 602 603 private final int register_intent_filter(F filter, Iterator<String> i, 604 ArrayMap<String, F[]> dest, String prefix) { 605 if (i == null) { 606 return 0; 607 } 608 609 int num = 0; 610 while (i.hasNext()) { 611 String name = i.next(); 612 num++; 613 if (localLOGV) Slog.v(TAG, prefix + name); 614 addFilter(dest, name, filter); 615 } 616 return num; 617 } 618 619 private final int unregister_intent_filter(F filter, Iterator<String> i, 620 ArrayMap<String, F[]> dest, String prefix) { 621 if (i == null) { 622 return 0; 623 } 624 625 int num = 0; 626 while (i.hasNext()) { 627 String name = i.next(); 628 num++; 629 if (localLOGV) Slog.v(TAG, prefix + name); 630 remove_all_objects(dest, name, filter); 631 } 632 return num; 633 } 634 635 private final void remove_all_objects(ArrayMap<String, F[]> map, String name, 636 Object object) { 637 F[] array = map.get(name); 638 if (array != null) { 639 int LAST = array.length-1; 640 while (LAST >= 0 && array[LAST] == null) { 641 LAST--; 642 } 643 for (int idx=LAST; idx>=0; idx--) { 644 if (array[idx] == object) { 645 final int remain = LAST - idx; 646 if (remain > 0) { 647 System.arraycopy(array, idx+1, array, idx, remain); 648 } 649 array[LAST] = null; 650 LAST--; 651 } 652 } 653 if (LAST < 0) { 654 map.remove(name); 655 } else if (LAST < (array.length/2)) { 656 F[] newa = newArray(LAST+2); 657 System.arraycopy(array, 0, newa, 0, LAST+1); 658 map.put(name, newa); 659 } 660 } 661 } 662 663 private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) { 664 final Set<String> categories = intent.getCategories(); 665 if (categories == null) { 666 return null; 667 } 668 return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()])); 669 } 670 671 private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, 672 boolean debug, boolean defaultOnly, 673 String resolvedType, String scheme, F[] src, List<R> dest, int userId) { 674 final String action = intent.getAction(); 675 final Uri data = intent.getData(); 676 final String packageName = intent.getPackage(); 677 678 final boolean excludingStopped = intent.isExcludingStopped(); 679 680 final Printer logPrinter; 681 final PrintWriter logPrintWriter; 682 if (debug) { 683 logPrinter = new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM); 684 logPrintWriter = new FastPrintWriter(logPrinter); 685 } else { 686 logPrinter = null; 687 logPrintWriter = null; 688 } 689 690 final int N = src != null ? src.length : 0; 691 boolean hasNonDefaults = false; 692 int i; 693 F filter; 694 for (i=0; i<N && (filter=src[i]) != null; i++) { 695 int match; 696 if (debug) Slog.v(TAG, "Matching against filter " + filter); 697 698 if (excludingStopped && isFilterStopped(filter, userId)) { 699 if (debug) { 700 Slog.v(TAG, " Filter's target is stopped; skipping"); 701 } 702 continue; 703 } 704 705 // Is delivery being limited to filters owned by a particular package? 706 if (packageName != null && !isPackageForFilter(packageName, filter)) { 707 if (debug) { 708 Slog.v(TAG, " Filter is not from package " + packageName + "; skipping"); 709 } 710 continue; 711 } 712 713 // Do we already have this one? 714 if (!allowFilterResult(filter, dest)) { 715 if (debug) { 716 Slog.v(TAG, " Filter's target already added"); 717 } 718 continue; 719 } 720 721 match = filter.match(action, resolvedType, scheme, data, categories, TAG); 722 if (match >= 0) { 723 if (debug) Slog.v(TAG, " Filter matched! match=0x" + 724 Integer.toHexString(match) + " hasDefault=" 725 + filter.hasCategory(Intent.CATEGORY_DEFAULT)); 726 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) { 727 final R oneResult = newResult(filter, match, userId); 728 if (oneResult != null) { 729 dest.add(oneResult); 730 if (debug) { 731 dumpFilter(logPrintWriter, " ", filter); 732 logPrintWriter.flush(); 733 filter.dump(logPrinter, " "); 734 } 735 } 736 } else { 737 hasNonDefaults = true; 738 } 739 } else { 740 if (debug) { 741 String reason; 742 switch (match) { 743 case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; 744 case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; 745 case IntentFilter.NO_MATCH_DATA: reason = "data"; break; 746 case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; 747 default: reason = "unknown reason"; break; 748 } 749 Slog.v(TAG, " Filter did not match: " + reason); 750 } 751 } 752 } 753 754 if (hasNonDefaults) { 755 if (dest.size() == 0) { 756 Slog.w(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT"); 757 } else if (dest.size() > 1) { 758 Slog.w(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT"); 759 } 760 } 761 } 762 763 // Sorts a List of IntentFilter objects into descending priority order. 764 @SuppressWarnings("rawtypes") 765 private static final Comparator mResolvePrioritySorter = new Comparator() { 766 public int compare(Object o1, Object o2) { 767 final int q1 = ((IntentFilter) o1).getPriority(); 768 final int q2 = ((IntentFilter) o2).getPriority(); 769 return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0); 770 } 771 }; 772 773 /** 774 * All filters that have been registered. 775 */ 776 private final ArraySet<F> mFilters = new ArraySet<F>(); 777 778 /** 779 * All of the MIME types that have been registered, such as "image/jpeg", 780 * "image/*", or "{@literal *}/*". 781 */ 782 private final ArrayMap<String, F[]> mTypeToFilter = new ArrayMap<String, F[]>(); 783 784 /** 785 * The base names of all of all fully qualified MIME types that have been 786 * registered, such as "image" or "*". Wild card MIME types such as 787 * "image/*" will not be here. 788 */ 789 private final ArrayMap<String, F[]> mBaseTypeToFilter = new ArrayMap<String, F[]>(); 790 791 /** 792 * The base names of all of the MIME types with a sub-type wildcard that 793 * have been registered. For example, a filter with "image/*" will be 794 * included here as "image" but one with "image/jpeg" will not be 795 * included here. This also includes the "*" for the "{@literal *}/*" 796 * MIME type. 797 */ 798 private final ArrayMap<String, F[]> mWildTypeToFilter = new ArrayMap<String, F[]>(); 799 800 /** 801 * All of the URI schemes (such as http) that have been registered. 802 */ 803 private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>(); 804 805 /** 806 * All of the actions that have been registered, but only those that did 807 * not specify data. 808 */ 809 private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>(); 810 811 /** 812 * All of the actions that have been registered and specified a MIME type. 813 */ 814 private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>(); 815 } 816