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