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