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 
ProviderMap(ActivityManagerService am)57     ProviderMap(ActivityManagerService am) {
58         mAm = am;
59     }
60 
getProviderByName(String name)61     ContentProviderRecord getProviderByName(String name) {
62         return getProviderByName(name, -1);
63     }
64 
getProviderByName(String name, int userId)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 
getProviderByClass(ComponentName name)79     ContentProviderRecord getProviderByClass(ComponentName name) {
80         return getProviderByClass(name, -1);
81     }
82 
getProviderByClass(ComponentName name, int userId)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 
putProviderByName(String name, ContentProviderRecord record)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 
putProviderByClass(ComponentName name, ContentProviderRecord record)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 
removeProviderByName(String name, int userId)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 
removeProviderByClass(ComponentName name, int userId)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 
getProvidersByName(int userId)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 
getProvidersByClass(int userId)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 
collectForceStopProvidersLocked(String name, int appId, boolean doit, boolean evenPersistent, int userId, HashMap<ComponentName, ContentProviderRecord> providers, ArrayList<ContentProviderRecord> result)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 
collectForceStopProviders(String name, int appId, boolean doit, boolean evenPersistent, int userId, ArrayList<ContentProviderRecord> result)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 
dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, String dumpPackage, String header, boolean needSep, HashMap<ComponentName, ContentProviderRecord> map)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 
dumpProvidersByNameLocked(PrintWriter pw, String dumpPackage, String header, boolean needSep, HashMap<String, ContentProviderRecord> map)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 
dumpProvidersLocked(PrintWriter pw, boolean dumpAll, String dumpPackage)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 
dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll)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      */
dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, String[] args, boolean dumpAll)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