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