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