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