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 
     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 
     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          */
     79         App(UUID uuid, T callback) {
     80             this.uuid = uuid;
     81             this.callback = callback;
     82         }
     83 
     84         /**
     85          * Link death recipient
     86          */
     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          */
    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 
    111         void queueCallback(CallbackInfo callbackInfo) {
    112             congestionQueue.add(callbackInfo);
    113         }
    114 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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