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