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