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