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