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