• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ProviderMap(ActivityManagerService am)65     ProviderMap(ActivityManagerService am) {
66         mAm = am;
67     }
68 
getProviderByName(String name)69     ContentProviderRecord getProviderByName(String name) {
70         return getProviderByName(name, -1);
71     }
72 
getProviderByName(String name, int userId)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 
getProviderByClass(ComponentName name)87     ContentProviderRecord getProviderByClass(ComponentName name) {
88         return getProviderByClass(name, -1);
89     }
90 
getProviderByClass(ComponentName name, int userId)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 
putProviderByName(String name, ContentProviderRecord record)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 
putProviderByClass(ComponentName name, ContentProviderRecord record)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 
removeProviderByName(String name, int userId)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 
removeProviderByClass(ComponentName name, int userId)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 
getProvidersByName(int userId)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 
getProvidersByClass(int userId)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 
collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, HashMap<ComponentName, ContentProviderRecord> providers, ArrayList<ContentProviderRecord> result)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 
collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, int userId, ArrayList<ContentProviderRecord> result)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 
dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, String dumpPackage, String header, boolean needSep, HashMap<ComponentName, ContentProviderRecord> map)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 
dumpProvidersByNameLocked(PrintWriter pw, String dumpPackage, String header, boolean needSep, HashMap<String, ContentProviderRecord> map)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 
dumpProvidersLocked(PrintWriter pw, boolean dumpAll, String dumpPackage)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 
getProvidersForName(String name)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 
dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll)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      */
dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, String[] args, boolean dumpAll)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      */
dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name, String[] args)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      */
dumpToTransferPipe(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, String[] args)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