• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import java.util.HashMap;
31 import 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 
Connection(int connId, String address,int appId)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          */
App(UUID uuid, T callback)81         App(UUID uuid, T callback) {
82             this.uuid = uuid;
83             this.callback = callback;
84         }
85 
86         /**
87          * Link death recipient
88          */
linkToDeath(IBinder.DeathRecipient deathRecipient)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          */
unlinkToDeath()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 
queueCallback(CallbackInfo callbackInfo)113         void queueCallback(CallbackInfo callbackInfo) {
114             congestionQueue.add(callbackInfo);
115         }
116 
popQueuedCallback()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      */
add(UUID uuid, T callback)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      */
remove(UUID uuid)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      */
remove(int id)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      */
addConnection(int id, int connId, String address)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      */
removeConnection(int id, int connId)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      */
getById(int id)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      */
getByUuid(UUID uuid)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      */
getConnectedDevices()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      */
getByConnId(int connId)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      */
connIdByAddress(int id, String address)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      */
addressByConnId(int connId)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 
getConnectionByApp(int appId)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      */
clear()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      */
getConnectedMap()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      */
dump(StringBuilder sb)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