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 
ProviderMap(ActivityManagerService am)58     ProviderMap(ActivityManagerService am) {
59         mAm = am;
60     }
61 
getProviderByName(String name)62     ContentProviderRecord getProviderByName(String name) {
63         return getProviderByName(name, -1);
64     }
65 
getProviderByName(String name, int userId)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 
getProviderByClass(ComponentName name)80     ContentProviderRecord getProviderByClass(ComponentName name) {
81         return getProviderByClass(name, -1);
82     }
83 
getProviderByClass(ComponentName name, int userId)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 
putProviderByName(String name, ContentProviderRecord record)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 
putProviderByClass(ComponentName name, ContentProviderRecord record)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 
removeProviderByName(String name, int userId)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 
removeProviderByClass(ComponentName name, int userId)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 
getProvidersByName(int userId)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 
getProvidersByClass(int userId)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 
collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, HashMap<ComponentName, ContentProviderRecord> providers, ArrayList<ContentProviderRecord> result)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 
collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, int userId, ArrayList<ContentProviderRecord> result)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_OWNER) {
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 
dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, String dumpPackage, String header, boolean needSep, HashMap<ComponentName, ContentProviderRecord> map)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 
dumpProvidersByNameLocked(PrintWriter pw, String dumpPackage, String header, boolean needSep, HashMap<String, ContentProviderRecord> map)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 
dumpProvidersLocked(PrintWriter pw, boolean dumpAll, String dumpPackage)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 
dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll)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      * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider if
386      * there is a thread associated with the provider.
387      */
dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, String[] args, boolean dumpAll)388     private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
389             final ContentProviderRecord r, String[] args, boolean dumpAll) {
390         String innerPrefix = prefix + "  ";
391         synchronized (mAm) {
392             pw.print(prefix); pw.print("PROVIDER ");
393                     pw.print(r);
394                     pw.print(" pid=");
395                     if (r.proc != null) pw.println(r.proc.pid);
396                     else pw.println("(not running)");
397             if (dumpAll) {
398                 r.dump(pw, innerPrefix, true);
399             }
400         }
401         if (r.proc != null && r.proc.thread != null) {
402             pw.println("    Client:");
403             pw.flush();
404             try {
405                 TransferPipe tp = new TransferPipe();
406                 try {
407                     r.proc.thread.dumpProvider(
408                             tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args);
409                     tp.setBufferPrefix("      ");
410                     // Short timeout, since blocking here can
411                     // deadlock with the application.
412                     tp.go(fd, 2000);
413                 } finally {
414                     tp.kill();
415                 }
416             } catch (IOException ex) {
417                 pw.println("      Failure while dumping the provider: " + ex);
418             } catch (RemoteException ex) {
419                 pw.println("      Got a RemoteException while dumping the service");
420             }
421         }
422     }
423 }
424