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