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