• 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 
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 
Connection(int connId, String address,int appId)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         /** Flag to signal that transport is congested */
71         Boolean isCongested = false;
72 
73         /** Internal callback info queue, waiting to be send on congestion clear */
74         private List<CallbackInfo> congestionQueue = new ArrayList<CallbackInfo>();
75 
76         /**
77          * Creates a new app context.
78          */
App(UUID uuid, T callback)79         App(UUID uuid, T callback) {
80             this.uuid = uuid;
81             this.callback = callback;
82         }
83 
84         /**
85          * Link death recipient
86          */
linkToDeath(IBinder.DeathRecipient deathRecipient)87         void linkToDeath(IBinder.DeathRecipient deathRecipient) {
88             try {
89                 IBinder binder = ((IInterface)callback).asBinder();
90                 binder.linkToDeath(deathRecipient, 0);
91                 mDeathRecipient = deathRecipient;
92             } catch (RemoteException e) {
93                 Log.e(TAG, "Unable to link deathRecipient for app id " + id);
94             }
95         }
96 
97         /**
98          * Unlink death recipient
99          */
unlinkToDeath()100         void unlinkToDeath() {
101             if (mDeathRecipient != null) {
102                 try {
103                     IBinder binder = ((IInterface)callback).asBinder();
104                     binder.unlinkToDeath(mDeathRecipient,0);
105                 } catch (NoSuchElementException e) {
106                     Log.e(TAG, "Unable to unlink deathRecipient for app id " + id);
107                 }
108             }
109         }
110 
queueCallback(CallbackInfo callbackInfo)111         void queueCallback(CallbackInfo callbackInfo) {
112             congestionQueue.add(callbackInfo);
113         }
114 
popQueuedCallback()115         CallbackInfo popQueuedCallback() {
116             if (congestionQueue.size() == 0) return null;
117             return congestionQueue.remove(0);
118         }
119     }
120 
121     /** Our internal application list */
122     List<App> mApps = new ArrayList<App>();
123 
124     /** Internal list of connected devices **/
125     Set<Connection> mConnections = new HashSet<Connection>();
126 
127     /**
128      * Add an entry to the application context list.
129      */
add(UUID uuid, T callback)130     void add(UUID uuid, T callback) {
131         synchronized (mApps) {
132             mApps.add(new App(uuid, callback));
133         }
134     }
135 
136     /**
137      * Remove the context for a given UUID
138      */
remove(UUID uuid)139     void remove(UUID uuid) {
140         synchronized (mApps) {
141             Iterator<App> i = mApps.iterator();
142             while(i.hasNext()) {
143                 App entry = i.next();
144                 if (entry.uuid.equals(uuid)) {
145                     entry.unlinkToDeath();
146                     i.remove();
147                     break;
148                 }
149             }
150         }
151     }
152 
153     /**
154      * Remove the context for a given application ID.
155      */
remove(int id)156     void remove(int id) {
157         synchronized (mApps) {
158             Iterator<App> i = mApps.iterator();
159             while(i.hasNext()) {
160                 App entry = i.next();
161                 if (entry.id == id) {
162                     entry.unlinkToDeath();
163                     i.remove();
164                     break;
165                 }
166             }
167         }
168     }
169 
170     /**
171      * Add a new connection for a given application ID.
172      */
addConnection(int id, int connId, String address)173     void addConnection(int id, int connId, String address) {
174         synchronized (mConnections) {
175             App entry = getById(id);
176             if (entry != null){
177                 mConnections.add(new Connection(connId, address, id));
178             }
179         }
180     }
181 
182     /**
183      * Remove a connection with the given ID.
184      */
removeConnection(int id, int connId)185     void removeConnection(int id, int connId) {
186         synchronized (mConnections) {
187             Iterator<Connection> i = mConnections.iterator();
188             while(i.hasNext()) {
189                 Connection connection = i.next();
190                 if (connection.connId == connId) {
191                     i.remove();
192                     break;
193                 }
194             }
195         }
196     }
197 
198     /**
199      * Get an application context by ID.
200      */
getById(int id)201     App getById(int id) {
202         Iterator<App> i = mApps.iterator();
203         while(i.hasNext()) {
204             App entry = i.next();
205             if (entry.id == id) return entry;
206         }
207         Log.e(TAG, "Context not found for ID " + id);
208         return null;
209     }
210 
211     /**
212      * Get an application context by UUID.
213      */
getByUuid(UUID uuid)214     App getByUuid(UUID uuid) {
215         Iterator<App> i = mApps.iterator();
216         while(i.hasNext()) {
217             App entry = i.next();
218             if (entry.uuid.equals(uuid)) return entry;
219         }
220         Log.e(TAG, "Context not found for UUID " + uuid);
221         return null;
222     }
223 
224     /**
225      * Get the device addresses for all connected devices
226      */
getConnectedDevices()227     Set<String> getConnectedDevices() {
228         Set<String> addresses = new HashSet<String>();
229         Iterator<Connection> i = mConnections.iterator();
230         while(i.hasNext()) {
231             Connection connection = i.next();
232             addresses.add(connection.address);
233         }
234         return addresses;
235     }
236 
237     /**
238      * Get an application context by a connection ID.
239      */
getByConnId(int connId)240     App getByConnId(int connId) {
241         Iterator<Connection> ii = mConnections.iterator();
242         while(ii.hasNext()) {
243             Connection connection = ii.next();
244             if (connection.connId == connId){
245                 return getById(connection.appId);
246             }
247         }
248         return null;
249     }
250 
251     /**
252      * Returns a connection ID for a given device address.
253      */
connIdByAddress(int id, String address)254     Integer connIdByAddress(int id, String address) {
255         App entry = getById(id);
256         if (entry == null) return null;
257 
258         Iterator<Connection> i = mConnections.iterator();
259         while(i.hasNext()) {
260             Connection connection = i.next();
261             if (connection.address.equals(address) && connection.appId == id)
262                 return connection.connId;
263         }
264         return null;
265     }
266 
267     /**
268      * Returns the device address for a given connection ID.
269      */
addressByConnId(int connId)270     String addressByConnId(int connId) {
271         Iterator<Connection> i = mConnections.iterator();
272         while(i.hasNext()) {
273             Connection connection = i.next();
274             if (connection.connId == connId) return connection.address;
275         }
276         return null;
277     }
278 
getConnectionByApp(int appId)279     List<Connection> getConnectionByApp(int appId) {
280         List<Connection> currentConnections = new ArrayList<Connection>();
281         Iterator<Connection> i = mConnections.iterator();
282         while(i.hasNext()) {
283             Connection connection = i.next();
284             if (connection.appId == appId)
285                 currentConnections.add(connection);
286         }
287         return currentConnections;
288     }
289 
290     /**
291      * Erases all application context entries.
292      */
clear()293     void clear() {
294         synchronized (mApps) {
295             Iterator<App> i = mApps.iterator();
296             while(i.hasNext()) {
297                 App entry = i.next();
298                 entry.unlinkToDeath();
299                 i.remove();
300             }
301         }
302 
303         synchronized (mConnections) {
304             mConnections.clear();
305         }
306     }
307 
308     /**
309      * Logs debug information.
310      */
dump(StringBuilder sb)311     void dump(StringBuilder sb) {
312         sb.append("  Entries: " + mApps.size() + "\n");
313 
314         Iterator<App> i = mApps.iterator();
315         while(i.hasNext()) {
316             App entry = i.next();
317             List<Connection> connections = getConnectionByApp(entry.id);
318 
319             sb.append("\n  Application Id: " + entry.id + "\n");
320             sb.append("  UUID: " + entry.uuid + "\n");
321             sb.append("  Connections: " + connections.size() + "\n");
322 
323             Iterator<Connection> ii = connections.iterator();
324             while(ii.hasNext()) {
325                 Connection connection = ii.next();
326                 sb.append("    " + connection.connId + ": " + connection.address + "\n");
327             }
328         }
329     }
330 }
331