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