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.Process; 22 import android.os.RemoteException; 23 import android.os.UserId; 24 import android.util.Slog; 25 import android.util.SparseArray; 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.Map.Entry; 35 import java.util.Set; 36 37 /** 38 * Keeps track of content providers by authority (name) and class. It separates the mapping by 39 * user and ones that are not user-specific (system providers). 40 */ 41 public class ProviderMap { 42 43 private static final String TAG = "ProviderMap"; 44 45 private static final boolean DBG = false; 46 47 private final HashMap<String, ContentProviderRecord> mGlobalByName 48 = new HashMap<String, ContentProviderRecord>(); 49 private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass 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 getProviderByName(String name)57 ContentProviderRecord getProviderByName(String name) { 58 return getProviderByName(name, -1); 59 } 60 getProviderByName(String name, int userId)61 ContentProviderRecord getProviderByName(String name, int userId) { 62 if (DBG) { 63 Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()); 64 } 65 // Try to find it in the global list 66 ContentProviderRecord record = mGlobalByName.get(name); 67 if (record != null) { 68 return record; 69 } 70 71 // Check the current user's list 72 return getProvidersByName(userId).get(name); 73 } 74 getProviderByClass(ComponentName name)75 ContentProviderRecord getProviderByClass(ComponentName name) { 76 return getProviderByClass(name, -1); 77 } 78 getProviderByClass(ComponentName name, int userId)79 ContentProviderRecord getProviderByClass(ComponentName name, int userId) { 80 if (DBG) { 81 Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid()); 82 } 83 // Try to find it in the global list 84 ContentProviderRecord record = mGlobalByClass.get(name); 85 if (record != null) { 86 return record; 87 } 88 89 // Check the current user's list 90 return getProvidersByClass(userId).get(name); 91 } 92 putProviderByName(String name, ContentProviderRecord record)93 void putProviderByName(String name, ContentProviderRecord record) { 94 if (DBG) { 95 Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid() 96 + ", record uid = " + record.appInfo.uid); 97 } 98 if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) { 99 mGlobalByName.put(name, record); 100 } else { 101 final int userId = UserId.getUserId(record.appInfo.uid); 102 getProvidersByName(userId).put(name, record); 103 } 104 } 105 putProviderByClass(ComponentName name, ContentProviderRecord record)106 void putProviderByClass(ComponentName name, ContentProviderRecord record) { 107 if (DBG) { 108 Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid() 109 + ", record uid = " + record.appInfo.uid); 110 } 111 if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) { 112 mGlobalByClass.put(name, record); 113 } else { 114 final int userId = UserId.getUserId(record.appInfo.uid); 115 getProvidersByClass(userId).put(name, record); 116 } 117 } 118 removeProviderByName(String name, int optionalUserId)119 void removeProviderByName(String name, int optionalUserId) { 120 if (mGlobalByName.containsKey(name)) { 121 if (DBG) 122 Slog.i(TAG, "Removing from globalByName name=" + name); 123 mGlobalByName.remove(name); 124 } else { 125 // TODO: Verify this works, i.e., the caller happens to be from the correct user 126 if (DBG) 127 Slog.i(TAG, 128 "Removing from providersByName name=" + name + " user=" 129 + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId)); 130 getProvidersByName(optionalUserId).remove(name); 131 } 132 } 133 removeProviderByClass(ComponentName name, int optionalUserId)134 void removeProviderByClass(ComponentName name, int optionalUserId) { 135 if (mGlobalByClass.containsKey(name)) { 136 if (DBG) 137 Slog.i(TAG, "Removing from globalByClass name=" + name); 138 mGlobalByClass.remove(name); 139 } else { 140 if (DBG) 141 Slog.i(TAG, 142 "Removing from providersByClass name=" + name + " user=" 143 + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId)); 144 getProvidersByClass(optionalUserId).remove(name); 145 } 146 } 147 getProvidersByName(int optionalUserId)148 private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) { 149 final int userId = optionalUserId >= 0 150 ? optionalUserId : Binder.getOrigCallingUser(); 151 final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId); 152 if (map == null) { 153 HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>(); 154 mProvidersByNamePerUser.put(userId, newMap); 155 return newMap; 156 } else { 157 return map; 158 } 159 } 160 getProvidersByClass(int optionalUserId)161 HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) { 162 final int userId = optionalUserId >= 0 163 ? optionalUserId : Binder.getOrigCallingUser(); 164 final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId); 165 if (map == null) { 166 HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>(); 167 mProvidersByClassPerUser.put(userId, newMap); 168 return newMap; 169 } else { 170 return map; 171 } 172 } 173 dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, HashMap<ComponentName, ContentProviderRecord> map)174 private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll, 175 HashMap<ComponentName, ContentProviderRecord> map) { 176 Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator(); 177 while (it.hasNext()) { 178 Map.Entry<ComponentName, ContentProviderRecord> e = it.next(); 179 ContentProviderRecord r = e.getValue(); 180 pw.print(" * "); 181 pw.println(r); 182 r.dump(pw, " ", dumpAll); 183 } 184 } 185 dumpProvidersByNameLocked(PrintWriter pw, HashMap<String, ContentProviderRecord> map)186 private void dumpProvidersByNameLocked(PrintWriter pw, 187 HashMap<String, ContentProviderRecord> map) { 188 Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator(); 189 while (it.hasNext()) { 190 Map.Entry<String, ContentProviderRecord> e = it.next(); 191 ContentProviderRecord r = e.getValue(); 192 pw.print(" "); 193 pw.print(e.getKey()); 194 pw.print(": "); 195 pw.println(r.toShortString()); 196 } 197 } 198 dumpProvidersLocked(PrintWriter pw, boolean dumpAll)199 void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) { 200 boolean needSep = false; 201 if (mGlobalByClass.size() > 0) { 202 if (needSep) 203 pw.println(" "); 204 pw.println(" Published content providers (by class):"); 205 dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass); 206 } 207 208 if (mProvidersByClassPerUser.size() > 1) { 209 pw.println(""); 210 for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { 211 HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i); 212 pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":"); 213 dumpProvidersByClassLocked(pw, dumpAll, map); 214 pw.println(" "); 215 } 216 } else if (mProvidersByClassPerUser.size() == 1) { 217 HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0); 218 dumpProvidersByClassLocked(pw, dumpAll, map); 219 } 220 needSep = true; 221 222 if (dumpAll) { 223 pw.println(" "); 224 pw.println(" Authority to provider mappings:"); 225 dumpProvidersByNameLocked(pw, mGlobalByName); 226 227 for (int i = 0; i < mProvidersByNamePerUser.size(); i++) { 228 if (i > 0) { 229 pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + ":"); 230 } 231 dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i)); 232 } 233 } 234 } 235 dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll)236 protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args, 237 int opti, boolean dumpAll) { 238 ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>(); 239 240 if ("all".equals(name)) { 241 synchronized (this) { 242 for (ContentProviderRecord r1 : getProvidersByClass(-1).values()) { 243 providers.add(r1); 244 } 245 } 246 } else { 247 ComponentName componentName = name != null 248 ? ComponentName.unflattenFromString(name) : null; 249 int objectId = 0; 250 if (componentName == null) { 251 // Not a '/' separated full component name; maybe an object ID? 252 try { 253 objectId = Integer.parseInt(name, 16); 254 name = null; 255 componentName = null; 256 } catch (RuntimeException e) { 257 } 258 } 259 260 synchronized (this) { 261 for (ContentProviderRecord r1 : getProvidersByClass(-1).values()) { 262 if (componentName != null) { 263 if (r1.name.equals(componentName)) { 264 providers.add(r1); 265 } 266 } else if (name != null) { 267 if (r1.name.flattenToString().contains(name)) { 268 providers.add(r1); 269 } 270 } else if (System.identityHashCode(r1) == objectId) { 271 providers.add(r1); 272 } 273 } 274 } 275 } 276 277 if (providers.size() <= 0) { 278 return false; 279 } 280 281 boolean needSep = false; 282 for (int i=0; i<providers.size(); i++) { 283 if (needSep) { 284 pw.println(); 285 } 286 needSep = true; 287 dumpProvider("", fd, pw, providers.get(i), args, dumpAll); 288 } 289 return true; 290 } 291 292 /** 293 * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider if 294 * there is a thread associated with the provider. 295 */ dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, final ContentProviderRecord r, String[] args, boolean dumpAll)296 private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw, 297 final ContentProviderRecord r, String[] args, boolean dumpAll) { 298 String innerPrefix = prefix + " "; 299 synchronized (this) { 300 pw.print(prefix); pw.print("PROVIDER "); 301 pw.print(r); 302 pw.print(" pid="); 303 if (r.proc != null) pw.println(r.proc.pid); 304 else pw.println("(not running)"); 305 if (dumpAll) { 306 r.dump(pw, innerPrefix, true); 307 } 308 } 309 if (r.proc != null && r.proc.thread != null) { 310 pw.println(" Client:"); 311 pw.flush(); 312 try { 313 TransferPipe tp = new TransferPipe(); 314 try { 315 r.proc.thread.dumpProvider( 316 tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args); 317 tp.setBufferPrefix(" "); 318 // Short timeout, since blocking here can 319 // deadlock with the application. 320 tp.go(fd, 2000); 321 } finally { 322 tp.kill(); 323 } 324 } catch (IOException ex) { 325 pw.println(" Failure while dumping the provider: " + ex); 326 } catch (RemoteException ex) { 327 pw.println(" Got a RemoteException while dumping the service"); 328 } 329 } 330 } 331 332 333 } 334