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