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