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         /**
     71          * Creates a new app context.
     72          */
     73         App(UUID uuid, T callback) {
     74             this.uuid = uuid;
     75             this.callback = callback;
     76         }
     77 
     78         /**
     79          * Link death recipient
     80          */
     81         void linkToDeath(IBinder.DeathRecipient deathRecipient) {
     82             try {
     83                 IBinder binder = ((IInterface)callback).asBinder();
     84                 binder.linkToDeath(deathRecipient, 0);
     85                 mDeathRecipient = deathRecipient;
     86             } catch (RemoteException e) {
     87                 Log.e(TAG, "Unable to link deathRecipient for app id " + id);
     88             }
     89         }
     90 
     91         /**
     92          * Unlink death recipient
     93          */
     94         void unlinkToDeath() {
     95             if (mDeathRecipient != null) {
     96                 try {
     97                     IBinder binder = ((IInterface)callback).asBinder();
     98                     binder.unlinkToDeath(mDeathRecipient,0);
     99                 } catch (NoSuchElementException e) {
    100                     Log.e(TAG, "Unable to unlink deathRecipient for app id " + id);
    101                 }
    102             }
    103         }
    104     }
    105 
    106     /** Our internal application list */
    107     List<App> mApps = new ArrayList<App>();
    108 
    109     /** Internal list of connected devices **/
    110     Set<Connection> mConnections = new HashSet<Connection>();
    111 
    112     /**
    113      * Add an entry to the application context list.
    114      */
    115     void add(UUID uuid, T callback) {
    116         synchronized (mApps) {
    117             mApps.add(new App(uuid, callback));
    118         }
    119     }
    120 
    121     /**
    122      * Remove the context for a given application ID.
    123      */
    124     void remove(int id) {
    125         synchronized (mApps) {
    126             Iterator<App> i = mApps.iterator();
    127             while(i.hasNext()) {
    128                 App entry = i.next();
    129                 if (entry.id == id) {
    130                     entry.unlinkToDeath();
    131                     i.remove();
    132                     break;
    133                 }
    134             }
    135         }
    136     }
    137 
    138     /**
    139      * Add a new connection for a given application ID.
    140      */
    141     void addConnection(int id, int connId, String address) {
    142         synchronized (mConnections) {
    143             App entry = getById(id);
    144             if (entry != null){
    145                 mConnections.add(new Connection(connId, address, id));
    146             }
    147         }
    148     }
    149 
    150     /**
    151      * Remove a connection with the given ID.
    152      */
    153     void removeConnection(int id, int connId) {
    154         synchronized (mConnections) {
    155             Iterator<Connection> i = mConnections.iterator();
    156             while(i.hasNext()) {
    157                 Connection connection = i.next();
    158                 if (connection.connId == connId) {
    159                     i.remove();
    160                     break;
    161                 }
    162             }
    163         }
    164     }
    165 
    166     /**
    167      * Get an application context by ID.
    168      */
    169     App getById(int id) {
    170         Iterator<App> i = mApps.iterator();
    171         while(i.hasNext()) {
    172             App entry = i.next();
    173             if (entry.id == id) return entry;
    174         }
    175         Log.e(TAG, "Context not found for ID " + id);
    176         return null;
    177     }
    178 
    179     /**
    180      * Get an application context by UUID.
    181      */
    182     App getByUuid(UUID uuid) {
    183         Iterator<App> i = mApps.iterator();
    184         while(i.hasNext()) {
    185             App entry = i.next();
    186             if (entry.uuid.equals(uuid)) return entry;
    187         }
    188         Log.e(TAG, "Context not found for UUID " + uuid);
    189         return null;
    190     }
    191 
    192     /**
    193      * Get the device addresses for all connected devices
    194      */
    195     Set<String> getConnectedDevices() {
    196         Set<String> addresses = new HashSet<String>();
    197         Iterator<Connection> i = mConnections.iterator();
    198         while(i.hasNext()) {
    199             Connection connection = i.next();
    200             addresses.add(connection.address);
    201         }
    202         return addresses;
    203     }
    204 
    205     /**
    206      * Get an application context by a connection ID.
    207      */
    208     App getByConnId(int connId) {
    209         Iterator<Connection> ii = mConnections.iterator();
    210         while(ii.hasNext()) {
    211             Connection connection = ii.next();
    212             if (connection.connId == connId){
    213                 return getById(connection.appId);
    214             }
    215         }
    216         return null;
    217     }
    218 
    219     /**
    220      * Returns a connection ID for a given device address.
    221      */
    222     Integer connIdByAddress(int id, String address) {
    223         App entry = getById(id);
    224         if (entry == null) return null;
    225 
    226         Iterator<Connection> i = mConnections.iterator();
    227         while(i.hasNext()) {
    228             Connection connection = i.next();
    229             if (connection.address.equals(address) && connection.appId == id)
    230                 return connection.connId;
    231         }
    232         return null;
    233     }
    234 
    235     /**
    236      * Returns the device address for a given connection ID.
    237      */
    238     String addressByConnId(int connId) {
    239         Iterator<Connection> i = mConnections.iterator();
    240         while(i.hasNext()) {
    241             Connection connection = i.next();
    242             if (connection.connId == connId) return connection.address;
    243         }
    244         return null;
    245     }
    246 
    247     List<Connection> getConnectionByApp(int appId) {
    248         List<Connection> currentConnections = new ArrayList<Connection>();
    249         Iterator<Connection> i = mConnections.iterator();
    250         while(i.hasNext()) {
    251             Connection connection = i.next();
    252             if (connection.appId == appId)
    253                 currentConnections.add(connection);
    254         }
    255         return currentConnections;
    256     }
    257 
    258     /**
    259      * Erases all application context entries.
    260      */
    261     void clear() {
    262         synchronized (mApps) {
    263             Iterator<App> i = mApps.iterator();
    264             while(i.hasNext()) {
    265                 App entry = i.next();
    266                 entry.unlinkToDeath();
    267                 i.remove();
    268             }
    269         }
    270 
    271         synchronized (mConnections) {
    272             mConnections.clear();
    273         }
    274     }
    275 
    276     /**
    277      * Logs debug information.
    278      */
    279     void dump() {
    280         StringBuilder b = new StringBuilder();
    281         b.append(  "-------------- GATT Context Map ----------------");
    282         b.append("\nEntries: " + mApps.size());
    283 
    284         Iterator<App> i = mApps.iterator();
    285         while(i.hasNext()) {
    286             App entry = i.next();
    287             List<Connection> connections = getConnectionByApp(entry.id);
    288 
    289             b.append("\n\nApplication Id: " + entry.id);
    290             b.append("\nUUID: " + entry.uuid);
    291             b.append("\nConnections: " + connections.size());
    292 
    293             Iterator<Connection> ii = connections.iterator();
    294             while(ii.hasNext()) {
    295                 Connection connection = ii.next();
    296                 b.append("\n  " + connection.connId + ": " + connection.address);
    297             }
    298         }
    299 
    300         b.append("\n------------------------------------------------");
    301         Log.d(TAG, b.toString());
    302     }
    303 }
    304