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