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