ContextMap.java revision 7a068c2a0861e12a3916cec313cf105395871999
1/* 2 * Copyright (C) 2013 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 */ 16package com.android.bluetooth.gatt; 17 18import android.content.Context; 19import android.os.Binder; 20import android.os.IBinder; 21import android.os.IBinder.DeathRecipient; 22import android.os.IInterface; 23import android.os.RemoteException; 24import android.util.Log; 25import java.util.ArrayList; 26import java.util.HashSet; 27import java.util.Iterator; 28import java.util.List; 29import java.util.NoSuchElementException; 30import java.util.Set; 31import java.util.UUID; 32import java.util.HashMap; 33import java.util.Map; 34 35import com.android.bluetooth.btservice.BluetoothProto; 36/** 37 * Helper class that keeps track of registered GATT applications. 38 * This class manages application callbacks and keeps track of GATT connections. 39 * @hide 40 */ 41/*package*/ class ContextMap<T> { 42 private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; 43 static final int NUM_SCAN_EVENTS_KEPT = 20; 44 45 /** 46 * Connection class helps map connection IDs to device addresses. 47 */ 48 class Connection { 49 int connId; 50 String address; 51 int appId; 52 long startTime; 53 54 Connection(int connId, String address,int appId) { 55 this.connId = connId; 56 this.address = address; 57 this.appId = appId; 58 this.startTime = System.currentTimeMillis(); 59 } 60 } 61 62 /** 63 * Application entry mapping UUIDs to appIDs and callbacks. 64 */ 65 class App { 66 /** The UUID of the application */ 67 UUID uuid; 68 69 /** The id of the application */ 70 int id; 71 72 /** The package name of the application */ 73 String name; 74 75 /** Application callbacks */ 76 T callback; 77 78 /** Death receipient */ 79 private IBinder.DeathRecipient mDeathRecipient; 80 81 /** Flag to signal that transport is congested */ 82 Boolean isCongested = false; 83 84 /** Internal callback info queue, waiting to be send on congestion clear */ 85 private List<CallbackInfo> congestionQueue = new ArrayList<CallbackInfo>(); 86 87 /** 88 * Creates a new app context. 89 */ 90 App(UUID uuid, T callback, String name) { 91 this.uuid = uuid; 92 this.callback = callback; 93 this.name = name; 94 } 95 96 /** 97 * Link death recipient 98 */ 99 void linkToDeath(IBinder.DeathRecipient deathRecipient) { 100 try { 101 IBinder binder = ((IInterface)callback).asBinder(); 102 binder.linkToDeath(deathRecipient, 0); 103 mDeathRecipient = deathRecipient; 104 } catch (RemoteException e) { 105 Log.e(TAG, "Unable to link deathRecipient for app id " + id); 106 } 107 } 108 109 /** 110 * Unlink death recipient 111 */ 112 void unlinkToDeath() { 113 if (mDeathRecipient != null) { 114 try { 115 IBinder binder = ((IInterface)callback).asBinder(); 116 binder.unlinkToDeath(mDeathRecipient,0); 117 } catch (NoSuchElementException e) { 118 Log.e(TAG, "Unable to unlink deathRecipient for app id " + id); 119 } 120 } 121 } 122 123 void queueCallback(CallbackInfo callbackInfo) { 124 congestionQueue.add(callbackInfo); 125 } 126 127 CallbackInfo popQueuedCallback() { 128 if (congestionQueue.size() == 0) return null; 129 return congestionQueue.remove(0); 130 } 131 } 132 133 /** Our internal application list */ 134 List<App> mApps = new ArrayList<App>(); 135 136 /** Internal map to keep track of logging information by app name */ 137 HashMap<String, AppScanStats> mAppScanStats = new HashMap<String, AppScanStats>(); 138 139 /** Internal list of scan events to use with the proto */ 140 ArrayList<BluetoothProto.ScanEvent> mScanEvents = 141 new ArrayList<BluetoothProto.ScanEvent>(NUM_SCAN_EVENTS_KEPT); 142 143 /** Internal list of connected devices **/ 144 Set<Connection> mConnections = new HashSet<Connection>(); 145 146 /** 147 * Add an entry to the application context list. 148 */ 149 void add(UUID uuid, T callback, Context context) { 150 String appName = context.getPackageManager().getNameForUid( 151 Binder.getCallingUid()); 152 if (appName == null) { 153 // Assign an app name if one isn't found 154 appName = "Unknown App (UID: " + Binder.getCallingUid() + ")"; 155 } 156 synchronized (mApps) { 157 mApps.add(new App(uuid, callback, appName)); 158 AppScanStats appScanStats = mAppScanStats.get(appName); 159 if (appScanStats == null) { 160 appScanStats = new AppScanStats(appName, this); 161 mAppScanStats.put(appName, appScanStats); 162 } 163 appScanStats.isRegistered = true; 164 } 165 } 166 167 /** 168 * Remove the context for a given UUID 169 */ 170 void remove(UUID uuid) { 171 synchronized (mApps) { 172 Iterator<App> i = mApps.iterator(); 173 while (i.hasNext()) { 174 App entry = i.next(); 175 if (entry.uuid.equals(uuid)) { 176 entry.unlinkToDeath(); 177 mAppScanStats.get(entry.name).isRegistered = false; 178 i.remove(); 179 break; 180 } 181 } 182 } 183 } 184 185 /** 186 * Remove the context for a given application ID. 187 */ 188 void remove(int id) { 189 synchronized (mApps) { 190 Iterator<App> i = mApps.iterator(); 191 while (i.hasNext()) { 192 App entry = i.next(); 193 if (entry.id == id) { 194 entry.unlinkToDeath(); 195 mAppScanStats.get(entry.name).isRegistered = false; 196 i.remove(); 197 break; 198 } 199 } 200 } 201 } 202 203 /** 204 * Add a new connection for a given application ID. 205 */ 206 void addConnection(int id, int connId, String address) { 207 synchronized (mConnections) { 208 App entry = getById(id); 209 if (entry != null){ 210 mConnections.add(new Connection(connId, address, id)); 211 } 212 } 213 } 214 215 /** 216 * Remove a connection with the given ID. 217 */ 218 void removeConnection(int id, int connId) { 219 synchronized (mConnections) { 220 Iterator<Connection> i = mConnections.iterator(); 221 while (i.hasNext()) { 222 Connection connection = i.next(); 223 if (connection.connId == connId) { 224 i.remove(); 225 break; 226 } 227 } 228 } 229 } 230 231 /** 232 * Get an application context by ID. 233 */ 234 App getById(int id) { 235 Iterator<App> i = mApps.iterator(); 236 while (i.hasNext()) { 237 App entry = i.next(); 238 if (entry.id == id) return entry; 239 } 240 Log.e(TAG, "Context not found for ID " + id); 241 return null; 242 } 243 244 /** 245 * Get an application context by UUID. 246 */ 247 App getByUuid(UUID uuid) { 248 Iterator<App> i = mApps.iterator(); 249 while (i.hasNext()) { 250 App entry = i.next(); 251 if (entry.uuid.equals(uuid)) return entry; 252 } 253 Log.e(TAG, "Context not found for UUID " + uuid); 254 return null; 255 } 256 257 /** 258 * Get an application context by the calling Apps name. 259 */ 260 App getByName(String name) { 261 Iterator<App> i = mApps.iterator(); 262 while (i.hasNext()) { 263 App entry = i.next(); 264 if (entry.name.equals(name)) return entry; 265 } 266 Log.e(TAG, "Context not found for name " + name); 267 return null; 268 } 269 270 /** 271 * Get Logging info by ID 272 */ 273 AppScanStats getAppScanStatsById(int id) { 274 App temp = getById(id); 275 if (temp != null) { 276 return mAppScanStats.get(temp.name); 277 } 278 return null; 279 } 280 281 /** 282 * Get Logging info by application name 283 */ 284 AppScanStats getAppScanStatsByName(String name) { 285 return mAppScanStats.get(name); 286 } 287 288 /** 289 * Get the device addresses for all connected devices 290 */ 291 Set<String> getConnectedDevices() { 292 Set<String> addresses = new HashSet<String>(); 293 Iterator<Connection> i = mConnections.iterator(); 294 while (i.hasNext()) { 295 Connection connection = i.next(); 296 addresses.add(connection.address); 297 } 298 return addresses; 299 } 300 301 /** 302 * Get an application context by a connection ID. 303 */ 304 App getByConnId(int connId) { 305 Iterator<Connection> ii = mConnections.iterator(); 306 while (ii.hasNext()) { 307 Connection connection = ii.next(); 308 if (connection.connId == connId){ 309 return getById(connection.appId); 310 } 311 } 312 return null; 313 } 314 315 /** 316 * Returns a connection ID for a given device address. 317 */ 318 Integer connIdByAddress(int id, String address) { 319 App entry = getById(id); 320 if (entry == null) return null; 321 322 Iterator<Connection> i = mConnections.iterator(); 323 while (i.hasNext()) { 324 Connection connection = i.next(); 325 if (connection.address.equals(address) && connection.appId == id) 326 return connection.connId; 327 } 328 return null; 329 } 330 331 /** 332 * Returns the device address for a given connection ID. 333 */ 334 String addressByConnId(int connId) { 335 Iterator<Connection> i = mConnections.iterator(); 336 while (i.hasNext()) { 337 Connection connection = i.next(); 338 if (connection.connId == connId) return connection.address; 339 } 340 return null; 341 } 342 343 List<Connection> getConnectionByApp(int appId) { 344 List<Connection> currentConnections = new ArrayList<Connection>(); 345 Iterator<Connection> i = mConnections.iterator(); 346 while (i.hasNext()) { 347 Connection connection = i.next(); 348 if (connection.appId == appId) 349 currentConnections.add(connection); 350 } 351 return currentConnections; 352 } 353 354 /** 355 * Erases all application context entries. 356 */ 357 void clear() { 358 synchronized (mApps) { 359 Iterator<App> i = mApps.iterator(); 360 while (i.hasNext()) { 361 App entry = i.next(); 362 entry.unlinkToDeath(); 363 i.remove(); 364 } 365 } 366 367 synchronized (mConnections) { 368 mConnections.clear(); 369 } 370 } 371 372 /** 373 * Returns connect device map with addr and appid 374 */ 375 Map<Integer, String> getConnectedMap(){ 376 Map<Integer, String> connectedmap = new HashMap<Integer, String>(); 377 for(Connection conn: mConnections){ 378 connectedmap.put(conn.appId, conn.address); 379 } 380 return connectedmap; 381 } 382 383 void addScanEvent(BluetoothProto.ScanEvent event) { 384 synchronized(mScanEvents) { 385 if (mScanEvents.size() == NUM_SCAN_EVENTS_KEPT) 386 mScanEvents.remove(0); 387 mScanEvents.add(event); 388 } 389 } 390 391 /** 392 * Logs debug information. 393 */ 394 void dump(StringBuilder sb) { 395 sb.append(" Entries: " + mAppScanStats.size() + "\n\n"); 396 397 Iterator<Map.Entry<String, AppScanStats>> it = mAppScanStats.entrySet().iterator(); 398 while (it.hasNext()) { 399 Map.Entry<String, AppScanStats> entry = it.next(); 400 401 String name = entry.getKey(); 402 AppScanStats appScanStats = entry.getValue(); 403 appScanStats.dumpToString(sb); 404 } 405 } 406 407 void dumpProto(BluetoothProto.BluetoothLog proto) { 408 synchronized(mScanEvents) { 409 for (BluetoothProto.ScanEvent event : mScanEvents) { 410 proto.addScanEvent(event); 411 } 412 } 413 } 414} 415