• 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.os.Binder;
21 import android.os.Process;
22 import android.os.RemoteException;
23 import android.os.UserId;
24 import android.util.Slog;
25 import android.util.SparseArray;
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 import java.util.Map.Entry;
35 import java.util.Set;
36 
37 /**
38  * Keeps track of content providers by authority (name) and class. It separates the mapping by
39  * user and ones that are not user-specific (system providers).
40  */
41 public class ProviderMap {
42 
43     private static final String TAG = "ProviderMap";
44 
45     private static final boolean DBG = false;
46 
47     private final HashMap<String, ContentProviderRecord> mGlobalByName
48             = new HashMap<String, ContentProviderRecord>();
49     private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
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 
getProviderByName(String name)57     ContentProviderRecord getProviderByName(String name) {
58         return getProviderByName(name, -1);
59     }
60 
getProviderByName(String name, int userId)61     ContentProviderRecord getProviderByName(String name, int userId) {
62         if (DBG) {
63             Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
64         }
65         // Try to find it in the global list
66         ContentProviderRecord record = mGlobalByName.get(name);
67         if (record != null) {
68             return record;
69         }
70 
71         // Check the current user's list
72         return getProvidersByName(userId).get(name);
73     }
74 
getProviderByClass(ComponentName name)75     ContentProviderRecord getProviderByClass(ComponentName name) {
76         return getProviderByClass(name, -1);
77     }
78 
getProviderByClass(ComponentName name, int userId)79     ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
80         if (DBG) {
81             Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
82         }
83         // Try to find it in the global list
84         ContentProviderRecord record = mGlobalByClass.get(name);
85         if (record != null) {
86             return record;
87         }
88 
89         // Check the current user's list
90         return getProvidersByClass(userId).get(name);
91     }
92 
putProviderByName(String name, ContentProviderRecord record)93     void putProviderByName(String name, ContentProviderRecord record) {
94         if (DBG) {
95             Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
96                 + ", record uid = " + record.appInfo.uid);
97         }
98         if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
99             mGlobalByName.put(name, record);
100         } else {
101             final int userId = UserId.getUserId(record.appInfo.uid);
102             getProvidersByName(userId).put(name, record);
103         }
104     }
105 
putProviderByClass(ComponentName name, ContentProviderRecord record)106     void putProviderByClass(ComponentName name, ContentProviderRecord record) {
107         if (DBG) {
108             Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
109                 + ", record uid = " + record.appInfo.uid);
110         }
111         if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
112             mGlobalByClass.put(name, record);
113         } else {
114             final int userId = UserId.getUserId(record.appInfo.uid);
115             getProvidersByClass(userId).put(name, record);
116         }
117     }
118 
removeProviderByName(String name, int optionalUserId)119     void removeProviderByName(String name, int optionalUserId) {
120         if (mGlobalByName.containsKey(name)) {
121             if (DBG)
122                 Slog.i(TAG, "Removing from globalByName name=" + name);
123             mGlobalByName.remove(name);
124         } else {
125             // TODO: Verify this works, i.e., the caller happens to be from the correct user
126             if (DBG)
127                 Slog.i(TAG,
128                         "Removing from providersByName name=" + name + " user="
129                         + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
130             getProvidersByName(optionalUserId).remove(name);
131         }
132     }
133 
removeProviderByClass(ComponentName name, int optionalUserId)134     void removeProviderByClass(ComponentName name, int optionalUserId) {
135         if (mGlobalByClass.containsKey(name)) {
136             if (DBG)
137                 Slog.i(TAG, "Removing from globalByClass name=" + name);
138             mGlobalByClass.remove(name);
139         } else {
140             if (DBG)
141                 Slog.i(TAG,
142                         "Removing from providersByClass name=" + name + " user="
143                         + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
144             getProvidersByClass(optionalUserId).remove(name);
145         }
146     }
147 
getProvidersByName(int optionalUserId)148     private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
149         final int userId = optionalUserId >= 0
150                 ? optionalUserId : Binder.getOrigCallingUser();
151         final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
152         if (map == null) {
153             HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
154             mProvidersByNamePerUser.put(userId, newMap);
155             return newMap;
156         } else {
157             return map;
158         }
159     }
160 
getProvidersByClass(int optionalUserId)161     HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
162         final int userId = optionalUserId >= 0
163                 ? optionalUserId : Binder.getOrigCallingUser();
164         final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
165         if (map == null) {
166             HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
167             mProvidersByClassPerUser.put(userId, newMap);
168             return newMap;
169         } else {
170             return map;
171         }
172     }
173 
dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, HashMap<ComponentName, ContentProviderRecord> map)174     private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
175             HashMap<ComponentName, ContentProviderRecord> map) {
176         Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
177         while (it.hasNext()) {
178             Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
179             ContentProviderRecord r = e.getValue();
180             pw.print("  * ");
181             pw.println(r);
182             r.dump(pw, "    ", dumpAll);
183         }
184     }
185 
dumpProvidersByNameLocked(PrintWriter pw, HashMap<String, ContentProviderRecord> map)186     private void dumpProvidersByNameLocked(PrintWriter pw,
187             HashMap<String, ContentProviderRecord> map) {
188         Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
189         while (it.hasNext()) {
190             Map.Entry<String, ContentProviderRecord> e = it.next();
191             ContentProviderRecord r = e.getValue();
192             pw.print("  ");
193             pw.print(e.getKey());
194             pw.print(": ");
195             pw.println(r.toShortString());
196         }
197     }
198 
dumpProvidersLocked(PrintWriter pw, boolean dumpAll)199     void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
200         boolean needSep = false;
201         if (mGlobalByClass.size() > 0) {
202             if (needSep)
203                 pw.println(" ");
204             pw.println("  Published content providers (by class):");
205             dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
206         }
207 
208         if (mProvidersByClassPerUser.size() > 1) {
209             pw.println("");
210             for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
211                 HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
212                 pw.println("  User " + mProvidersByClassPerUser.keyAt(i) + ":");
213                 dumpProvidersByClassLocked(pw, dumpAll, map);
214                 pw.println(" ");
215             }
216         } else if (mProvidersByClassPerUser.size() == 1) {
217             HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0);
218             dumpProvidersByClassLocked(pw, dumpAll, map);
219         }
220         needSep = true;
221 
222         if (dumpAll) {
223             pw.println(" ");
224             pw.println("  Authority to provider mappings:");
225             dumpProvidersByNameLocked(pw, mGlobalByName);
226 
227             for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
228                 if (i > 0) {
229                     pw.println("  User " + mProvidersByNamePerUser.keyAt(i) + ":");
230                 }
231                 dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
232             }
233         }
234     }
235 
dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll)236     protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
237             int opti, boolean dumpAll) {
238         ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
239 
240         if ("all".equals(name)) {
241             synchronized (this) {
242                 for (ContentProviderRecord r1 : getProvidersByClass(-1).values()) {
243                     providers.add(r1);
244                 }
245             }
246         } else {
247             ComponentName componentName = name != null
248                     ? ComponentName.unflattenFromString(name) : null;
249             int objectId = 0;
250             if (componentName == null) {
251                 // Not a '/' separated full component name; maybe an object ID?
252                 try {
253                     objectId = Integer.parseInt(name, 16);
254                     name = null;
255                     componentName = null;
256                 } catch (RuntimeException e) {
257                 }
258             }
259 
260             synchronized (this) {
261                 for (ContentProviderRecord r1 : getProvidersByClass(-1).values()) {
262                     if (componentName != null) {
263                         if (r1.name.equals(componentName)) {
264                             providers.add(r1);
265                         }
266                     } else if (name != null) {
267                         if (r1.name.flattenToString().contains(name)) {
268                             providers.add(r1);
269                         }
270                     } else if (System.identityHashCode(r1) == objectId) {
271                         providers.add(r1);
272                     }
273                 }
274             }
275         }
276 
277         if (providers.size() <= 0) {
278             return false;
279         }
280 
281         boolean needSep = false;
282         for (int i=0; i<providers.size(); i++) {
283             if (needSep) {
284                 pw.println();
285             }
286             needSep = true;
287             dumpProvider("", fd, pw, providers.get(i), args, dumpAll);
288         }
289         return true;
290     }
291 
292     /**
293      * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider if
294      * there is a thread associated with the provider.
295      */
dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, String[] args, boolean dumpAll)296     private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
297             final ContentProviderRecord r, String[] args, boolean dumpAll) {
298         String innerPrefix = prefix + "  ";
299         synchronized (this) {
300             pw.print(prefix); pw.print("PROVIDER ");
301                     pw.print(r);
302                     pw.print(" pid=");
303                     if (r.proc != null) pw.println(r.proc.pid);
304                     else pw.println("(not running)");
305             if (dumpAll) {
306                 r.dump(pw, innerPrefix, true);
307             }
308         }
309         if (r.proc != null && r.proc.thread != null) {
310             pw.println("    Client:");
311             pw.flush();
312             try {
313                 TransferPipe tp = new TransferPipe();
314                 try {
315                     r.proc.thread.dumpProvider(
316                             tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args);
317                     tp.setBufferPrefix("      ");
318                     // Short timeout, since blocking here can
319                     // deadlock with the application.
320                     tp.go(fd, 2000);
321                 } finally {
322                     tp.kill();
323                 }
324             } catch (IOException ex) {
325                 pw.println("      Failure while dumping the provider: " + ex);
326             } catch (RemoteException ex) {
327                 pw.println("      Got a RemoteException while dumping the service");
328             }
329         }
330     }
331 
332 
333 }
334