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