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         try {
355             mAm.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
356             ArrayList<ContentProviderRecord> providers = getProvidersForName(name);
357 
358             if (providers.size() <= 0) {
359                 return false;
360             }
361 
362             boolean needSep = false;
363             for (int i=0; i<providers.size(); i++) {
364                 if (needSep) {
365                     pw.println();
366                 }
367                 needSep = true;
368                 dumpProvider("", fd, pw, providers.get(i), args, dumpAll);
369             }
370             return true;
371         } finally {
372             mAm.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
373         }
374     }
375 
376     /**
377      * Before invoking IApplicationThread.dumpProvider(), print meta information to the print
378      * writer and handle passed flags.
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         final IApplicationThread thread = r.proc != null ? r.proc.getThread() : null;
383         for (String s: args) {
384             if (!dumpAll && s.contains("--proto")) {
385                 if (thread != null) {
386                     dumpToTransferPipe(null , fd, pw, r, thread, args);
387                 }
388                 return;
389             }
390         }
391         String innerPrefix = prefix + "  ";
392         synchronized (mAm) {
393             pw.print(prefix); pw.print("PROVIDER ");
394             pw.print(r);
395             pw.print(" pid=");
396             if (r.proc != null) {
397                 pw.println(r.proc.getPid());
398             } else {
399                 pw.println("(not running)");
400             }
401             if (dumpAll) {
402                 r.dump(pw, innerPrefix, true);
403             }
404         }
405         if (thread != null) {
406             pw.println("    Client:");
407             pw.flush();
408             dumpToTransferPipe("      ", fd, pw, r, thread, args);
409         }
410     }
411 
412     /**
413      * Similar to the dumpProvider, but only dumps the first matching provider.
414      * The provider is responsible for dumping as proto.
415      */
dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name, String[] args)416     protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
417             String[] args) {
418         //add back the --proto arg, which was stripped out by PriorityDump
419         String[] newArgs = Arrays.copyOf(args, args.length + 1);
420         newArgs[args.length] = "--proto";
421 
422         ArrayList<ContentProviderRecord> providers = getProvidersForName(name);
423 
424         if (providers.size() <= 0) {
425             return false;
426         }
427 
428         // Only dump the first provider, since we are dumping in proto format
429         for (int i = 0; i < providers.size(); i++) {
430             final ContentProviderRecord r = providers.get(i);
431             IApplicationThread thread;
432             if (r.proc != null && (thread = r.proc.getThread()) != null) {
433                 dumpToTransferPipe(null, fd, pw, r, thread, newArgs);
434                 return true;
435             }
436         }
437         return false;
438     }
439 
440     /**
441      * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider without
442      * any meta string (e.g., provider info, indentation) written to the file descriptor.
443      */
dumpToTransferPipe(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, final IApplicationThread thread, String[] args)444     private void dumpToTransferPipe(String prefix, FileDescriptor fd, PrintWriter pw,
445             final ContentProviderRecord r, final IApplicationThread thread, String[] args) {
446         try {
447             TransferPipe tp = new TransferPipe();
448             try {
449                 thread.dumpProvider(
450                     tp.getWriteFd(), r.provider.asBinder(), args);
451                 tp.setBufferPrefix(prefix);
452                 // Short timeout, since blocking here can
453                 // deadlock with the application.
454                 tp.go(fd, 2000);
455             } finally {
456                 tp.kill();
457             }
458         } catch (IOException ex) {
459             pw.println("      Failure while dumping the provider: " + ex);
460         } catch (RemoteException ex) {
461             pw.println("      Got a RemoteException while dumping the service");
462         }
463     }
464 }
465