ContextMap.java revision f19f1ac64a5fefb248ab15b918d009b926c99dde
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.IBinder; 19import android.os.IBinder.DeathRecipient; 20import android.os.IInterface; 21import android.os.RemoteException; 22import android.util.Log; 23import java.util.ArrayList; 24import java.util.HashSet; 25import java.util.Iterator; 26import java.util.List; 27import java.util.NoSuchElementException; 28import java.util.Set; 29import java.util.UUID; 30import java.util.HashMap; 31import java.util.Map; 32 33/** 34 * Helper class that keeps track of registered GATT applications. 35 * This class manages application callbacks and keeps track of GATT connections. 36 * @hide 37 */ 38/*package*/ class ContextMap<T> { 39 private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; 40 41 /** 42 * Connection class helps map connection IDs to device addresses. 43 */ 44 class Connection { 45 int connId; 46 String address; 47 int appId; 48 49 Connection(int connId, String address,int appId) { 50 this.connId = connId; 51 this.address = address; 52 this.appId = appId; 53 } 54 } 55 56 /** 57 * Application entry mapping UUIDs to appIDs and callbacks. 58 */ 59 class App { 60 /** The UUID of the application */ 61 UUID uuid; 62 63 /** The id of the application */ 64 int id; 65 66 /** Application callbacks */ 67 T callback; 68 69 /** Death receipient */ 70 private IBinder.DeathRecipient mDeathRecipient; 71 72 /** Flag to signal that transport is congested */ 73 Boolean isCongested = false; 74 75 /** Internal callback info queue, waiting to be send on congestion clear */ 76 private List<CallbackInfo> congestionQueue = new ArrayList<CallbackInfo>(); 77 78 /** 79 * Creates a new app context. 80 */ 81 App(UUID uuid, T callback) { 82 this.uuid = uuid; 83 this.callback = callback; 84 } 85 86 /** 87 * Link death recipient 88 */ 89 void linkToDeath(IBinder.DeathRecipient deathRecipient) { 90 try { 91 IBinder binder = ((IInterface)callback).asBinder(); 92 binder.linkToDeath(deathRecipient, 0); 93 mDeathRecipient = deathRecipient; 94 } catch (RemoteException e) { 95 Log.e(TAG, "Unable to link deathRecipient for app id " + id); 96 } 97 } 98 99 /** 100 * Unlink death recipient 101 */ 102 void unlinkToDeath() { 103 if (mDeathRecipient != null) { 104 try { 105 IBinder binder = ((IInterface)callback).asBinder(); 106 binder.unlinkToDeath(mDeathRecipient,0); 107 } catch (NoSuchElementException e) { 108 Log.e(TAG, "Unable to unlink deathRecipient for app id " + id); 109 } 110 } 111 } 112 113 void queueCallback(CallbackInfo callbackInfo) { 114 congestionQueue.add(callbackInfo); 115 } 116 117 CallbackInfo popQueuedCallback() { 118 if (congestionQueue.size() == 0) return null; 119 return congestionQueue.remove(0); 120 } 121 } 122 123 /** Our internal application list */ 124 List<App> mApps = new ArrayList<App>(); 125 126 /** Internal list of connected devices **/ 127 Set<Connection> mConnections = new HashSet<Connection>(); 128 129 /** 130 * Add an entry to the application context list. 131 */ 132 void add(UUID uuid, T callback) { 133 synchronized (mApps) { 134 mApps.add(new App(uuid, callback)); 135 } 136 } 137 138 /** 139 * Remove the context for a given UUID 140 */ 141 void remove(UUID uuid) { 142 synchronized (mApps) { 143 Iterator<App> i = mApps.iterator(); 144 while(i.hasNext()) { 145 App entry = i.next(); 146 if (entry.uuid.equals(uuid)) { 147 entry.unlinkToDeath(); 148 i.remove(); 149 break; 150 } 151 } 152 } 153 } 154 155 /** 156 * Remove the context for a given application ID. 157 */ 158 void remove(int id) { 159 synchronized (mApps) { 160 Iterator<App> i = mApps.iterator(); 161 while(i.hasNext()) { 162 App entry = i.next(); 163 if (entry.id == id) { 164 entry.unlinkToDeath(); 165 i.remove(); 166 break; 167 } 168 } 169 } 170 } 171 172 /** 173 * Add a new connection for a given application ID. 174 */ 175 void addConnection(int id, int connId, String address) { 176 synchronized (mConnections) { 177 App entry = getById(id); 178 if (entry != null){ 179 mConnections.add(new Connection(connId, address, id)); 180 } 181 } 182 } 183 184 /** 185 * Remove a connection with the given ID. 186 */ 187 void removeConnection(int id, int connId) { 188 synchronized (mConnections) { 189 Iterator<Connection> i = mConnections.iterator(); 190 while(i.hasNext()) { 191 Connection connection = i.next(); 192 if (connection.connId == connId) { 193 i.remove(); 194 break; 195 } 196 } 197 } 198 } 199 200 /** 201 * Get an application context by ID. 202 */ 203 App getById(int id) { 204 Iterator<App> i = mApps.iterator(); 205 while(i.hasNext()) { 206 App entry = i.next(); 207 if (entry.id == id) return entry; 208 } 209 Log.e(TAG, "Context not found for ID " + id); 210 return null; 211 } 212 213 /** 214 * Get an application context by UUID. 215 */ 216 App getByUuid(UUID uuid) { 217 Iterator<App> i = mApps.iterator(); 218 while(i.hasNext()) { 219 App entry = i.next(); 220 if (entry.uuid.equals(uuid)) return entry; 221 } 222 Log.e(TAG, "Context not found for UUID " + uuid); 223 return null; 224 } 225 226 /** 227 * Get the device addresses for all connected devices 228 */ 229 Set<String> getConnectedDevices() { 230 Set<String> addresses = new HashSet<String>(); 231 Iterator<Connection> i = mConnections.iterator(); 232 while(i.hasNext()) { 233 Connection connection = i.next(); 234 addresses.add(connection.address); 235 } 236 return addresses; 237 } 238 239 /** 240 * Get an application context by a connection ID. 241 */ 242 App getByConnId(int connId) { 243 Iterator<Connection> ii = mConnections.iterator(); 244 while(ii.hasNext()) { 245 Connection connection = ii.next(); 246 if (connection.connId == connId){ 247 return getById(connection.appId); 248 } 249 } 250 return null; 251 } 252 253 /** 254 * Returns a connection ID for a given device address. 255 */ 256 Integer connIdByAddress(int id, String address) { 257 App entry = getById(id); 258 if (entry == null) return null; 259 260 Iterator<Connection> i = mConnections.iterator(); 261 while(i.hasNext()) { 262 Connection connection = i.next(); 263 if (connection.address.equals(address) && connection.appId == id) 264 return connection.connId; 265 } 266 return null; 267 } 268 269 /** 270 * Returns the device address for a given connection ID. 271 */ 272 String addressByConnId(int connId) { 273 Iterator<Connection> i = mConnections.iterator(); 274 while(i.hasNext()) { 275 Connection connection = i.next(); 276 if (connection.connId == connId) return connection.address; 277 } 278 return null; 279 } 280 281 List<Connection> getConnectionByApp(int appId) { 282 List<Connection> currentConnections = new ArrayList<Connection>(); 283 Iterator<Connection> i = mConnections.iterator(); 284 while(i.hasNext()) { 285 Connection connection = i.next(); 286 if (connection.appId == appId) 287 currentConnections.add(connection); 288 } 289 return currentConnections; 290 } 291 292 /** 293 * Erases all application context entries. 294 */ 295 void clear() { 296 synchronized (mApps) { 297 Iterator<App> i = mApps.iterator(); 298 while(i.hasNext()) { 299 App entry = i.next(); 300 entry.unlinkToDeath(); 301 i.remove(); 302 } 303 } 304 305 synchronized (mConnections) { 306 mConnections.clear(); 307 } 308 } 309 310 /** 311 * Returns connect device map with addr and appid 312 */ 313 Map<Integer, String> getConnectedMap(){ 314 Map<Integer, String> connectedmap = new HashMap<Integer, String>(); 315 for(Connection conn: mConnections){ 316 connectedmap.put(conn.appId, conn.address); 317 } 318 return connectedmap; 319 } 320 321 /** 322 * Logs debug information. 323 */ 324 void dump(StringBuilder sb) { 325 sb.append(" Entries: " + mApps.size() + "\n"); 326 327 Iterator<App> i = mApps.iterator(); 328 while(i.hasNext()) { 329 App entry = i.next(); 330 List<Connection> connections = getConnectionByApp(entry.id); 331 332 sb.append("\n Application Id: " + entry.id + "\n"); 333 sb.append(" UUID: " + entry.uuid + "\n"); 334 sb.append(" Connections: " + connections.size() + "\n"); 335 336 Iterator<Connection> ii = connections.iterator(); 337 while(ii.hasNext()) { 338 Connection connection = ii.next(); 339 sb.append(" " + connection.connId + ": " + connection.address + "\n"); 340 } 341 } 342 } 343} 344