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.RemoteException; 22 import android.os.UserHandle; 23 import android.util.Slog; 24 import android.util.SparseArray; 25 import com.android.internal.os.TransferPipe; 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.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 */ 40 public 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 ProviderMap(ActivityManagerService am)58 ProviderMap(ActivityManagerService am) { 59 mAm = am; 60 } 61 getProviderByName(String name)62 ContentProviderRecord getProviderByName(String name) { 63 return getProviderByName(name, -1); 64 } 65 getProviderByName(String name, int userId)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 getProviderByClass(ComponentName name)80 ContentProviderRecord getProviderByClass(ComponentName name) { 81 return getProviderByClass(name, -1); 82 } 83 getProviderByClass(ComponentName name, int userId)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 putProviderByName(String name, ContentProviderRecord record)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 putProviderByClass(ComponentName name, ContentProviderRecord record)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 removeProviderByName(String name, int userId)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 removeProviderByClass(ComponentName name, int userId)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 getProvidersByName(int userId)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 getProvidersByClass(int userId)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 collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, HashMap<ComponentName, ContentProviderRecord> providers, ArrayList<ContentProviderRecord> result)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 collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, int userId, ArrayList<ContentProviderRecord> result)210 boolean collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, 211 boolean doit, boolean evenPersistent, int userId, 212 ArrayList<ContentProviderRecord> result) { 213 boolean didSomething = false; 214 if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_OWNER) { 215 didSomething = collectPackageProvidersLocked(packageName, filterByClasses, 216 doit, evenPersistent, mSingletonByClass, result); 217 } 218 if (!doit && didSomething) { 219 return true; 220 } 221 if (userId == UserHandle.USER_ALL) { 222 for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { 223 if (collectPackageProvidersLocked(packageName, filterByClasses, 224 doit, evenPersistent, mProvidersByClassPerUser.valueAt(i), result)) { 225 if (!doit) { 226 return true; 227 } 228 didSomething = true; 229 } 230 } 231 } else { 232 HashMap<ComponentName, ContentProviderRecord> items 233 = getProvidersByClass(userId); 234 if (items != null) { 235 didSomething |= collectPackageProvidersLocked(packageName, filterByClasses, 236 doit, evenPersistent, items, result); 237 } 238 } 239 return didSomething; 240 } 241 dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, String dumpPackage, String header, boolean needSep, HashMap<ComponentName, ContentProviderRecord> map)242 private boolean dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, String dumpPackage, 243 String header, boolean needSep, HashMap<ComponentName, ContentProviderRecord> map) { 244 Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator(); 245 boolean written = false; 246 while (it.hasNext()) { 247 Map.Entry<ComponentName, ContentProviderRecord> e = it.next(); 248 ContentProviderRecord r = e.getValue(); 249 if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { 250 continue; 251 } 252 if (needSep) { 253 pw.println(""); 254 needSep = false; 255 } 256 if (header != null) { 257 pw.println(header); 258 header = null; 259 } 260 written = true; 261 pw.print(" * "); 262 pw.println(r); 263 r.dump(pw, " ", dumpAll); 264 } 265 return written; 266 } 267 dumpProvidersByNameLocked(PrintWriter pw, String dumpPackage, String header, boolean needSep, HashMap<String, ContentProviderRecord> map)268 private boolean dumpProvidersByNameLocked(PrintWriter pw, String dumpPackage, 269 String header, boolean needSep, HashMap<String, ContentProviderRecord> map) { 270 Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator(); 271 boolean written = false; 272 while (it.hasNext()) { 273 Map.Entry<String, ContentProviderRecord> e = it.next(); 274 ContentProviderRecord r = e.getValue(); 275 if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { 276 continue; 277 } 278 if (needSep) { 279 pw.println(""); 280 needSep = false; 281 } 282 if (header != null) { 283 pw.println(header); 284 header = null; 285 } 286 written = true; 287 pw.print(" "); 288 pw.print(e.getKey()); 289 pw.print(": "); 290 pw.println(r.toShortString()); 291 } 292 return written; 293 } 294 dumpProvidersLocked(PrintWriter pw, boolean dumpAll, String dumpPackage)295 boolean dumpProvidersLocked(PrintWriter pw, boolean dumpAll, String dumpPackage) { 296 boolean needSep = false; 297 298 if (mSingletonByClass.size() > 0) { 299 needSep |= dumpProvidersByClassLocked(pw, dumpAll, dumpPackage, 300 " Published single-user content providers (by class):", needSep, 301 mSingletonByClass); 302 } 303 304 for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { 305 HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i); 306 needSep |= dumpProvidersByClassLocked(pw, dumpAll, dumpPackage, 307 " Published user " + mProvidersByClassPerUser.keyAt(i) 308 + " content providers (by class):", needSep, map); 309 } 310 311 if (dumpAll) { 312 needSep |= dumpProvidersByNameLocked(pw, dumpPackage, 313 " Single-user authority to provider mappings:", needSep, mSingletonByName); 314 315 for (int i = 0; i < mProvidersByNamePerUser.size(); i++) { 316 needSep |= dumpProvidersByNameLocked(pw, dumpPackage, 317 " User " + mProvidersByNamePerUser.keyAt(i) 318 + " authority to provider mappings:", needSep, 319 mProvidersByNamePerUser.valueAt(i)); 320 } 321 } 322 return needSep; 323 } 324 dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll)325 protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, 326 int opti, boolean dumpAll) { 327 ArrayList<ContentProviderRecord> allProviders = new ArrayList<ContentProviderRecord>(); 328 ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>(); 329 330 synchronized (mAm) { 331 allProviders.addAll(mSingletonByClass.values()); 332 for (int i=0; i<mProvidersByClassPerUser.size(); i++) { 333 allProviders.addAll(mProvidersByClassPerUser.valueAt(i).values()); 334 } 335 336 if ("all".equals(name)) { 337 providers.addAll(allProviders); 338 } else { 339 ComponentName componentName = name != null 340 ? ComponentName.unflattenFromString(name) : null; 341 int objectId = 0; 342 if (componentName == null) { 343 // Not a '/' separated full component name; maybe an object ID? 344 try { 345 objectId = Integer.parseInt(name, 16); 346 name = null; 347 componentName = null; 348 } catch (RuntimeException e) { 349 } 350 } 351 352 for (int i=0; i<allProviders.size(); i++) { 353 ContentProviderRecord r1 = allProviders.get(i); 354 if (componentName != null) { 355 if (r1.name.equals(componentName)) { 356 providers.add(r1); 357 } 358 } else if (name != null) { 359 if (r1.name.flattenToString().contains(name)) { 360 providers.add(r1); 361 } 362 } else if (System.identityHashCode(r1) == objectId) { 363 providers.add(r1); 364 } 365 } 366 } 367 } 368 369 if (providers.size() <= 0) { 370 return false; 371 } 372 373 boolean needSep = false; 374 for (int i=0; i<providers.size(); i++) { 375 if (needSep) { 376 pw.println(); 377 } 378 needSep = true; 379 dumpProvider("", fd, pw, providers.get(i), args, dumpAll); 380 } 381 return true; 382 } 383 384 /** 385 * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider if 386 * there is a thread associated with the provider. 387 */ dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, String[] args, boolean dumpAll)388 private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, 389 final ContentProviderRecord r, String[] args, boolean dumpAll) { 390 String innerPrefix = prefix + " "; 391 synchronized (mAm) { 392 pw.print(prefix); pw.print("PROVIDER "); 393 pw.print(r); 394 pw.print(" pid="); 395 if (r.proc != null) pw.println(r.proc.pid); 396 else pw.println("(not running)"); 397 if (dumpAll) { 398 r.dump(pw, innerPrefix, true); 399 } 400 } 401 if (r.proc != null && r.proc.thread != null) { 402 pw.println(" Client:"); 403 pw.flush(); 404 try { 405 TransferPipe tp = new TransferPipe(); 406 try { 407 r.proc.thread.dumpProvider( 408 tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args); 409 tp.setBufferPrefix(" "); 410 // Short timeout, since blocking here can 411 // deadlock with the application. 412 tp.go(fd, 2000); 413 } finally { 414 tp.kill(); 415 } 416 } catch (IOException ex) { 417 pw.println(" Failure while dumping the provider: " + ex); 418 } catch (RemoteException ex) { 419 pw.println(" Got a RemoteException while dumping the service"); 420 } 421 } 422 } 423 } 424