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