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