Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2011 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.am;
     18 
     19 import android.content.ComponentName;
     20 import android.os.Binder;
     21 import android.os.RemoteException;
     22 import android.os.UserHandle;
     23 import android.util.Slog;
     24 import android.util.SparseArray;
     25 import com.android.internal.os.TransferPipe;
     26 
     27 import java.io.FileDescriptor;
     28 import java.io.IOException;
     29 import java.io.PrintWriter;
     30 import java.util.ArrayList;
     31 import java.util.HashMap;
     32 import java.util.Iterator;
     33 import java.util.Map;
     34 import java.util.Set;
     35 
     36 /**
     37  * Keeps track of content providers by authority (name) and class. It separates the mapping by
     38  * user and ones that are not user-specific (system providers).
     39  */
     40 public final class ProviderMap {
     41 
     42     private static final String TAG = "ProviderMap";
     43 
     44     private static final boolean DBG = false;
     45 
     46     private final ActivityManagerService mAm;
     47 
     48     private final HashMap<String, ContentProviderRecord> mSingletonByName
     49             = new HashMap<String, ContentProviderRecord>();
     50     private final HashMap<ComponentName, ContentProviderRecord> mSingletonByClass
     51             = new HashMap<ComponentName, ContentProviderRecord>();
     52 
     53     private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
     54             = new SparseArray<HashMap<String, ContentProviderRecord>>();
     55     private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
     56             = new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
     57 
     58     ProviderMap(ActivityManagerService am) {
     59         mAm = am;
     60     }
     61 
     62     ContentProviderRecord getProviderByName(String name) {
     63         return getProviderByName(name, -1);
     64     }
     65 
     66     ContentProviderRecord getProviderByName(String name, int userId) {
     67         if (DBG) {
     68             Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
     69         }
     70         // Try to find it in the global list
     71         ContentProviderRecord record = mSingletonByName.get(name);
     72         if (record != null) {
     73             return record;
     74         }
     75 
     76         // Check the current user's list
     77         return getProvidersByName(userId).get(name);
     78     }
     79 
     80     ContentProviderRecord getProviderByClass(ComponentName name) {
     81         return getProviderByClass(name, -1);
     82     }
     83 
     84     ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
     85         if (DBG) {
     86             Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
     87         }
     88         // Try to find it in the global list
     89         ContentProviderRecord record = mSingletonByClass.get(name);
     90         if (record != null) {
     91             return record;
     92         }
     93 
     94         // Check the current user's list
     95         return getProvidersByClass(userId).get(name);
     96     }
     97 
     98     void putProviderByName(String name, ContentProviderRecord record) {
     99         if (DBG) {
    100             Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
    101                 + ", record uid = " + record.appInfo.uid);
    102         }
    103         if (record.singleton) {
    104             mSingletonByName.put(name, record);
    105         } else {
    106             final int userId = UserHandle.getUserId(record.appInfo.uid);
    107             getProvidersByName(userId).put(name, record);
    108         }
    109     }
    110 
    111     void putProviderByClass(ComponentName name, ContentProviderRecord record) {
    112         if (DBG) {
    113             Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
    114                 + ", record uid = " + record.appInfo.uid);
    115         }
    116         if (record.singleton) {
    117             mSingletonByClass.put(name, record);
    118         } else {
    119             final int userId = UserHandle.getUserId(record.appInfo.uid);
    120             getProvidersByClass(userId).put(name, record);
    121         }
    122     }
    123 
    124     void removeProviderByName(String name, int userId) {
    125         if (mSingletonByName.containsKey(name)) {
    126             if (DBG)
    127                 Slog.i(TAG, "Removing from globalByName name=" + name);
    128             mSingletonByName.remove(name);
    129         } else {
    130             if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
    131             if (DBG)
    132                 Slog.i(TAG,
    133                         "Removing from providersByName name=" + name + " user=" + userId);
    134             HashMap<String, ContentProviderRecord> map = getProvidersByName(userId);
    135             // map returned by getProvidersByName wouldn't be null
    136             map.remove(name);
    137             if (map.size() == 0) {
    138                 mProvidersByNamePerUser.remove(userId);
    139             }
    140         }
    141     }
    142 
    143     void removeProviderByClass(ComponentName name, int userId) {
    144         if (mSingletonByClass.containsKey(name)) {
    145             if (DBG)
    146                 Slog.i(TAG, "Removing from globalByClass name=" + name);
    147             mSingletonByClass.remove(name);
    148         } else {
    149             if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
    150             if (DBG)
    151                 Slog.i(TAG,
    152                         "Removing from providersByClass name=" + name + " user=" + userId);
    153             HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(userId);
    154             // map returned by getProvidersByClass wouldn't be null
    155             map.remove(name);
    156             if (map.size() == 0) {
    157                 mProvidersByClassPerUser.remove(userId);
    158             }
    159         }
    160     }
    161 
    162     private HashMap<String, ContentProviderRecord> getProvidersByName(int userId) {
    163         if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
    164         final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
    165         if (map == null) {
    166             HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
    167             mProvidersByNamePerUser.put(userId, newMap);
    168             return newMap;
    169         } else {
    170             return map;
    171         }
    172     }
    173 
    174     HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int userId) {
    175         if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
    176         final HashMap<ComponentName, ContentProviderRecord> map
    177                 = mProvidersByClassPerUser.get(userId);
    178         if (map == null) {
    179             HashMap<ComponentName, ContentProviderRecord> newMap
    180                     = new HashMap<ComponentName, ContentProviderRecord>();
    181             mProvidersByClassPerUser.put(userId, newMap);
    182             return newMap;
    183         } else {
    184             return map;
    185         }
    186     }
    187 
    188     private boolean collectPackageProvidersLocked(String packageName,
    189             Set<String> filterByClasses, boolean doit, boolean evenPersistent,
    190             HashMap<ComponentName, ContentProviderRecord> providers,
    191             ArrayList<ContentProviderRecord> result) {
    192         boolean didSomething = false;
    193         for (ContentProviderRecord provider : providers.values()) {
    194             final boolean sameComponent = packageName == null
    195                     || (provider.info.packageName.equals(packageName)
    196                         && (filterByClasses == null
    197                             || filterByClasses.contains(provider.name.getClassName())));
    198             if (sameComponent
    199                     && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
    200                 if (!doit) {
    201                     return true;
    202                 }
    203                 didSomething = true;
    204                 result.add(provider);
    205             }
    206         }
    207         return didSomething;
    208     }
    209 
    210     boolean collectPackageProvidersLocked(String packageName, Set<String> filterByClasses,
    211             boolean doit, boolean evenPersistent, int userId,
    212             ArrayList<ContentProviderRecord> result) {
    213         boolean didSomething = false;
    214         if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_SYSTEM) {
    215             didSomething = collectPackageProvidersLocked(packageName, filterByClasses,
    216                     doit, evenPersistent, mSingletonByClass, result);
    217         }
    218         if (!doit && didSomething) {
    219             return true;
    220         }
    221         if (userId == UserHandle.USER_ALL) {
    222             for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
    223                 if (collectPackageProvidersLocked(packageName, filterByClasses,
    224                         doit, evenPersistent, mProvidersByClassPerUser.valueAt(i), result)) {
    225                     if (!doit) {
    226                         return true;
    227                     }
    228                     didSomething = true;
    229                 }
    230             }
    231         } else {
    232             HashMap<ComponentName, ContentProviderRecord> items
    233                     = getProvidersByClass(userId);
    234             if (items != null) {
    235                 didSomething |= collectPackageProvidersLocked(packageName, filterByClasses,
    236                         doit, evenPersistent, items, result);
    237             }
    238         }
    239         return didSomething;
    240     }
    241 
    242     private boolean dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, String dumpPackage,
    243             String header, boolean needSep, HashMap<ComponentName, ContentProviderRecord> map) {
    244         Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
    245         boolean written = false;
    246         while (it.hasNext()) {
    247             Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
    248             ContentProviderRecord r = e.getValue();
    249             if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
    250                 continue;
    251             }
    252             if (needSep) {
    253                 pw.println("");
    254                 needSep = false;
    255             }
    256             if (header != null) {
    257                 pw.println(header);
    258                 header = null;
    259             }
    260             written = true;
    261             pw.print("  * ");
    262             pw.println(r);
    263             r.dump(pw, "    ", dumpAll);
    264         }
    265         return written;
    266     }
    267 
    268     private boolean dumpProvidersByNameLocked(PrintWriter pw, String dumpPackage,
    269             String header, boolean needSep, HashMap<String, ContentProviderRecord> map) {
    270         Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
    271         boolean written = false;
    272         while (it.hasNext()) {
    273             Map.Entry<String, ContentProviderRecord> e = it.next();
    274             ContentProviderRecord r = e.getValue();
    275             if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
    276                 continue;
    277             }
    278             if (needSep) {
    279                 pw.println("");
    280                 needSep = false;
    281             }
    282             if (header != null) {
    283                 pw.println(header);
    284                 header = null;
    285             }
    286             written = true;
    287             pw.print("  ");
    288             pw.print(e.getKey());
    289             pw.print(": ");
    290             pw.println(r.toShortString());
    291         }
    292         return written;
    293     }
    294 
    295     boolean dumpProvidersLocked(PrintWriter pw, boolean dumpAll, String dumpPackage) {
    296         boolean needSep = false;
    297 
    298         if (mSingletonByClass.size() > 0) {
    299             needSep |= dumpProvidersByClassLocked(pw, dumpAll, dumpPackage,
    300                     "  Published single-user content providers (by class):", needSep,
    301                     mSingletonByClass);
    302         }
    303 
    304         for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
    305             HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
    306             needSep |= dumpProvidersByClassLocked(pw, dumpAll, dumpPackage,
    307                     "  Published user " + mProvidersByClassPerUser.keyAt(i)
    308                             + " content providers (by class):", needSep, map);
    309         }
    310 
    311         if (dumpAll) {
    312             needSep |= dumpProvidersByNameLocked(pw, dumpPackage,
    313                     "  Single-user authority to provider mappings:", needSep, mSingletonByName);
    314 
    315             for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
    316                 needSep |= dumpProvidersByNameLocked(pw, dumpPackage,
    317                         "  User " + mProvidersByNamePerUser.keyAt(i)
    318                                 + " authority to provider mappings:", needSep,
    319                         mProvidersByNamePerUser.valueAt(i));
    320             }
    321         }
    322         return needSep;
    323     }
    324 
    325     protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
    326             int opti, boolean dumpAll) {
    327         ArrayList<ContentProviderRecord> allProviders = new ArrayList<ContentProviderRecord>();
    328         ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
    329 
    330         synchronized (mAm) {
    331             allProviders.addAll(mSingletonByClass.values());
    332             for (int i=0; i<mProvidersByClassPerUser.size(); i++) {
    333                 allProviders.addAll(mProvidersByClassPerUser.valueAt(i).values());
    334             }
    335 
    336             if ("all".equals(name)) {
    337                 providers.addAll(allProviders);
    338             } else {
    339                 ComponentName componentName = name != null
    340                         ? ComponentName.unflattenFromString(name) : null;
    341                 int objectId = 0;
    342                 if (componentName == null) {
    343                     // Not a '/' separated full component name; maybe an object ID?
    344                     try {
    345                         objectId = Integer.parseInt(name, 16);
    346                         name = null;
    347                         componentName = null;
    348                     } catch (RuntimeException e) {
    349                     }
    350                 }
    351 
    352                 for (int i=0; i<allProviders.size(); i++) {
    353                     ContentProviderRecord r1 = allProviders.get(i);
    354                     if (componentName != null) {
    355                         if (r1.name.equals(componentName)) {
    356                             providers.add(r1);
    357                         }
    358                     } else if (name != null) {
    359                         if (r1.name.flattenToString().contains(name)) {
    360                             providers.add(r1);
    361                         }
    362                     } else if (System.identityHashCode(r1) == objectId) {
    363                         providers.add(r1);
    364                     }
    365                 }
    366             }
    367         }
    368 
    369         if (providers.size() <= 0) {
    370             return false;
    371         }
    372 
    373         boolean needSep = false;
    374         for (int i=0; i<providers.size(); i++) {
    375             if (needSep) {
    376                 pw.println();
    377             }
    378             needSep = true;
    379             dumpProvider("", fd, pw, providers.get(i), args, dumpAll);
    380         }
    381         return true;
    382     }
    383 
    384     /**
    385      * Before invoking IApplicationThread.dumpProvider(), print meta information to the print
    386      * writer and handle passed flags.
    387      */
    388     private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
    389             final ContentProviderRecord r, String[] args, boolean dumpAll) {
    390         for (String s: args) {
    391             if (!dumpAll && s.contains("--proto")) {
    392                 if (r.proc != null && r.proc.thread != null) {
    393                     dumpToTransferPipe(null , fd, pw, r, args);
    394                 }
    395                 return;
    396             }
    397         }
    398         String innerPrefix = prefix + "  ";
    399         synchronized (mAm) {
    400             pw.print(prefix); pw.print("PROVIDER ");
    401             pw.print(r);
    402             pw.print(" pid=");
    403             if (r.proc != null) {
    404                 pw.println(r.proc.pid);
    405             } else {
    406                 pw.println("(not running)");
    407             }
    408             if (dumpAll) {
    409                 r.dump(pw, innerPrefix, true);
    410             }
    411         }
    412         if (r.proc != null && r.proc.thread != null) {
    413             pw.println("    Client:");
    414             pw.flush();
    415             dumpToTransferPipe("      ", fd, pw, r, args);
    416         }
    417     }
    418 
    419     /**
    420      * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider without
    421      * any meta string (e.g., provider info, indentation) written to the file descriptor.
    422      */
    423     private void dumpToTransferPipe(String prefix, FileDescriptor fd, PrintWriter pw,
    424             final ContentProviderRecord r, String[] args) {
    425         try {
    426             TransferPipe tp = new TransferPipe();
    427             try {
    428                 r.proc.thread.dumpProvider(
    429                     tp.getWriteFd(), r.provider.asBinder(), args);
    430                 tp.setBufferPrefix(prefix);
    431                 // Short timeout, since blocking here can
    432                 // deadlock with the application.
    433                 tp.go(fd, 2000);
    434             } finally {
    435                 tp.kill();
    436             }
    437         } catch (IOException ex) {
    438             pw.println("      Failure while dumping the provider: " + ex);
    439         } catch (RemoteException ex) {
    440             pw.println("      Got a RemoteException while dumping the service");
    441         }
    442     }
    443 }
    444