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