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.Collections;
     22 import java.util.Comparator;
     23 import java.util.HashMap;
     24 import java.util.HashSet;
     25 import java.util.Iterator;
     26 import java.util.List;
     27 import java.util.Map;
     28 import java.util.Set;
     29 
     30 import android.net.Uri;
     31 import android.util.FastImmutableArraySet;
     32 import android.util.Log;
     33 import android.util.PrintWriterPrinter;
     34 import android.util.Slog;
     35 import android.util.LogPrinter;
     36 import android.util.Printer;
     37 
     38 import android.content.Intent;
     39 import android.content.IntentFilter;
     40 
     41 /**
     42  * Temporary for verification of new implementation.
     43  * {@hide}
     44  */
     45 public abstract class IntentResolverOld<F extends IntentFilter, R extends Object> {
     46     final private static String TAG = "IntentResolver";
     47     final private static boolean DEBUG = false;
     48     final private static boolean localLOGV = DEBUG || false;
     49 
     50     public void addFilter(F f) {
     51         if (localLOGV) {
     52             Slog.v(TAG, "Adding filter: " + f);
     53             f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
     54             Slog.v(TAG, "    Building Lookup Maps:");
     55         }
     56 
     57         mFilters.add(f);
     58         int numS = register_intent_filter(f, f.schemesIterator(),
     59                 mSchemeToFilter, "      Scheme: ");
     60         int numT = register_mime_types(f, "      Type: ");
     61         if (numS == 0 && numT == 0) {
     62             register_intent_filter(f, f.actionsIterator(),
     63                     mActionToFilter, "      Action: ");
     64         }
     65         if (numT != 0) {
     66             register_intent_filter(f, f.actionsIterator(),
     67                     mTypedActionToFilter, "      TypedAction: ");
     68         }
     69     }
     70 
     71     public void removeFilter(F f) {
     72         removeFilterInternal(f);
     73         mFilters.remove(f);
     74     }
     75 
     76     void removeFilterInternal(F f) {
     77         if (localLOGV) {
     78             Slog.v(TAG, "Removing filter: " + f);
     79             f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
     80             Slog.v(TAG, "    Cleaning Lookup Maps:");
     81         }
     82 
     83         int numS = unregister_intent_filter(f, f.schemesIterator(),
     84                 mSchemeToFilter, "      Scheme: ");
     85         int numT = unregister_mime_types(f, "      Type: ");
     86         if (numS == 0 && numT == 0) {
     87             unregister_intent_filter(f, f.actionsIterator(),
     88                     mActionToFilter, "      Action: ");
     89         }
     90         if (numT != 0) {
     91             unregister_intent_filter(f, f.actionsIterator(),
     92                     mTypedActionToFilter, "      TypedAction: ");
     93         }
     94     }
     95 
     96     boolean dumpMap(PrintWriter out, String titlePrefix, String title,
     97             String prefix, Map<String, ArrayList<F>> map, String packageName,
     98             boolean printFilter) {
     99         String eprefix = prefix + "  ";
    100         String fprefix = prefix + "    ";
    101         boolean printedSomething = false;
    102         Printer printer = null;
    103         for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
    104             ArrayList<F> a = e.getValue();
    105             final int N = a.size();
    106             boolean printedHeader = false;
    107             for (int i=0; i<N; i++) {
    108                 F filter = a.get(i);
    109                 if (packageName != null && !isPackageForFilter(packageName, filter)) {
    110                     continue;
    111                 }
    112                 if (title != null) {
    113                     out.print(titlePrefix); out.println(title);
    114                     title = null;
    115                 }
    116                 if (!printedHeader) {
    117                     out.print(eprefix); out.print(e.getKey()); out.println(":");
    118                     printedHeader = true;
    119                 }
    120                 printedSomething = true;
    121                 dumpFilter(out, fprefix, filter);
    122                 if (printFilter) {
    123                     if (printer == null) {
    124                         printer = new PrintWriterPrinter(out);
    125                     }
    126                     filter.dump(printer, fprefix + "  ");
    127                 }
    128             }
    129         }
    130         return printedSomething;
    131     }
    132 
    133     public boolean dump(PrintWriter out, String title, String prefix, String packageName,
    134             boolean printFilter) {
    135         String innerPrefix = prefix + "  ";
    136         String sepPrefix = "\n" + prefix;
    137         String curPrefix = title + "\n" + prefix;
    138         if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
    139                 mTypeToFilter, packageName, printFilter)) {
    140             curPrefix = sepPrefix;
    141         }
    142         if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
    143                 mBaseTypeToFilter, packageName, printFilter)) {
    144             curPrefix = sepPrefix;
    145         }
    146         if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
    147                 mWildTypeToFilter, packageName, printFilter)) {
    148             curPrefix = sepPrefix;
    149         }
    150         if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
    151                 mSchemeToFilter, packageName, printFilter)) {
    152             curPrefix = sepPrefix;
    153         }
    154         if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
    155                 mActionToFilter, packageName, printFilter)) {
    156             curPrefix = sepPrefix;
    157         }
    158         if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
    159                 mTypedActionToFilter, packageName, printFilter)) {
    160             curPrefix = sepPrefix;
    161         }
    162         return curPrefix == sepPrefix;
    163     }
    164 
    165     private class IteratorWrapper implements Iterator<F> {
    166         private final Iterator<F> mI;
    167         private F mCur;
    168 
    169         IteratorWrapper(Iterator<F> it) {
    170             mI = it;
    171         }
    172 
    173         public boolean hasNext() {
    174             return mI.hasNext();
    175         }
    176 
    177         public F next() {
    178             return (mCur = mI.next());
    179         }
    180 
    181         public void remove() {
    182             if (mCur != null) {
    183                 removeFilterInternal(mCur);
    184             }
    185             mI.remove();
    186         }
    187 
    188     }
    189 
    190     /**
    191      * Returns an iterator allowing filters to be removed.
    192      */
    193     public Iterator<F> filterIterator() {
    194         return new IteratorWrapper(mFilters.iterator());
    195     }
    196 
    197     /**
    198      * Returns a read-only set of the filters.
    199      */
    200     public Set<F> filterSet() {
    201         return Collections.unmodifiableSet(mFilters);
    202     }
    203 
    204     public List<R> queryIntentFromList(Intent intent, String resolvedType,
    205             boolean defaultOnly, ArrayList<ArrayList<F>> listCut, int userId) {
    206         ArrayList<R> resultList = new ArrayList<R>();
    207 
    208         final boolean debug = localLOGV ||
    209                 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
    210 
    211         FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
    212         final String scheme = intent.getScheme();
    213         int N = listCut.size();
    214         for (int i = 0; i < N; ++i) {
    215             buildResolveList(intent, categories, debug, defaultOnly,
    216                     resolvedType, scheme, listCut.get(i), resultList, userId);
    217         }
    218         sortResults(resultList);
    219         return resultList;
    220     }
    221 
    222     public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
    223             int userId) {
    224         String scheme = intent.getScheme();
    225 
    226         ArrayList<R> finalList = new ArrayList<R>();
    227 
    228         final boolean debug = localLOGV ||
    229                 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
    230 
    231         if (debug) Slog.v(
    232             TAG, "Resolving type " + resolvedType + " scheme " + scheme
    233             + " of intent " + intent);
    234 
    235         ArrayList<F> firstTypeCut = null;
    236         ArrayList<F> secondTypeCut = null;
    237         ArrayList<F> thirdTypeCut = null;
    238         ArrayList<F> schemeCut = null;
    239 
    240         // If the intent includes a MIME type, then we want to collect all of
    241         // the filters that match that MIME type.
    242         if (resolvedType != null) {
    243             int slashpos = resolvedType.indexOf('/');
    244             if (slashpos > 0) {
    245                 final String baseType = resolvedType.substring(0, slashpos);
    246                 if (!baseType.equals("*")) {
    247                     if (resolvedType.length() != slashpos+2
    248                             || resolvedType.charAt(slashpos+1) != '*') {
    249                         // Not a wild card, so we can just look for all filters that
    250                         // completely match or wildcards whose base type matches.
    251                         firstTypeCut = mTypeToFilter.get(resolvedType);
    252                         if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
    253                         secondTypeCut = mWildTypeToFilter.get(baseType);
    254                         if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
    255                     } else {
    256                         // We can match anything with our base type.
    257                         firstTypeCut = mBaseTypeToFilter.get(baseType);
    258                         if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
    259                         secondTypeCut = mWildTypeToFilter.get(baseType);
    260                         if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
    261                     }
    262                     // Any */* types always apply, but we only need to do this
    263                     // if the intent type was not already */*.
    264                     thirdTypeCut = mWildTypeToFilter.get("*");
    265                     if (debug) Slog.v(TAG, "Third type cut: " + thirdTypeCut);
    266                 } else if (intent.getAction() != null) {
    267                     // The intent specified any type ({@literal *}/*).  This
    268                     // can be a whole heck of a lot of things, so as a first
    269                     // cut let's use the action instead.
    270                     firstTypeCut = mTypedActionToFilter.get(intent.getAction());
    271                     if (debug) Slog.v(TAG, "Typed Action list: " + firstTypeCut);
    272                 }
    273             }
    274         }
    275 
    276         // If the intent includes a data URI, then we want to collect all of
    277         // the filters that match its scheme (we will further refine matches
    278         // on the authority and path by directly matching each resulting filter).
    279         if (scheme != null) {
    280             schemeCut = mSchemeToFilter.get(scheme);
    281             if (debug) Slog.v(TAG, "Scheme list: " + schemeCut);
    282         }
    283 
    284         // If the intent does not specify any data -- either a MIME type or
    285         // a URI -- then we will only be looking for matches against empty
    286         // data.
    287         if (resolvedType == null && scheme == null && intent.getAction() != null) {
    288             firstTypeCut = mActionToFilter.get(intent.getAction());
    289             if (debug) Slog.v(TAG, "Action list: " + firstTypeCut);
    290         }
    291 
    292         FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
    293         if (firstTypeCut != null) {
    294             buildResolveList(intent, categories, debug, defaultOnly,
    295                     resolvedType, scheme, firstTypeCut, finalList, userId);
    296         }
    297         if (secondTypeCut != null) {
    298             buildResolveList(intent, categories, debug, defaultOnly,
    299                     resolvedType, scheme, secondTypeCut, finalList, userId);
    300         }
    301         if (thirdTypeCut != null) {
    302             buildResolveList(intent, categories, debug, defaultOnly,
    303                     resolvedType, scheme, thirdTypeCut, finalList, userId);
    304         }
    305         if (schemeCut != null) {
    306             buildResolveList(intent, categories, debug, defaultOnly,
    307                     resolvedType, scheme, schemeCut, finalList, userId);
    308         }
    309         sortResults(finalList);
    310 
    311         if (debug) {
    312             Slog.v(TAG, "Final result list:");
    313             for (R r : finalList) {
    314                 Slog.v(TAG, "  " + r);
    315             }
    316         }
    317         return finalList;
    318     }
    319 
    320     /**
    321      * Control whether the given filter is allowed to go into the result
    322      * list.  Mainly intended to prevent adding multiple filters for the
    323      * same target object.
    324      */
    325     protected boolean allowFilterResult(F filter, List<R> dest) {
    326         return true;
    327     }
    328 
    329     /**
    330      * Returns whether the object associated with the given filter is
    331      * "stopped," that is whether it should not be included in the result
    332      * if the intent requests to excluded stopped objects.
    333      */
    334     protected boolean isFilterStopped(F filter, int userId) {
    335         return false;
    336     }
    337 
    338     /**
    339      * Returns whether this filter is owned by this package. This must be
    340      * implemented to provide correct filtering of Intents that have
    341      * specified a package name they are to be delivered to.
    342      */
    343     protected abstract boolean isPackageForFilter(String packageName, F filter);
    344 
    345     @SuppressWarnings("unchecked")
    346     protected R newResult(F filter, int match, int userId) {
    347         return (R)filter;
    348     }
    349 
    350     @SuppressWarnings("unchecked")
    351     protected void sortResults(List<R> results) {
    352         Collections.sort(results, mResolvePrioritySorter);
    353     }
    354 
    355     protected void dumpFilter(PrintWriter out, String prefix, F filter) {
    356         out.print(prefix); out.println(filter);
    357     }
    358 
    359     private final int register_mime_types(F filter, String prefix) {
    360         final Iterator<String> i = filter.typesIterator();
    361         if (i == null) {
    362             return 0;
    363         }
    364 
    365         int num = 0;
    366         while (i.hasNext()) {
    367             String name = i.next();
    368             num++;
    369             if (localLOGV) Slog.v(TAG, prefix + name);
    370             String baseName = name;
    371             final int slashpos = name.indexOf('/');
    372             if (slashpos > 0) {
    373                 baseName = name.substring(0, slashpos).intern();
    374             } else {
    375                 name = name + "/*";
    376             }
    377 
    378             ArrayList<F> array = mTypeToFilter.get(name);
    379             if (array == null) {
    380                 //Slog.v(TAG, "Creating new array for " + name);
    381                 array = new ArrayList<F>();
    382                 mTypeToFilter.put(name, array);
    383             }
    384             array.add(filter);
    385 
    386             if (slashpos > 0) {
    387                 array = mBaseTypeToFilter.get(baseName);
    388                 if (array == null) {
    389                     //Slog.v(TAG, "Creating new array for " + name);
    390                     array = new ArrayList<F>();
    391                     mBaseTypeToFilter.put(baseName, array);
    392                 }
    393                 array.add(filter);
    394             } else {
    395                 array = mWildTypeToFilter.get(baseName);
    396                 if (array == null) {
    397                     //Slog.v(TAG, "Creating new array for " + name);
    398                     array = new ArrayList<F>();
    399                     mWildTypeToFilter.put(baseName, array);
    400                 }
    401                 array.add(filter);
    402             }
    403         }
    404 
    405         return num;
    406     }
    407 
    408     private final int unregister_mime_types(F filter, String prefix) {
    409         final Iterator<String> i = filter.typesIterator();
    410         if (i == null) {
    411             return 0;
    412         }
    413 
    414         int num = 0;
    415         while (i.hasNext()) {
    416             String name = i.next();
    417             num++;
    418             if (localLOGV) Slog.v(TAG, prefix + name);
    419             String baseName = name;
    420             final int slashpos = name.indexOf('/');
    421             if (slashpos > 0) {
    422                 baseName = name.substring(0, slashpos).intern();
    423             } else {
    424                 name = name + "/*";
    425             }
    426 
    427             if (!remove_all_objects(mTypeToFilter.get(name), filter)) {
    428                 mTypeToFilter.remove(name);
    429             }
    430 
    431             if (slashpos > 0) {
    432                 if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) {
    433                     mBaseTypeToFilter.remove(baseName);
    434                 }
    435             } else {
    436                 if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) {
    437                     mWildTypeToFilter.remove(baseName);
    438                 }
    439             }
    440         }
    441         return num;
    442     }
    443 
    444     private final int register_intent_filter(F filter, Iterator<String> i,
    445             HashMap<String, ArrayList<F>> dest, String prefix) {
    446         if (i == null) {
    447             return 0;
    448         }
    449 
    450         int num = 0;
    451         while (i.hasNext()) {
    452             String name = i.next();
    453             num++;
    454             if (localLOGV) Slog.v(TAG, prefix + name);
    455             ArrayList<F> array = dest.get(name);
    456             if (array == null) {
    457                 //Slog.v(TAG, "Creating new array for " + name);
    458                 array = new ArrayList<F>();
    459                 dest.put(name, array);
    460             }
    461             array.add(filter);
    462         }
    463         return num;
    464     }
    465 
    466     private final int unregister_intent_filter(F filter, Iterator<String> i,
    467             HashMap<String, ArrayList<F>> dest, String prefix) {
    468         if (i == null) {
    469             return 0;
    470         }
    471 
    472         int num = 0;
    473         while (i.hasNext()) {
    474             String name = i.next();
    475             num++;
    476             if (localLOGV) Slog.v(TAG, prefix + name);
    477             if (!remove_all_objects(dest.get(name), filter)) {
    478                 dest.remove(name);
    479             }
    480         }
    481         return num;
    482     }
    483 
    484     private final boolean remove_all_objects(List<F> list, Object object) {
    485         if (list != null) {
    486             int N = list.size();
    487             for (int idx=0; idx<N; idx++) {
    488                 if (list.get(idx) == object) {
    489                     list.remove(idx);
    490                     idx--;
    491                     N--;
    492                 }
    493             }
    494             return N > 0;
    495         }
    496         return false;
    497     }
    498 
    499     private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
    500         final Set<String> categories = intent.getCategories();
    501         if (categories == null) {
    502             return null;
    503         }
    504         return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
    505     }
    506 
    507     private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
    508             boolean debug, boolean defaultOnly,
    509             String resolvedType, String scheme, List<F> src, List<R> dest, int userId) {
    510         final String action = intent.getAction();
    511         final Uri data = intent.getData();
    512         final String packageName = intent.getPackage();
    513 
    514         final boolean excludingStopped = intent.isExcludingStopped();
    515 
    516         final int N = src != null ? src.size() : 0;
    517         boolean hasNonDefaults = false;
    518         int i;
    519         for (i=0; i<N; i++) {
    520             F filter = src.get(i);
    521             int match;
    522             if (debug) Slog.v(TAG, "Matching against filter " + filter);
    523 
    524             if (excludingStopped && isFilterStopped(filter, userId)) {
    525                 if (debug) {
    526                     Slog.v(TAG, "  Filter's target is stopped; skipping");
    527                 }
    528                 continue;
    529             }
    530 
    531             // Is delivery being limited to filters owned by a particular package?
    532             if (packageName != null && !isPackageForFilter(packageName, filter)) {
    533                 if (debug) {
    534                     Slog.v(TAG, "  Filter is not from package " + packageName + "; skipping");
    535                 }
    536                 continue;
    537             }
    538 
    539             // Do we already have this one?
    540             if (!allowFilterResult(filter, dest)) {
    541                 if (debug) {
    542                     Slog.v(TAG, "  Filter's target already added");
    543                 }
    544                 continue;
    545             }
    546 
    547             match = filter.match(action, resolvedType, scheme, data, categories, TAG);
    548             if (match >= 0) {
    549                 if (debug) Slog.v(TAG, "  Filter matched!  match=0x" +
    550                         Integer.toHexString(match));
    551                 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
    552                     final R oneResult = newResult(filter, match, userId);
    553                     if (oneResult != null) {
    554                         dest.add(oneResult);
    555                     }
    556                 } else {
    557                     hasNonDefaults = true;
    558                 }
    559             } else {
    560                 if (debug) {
    561                     String reason;
    562                     switch (match) {
    563                         case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
    564                         case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
    565                         case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
    566                         case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
    567                         default: reason = "unknown reason"; break;
    568                     }
    569                     Slog.v(TAG, "  Filter did not match: " + reason);
    570                 }
    571             }
    572         }
    573 
    574         if (dest.size() == 0 && hasNonDefaults) {
    575             Slog.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT");
    576         }
    577     }
    578 
    579     // Sorts a List of IntentFilter objects into descending priority order.
    580     @SuppressWarnings("rawtypes")
    581     private static final Comparator mResolvePrioritySorter = new Comparator() {
    582         public int compare(Object o1, Object o2) {
    583             final int q1 = ((IntentFilter) o1).getPriority();
    584             final int q2 = ((IntentFilter) o2).getPriority();
    585             return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);
    586         }
    587     };
    588 
    589     /**
    590      * All filters that have been registered.
    591      */
    592     final HashSet<F> mFilters = new HashSet<F>();
    593 
    594     /**
    595      * All of the MIME types that have been registered, such as "image/jpeg",
    596      * "image/*", or "{@literal *}/*".
    597      */
    598     final HashMap<String, ArrayList<F>> mTypeToFilter
    599             = new HashMap<String, ArrayList<F>>();
    600 
    601     /**
    602      * The base names of all of all fully qualified MIME types that have been
    603      * registered, such as "image" or "*".  Wild card MIME types such as
    604      * "image/*" will not be here.
    605      */
    606     final HashMap<String, ArrayList<F>> mBaseTypeToFilter
    607             = new HashMap<String, ArrayList<F>>();
    608 
    609     /**
    610      * The base names of all of the MIME types with a sub-type wildcard that
    611      * have been registered.  For example, a filter with "image/*" will be
    612      * included here as "image" but one with "image/jpeg" will not be
    613      * included here.  This also includes the "*" for the "{@literal *}/*"
    614      * MIME type.
    615      */
    616     final HashMap<String, ArrayList<F>> mWildTypeToFilter
    617             = new HashMap<String, ArrayList<F>>();
    618 
    619     /**
    620      * All of the URI schemes (such as http) that have been registered.
    621      */
    622     final HashMap<String, ArrayList<F>> mSchemeToFilter
    623             = new HashMap<String, ArrayList<F>>();
    624 
    625     /**
    626      * All of the actions that have been registered, but only those that did
    627      * not specify data.
    628      */
    629     final HashMap<String, ArrayList<F>> mActionToFilter
    630             = new HashMap<String, ArrayList<F>>();
    631 
    632     /**
    633      * All of the actions that have been registered and specified a MIME type.
    634      */
    635     final HashMap<String, ArrayList<F>> mTypedActionToFilter
    636             = new HashMap<String, ArrayList<F>>();
    637 }
    638 
    639