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