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