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