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