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