Home | History | Annotate | Download | only in gatt
      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 
     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