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