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.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 
     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          */
     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          */
    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          */
    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 
    126         void queueCallback(CallbackInfo callbackInfo) {
    127             congestionQueue.add(callbackInfo);
    128         }
    129 
    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      */
    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      */
    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      */
    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                     entry.unlinkToDeath();
    194                     entry.appScanStats.isRegistered = false;
    195                     i.remove();
    196                     break;
    197                 }
    198             }
    199         }
    200     }
    201 
    202     /**
    203      * Add a new connection for a given application ID.
    204      */
    205     void addConnection(int id, int connId, String address) {
    206         synchronized (mConnections) {
    207             App entry = getById(id);
    208             if (entry != null){
    209                 mConnections.add(new Connection(connId, address, id));
    210             }
    211         }
    212     }
    213 
    214     /**
    215      * Remove a connection with the given ID.
    216      */
    217     void removeConnection(int id, int connId) {
    218         synchronized (mConnections) {
    219             Iterator<Connection> i = mConnections.iterator();
    220             while (i.hasNext()) {
    221                 Connection connection = i.next();
    222                 if (connection.connId == connId) {
    223                     i.remove();
    224                     break;
    225                 }
    226             }
    227         }
    228     }
    229 
    230     /**
    231      * Get an application context by ID.
    232      */
    233     App getById(int id) {
    234         Iterator<App> i = mApps.iterator();
    235         while (i.hasNext()) {
    236             App entry = i.next();
    237             if (entry.id == id) return entry;
    238         }
    239         Log.e(TAG, "Context not found for ID " + id);
    240         return null;
    241     }
    242 
    243     /**
    244      * Get an application context by UUID.
    245      */
    246     App getByUuid(UUID uuid) {
    247         Iterator<App> i = mApps.iterator();
    248         while (i.hasNext()) {
    249             App entry = i.next();
    250             if (entry.uuid.equals(uuid)) return entry;
    251         }
    252         Log.e(TAG, "Context not found for UUID " + uuid);
    253         return null;
    254     }
    255 
    256     /**
    257      * Get an application context by the calling Apps name.
    258      */
    259     App getByName(String name) {
    260         Iterator<App> i = mApps.iterator();
    261         while (i.hasNext()) {
    262             App entry = i.next();
    263             if (entry.name.equals(name)) return entry;
    264         }
    265         Log.e(TAG, "Context not found for name " + name);
    266         return null;
    267     }
    268 
    269     /**
    270      * Get Logging info by ID
    271      */
    272     AppScanStats getAppScanStatsById(int id) {
    273         App temp = getById(id);
    274         if (temp != null) {
    275             return temp.appScanStats;
    276         }
    277         return null;
    278     }
    279 
    280     /**
    281      * Get Logging info by application name
    282      */
    283     AppScanStats getAppScanStatsByName(String name) {
    284         return mAppScanStats.get(name);
    285     }
    286 
    287     /**
    288      * Get the device addresses for all connected devices
    289      */
    290     Set<String> getConnectedDevices() {
    291         Set<String> addresses = new HashSet<String>();
    292         Iterator<Connection> i = mConnections.iterator();
    293         while (i.hasNext()) {
    294             Connection connection = i.next();
    295             addresses.add(connection.address);
    296         }
    297         return addresses;
    298     }
    299 
    300     /**
    301      * Get an application context by a connection ID.
    302      */
    303     App getByConnId(int connId) {
    304         Iterator<Connection> ii = mConnections.iterator();
    305         while (ii.hasNext()) {
    306             Connection connection = ii.next();
    307             if (connection.connId == connId){
    308                 return getById(connection.appId);
    309             }
    310         }
    311         return null;
    312     }
    313 
    314     /**
    315      * Returns a connection ID for a given device address.
    316      */
    317     Integer connIdByAddress(int id, String address) {
    318         App entry = getById(id);
    319         if (entry == null) return null;
    320 
    321         Iterator<Connection> i = mConnections.iterator();
    322         while (i.hasNext()) {
    323             Connection connection = i.next();
    324             if (connection.address.equals(address) && connection.appId == id)
    325                 return connection.connId;
    326         }
    327         return null;
    328     }
    329 
    330     /**
    331      * Returns the device address for a given connection ID.
    332      */
    333     String addressByConnId(int connId) {
    334         Iterator<Connection> i = mConnections.iterator();
    335         while (i.hasNext()) {
    336             Connection connection = i.next();
    337             if (connection.connId == connId) return connection.address;
    338         }
    339         return null;
    340     }
    341 
    342     List<Connection> getConnectionByApp(int appId) {
    343         List<Connection> currentConnections = new ArrayList<Connection>();
    344         Iterator<Connection> i = mConnections.iterator();
    345         while (i.hasNext()) {
    346             Connection connection = i.next();
    347             if (connection.appId == appId)
    348                 currentConnections.add(connection);
    349         }
    350         return currentConnections;
    351     }
    352 
    353     /**
    354      * Erases all application context entries.
    355      */
    356     void clear() {
    357         synchronized (mApps) {
    358             Iterator<App> i = mApps.iterator();
    359             while (i.hasNext()) {
    360                 App entry = i.next();
    361                 entry.unlinkToDeath();
    362                 entry.appScanStats.isRegistered = false;
    363                 i.remove();
    364             }
    365         }
    366 
    367         synchronized (mConnections) {
    368             mConnections.clear();
    369         }
    370     }
    371 
    372     /**
    373      * Returns connect device map with addr and appid
    374      */
    375     Map<Integer, String> getConnectedMap(){
    376         Map<Integer, String> connectedmap = new HashMap<Integer, String>();
    377         for(Connection conn: mConnections){
    378             connectedmap.put(conn.appId, conn.address);
    379         }
    380         return connectedmap;
    381     }
    382 
    383     /**
    384      * Logs debug information.
    385      */
    386     void dump(StringBuilder sb) {
    387         sb.append("  Entries: " + mAppScanStats.size() + "\n\n");
    388 
    389         Iterator<Map.Entry<String, AppScanStats>> it = mAppScanStats.entrySet().iterator();
    390         while (it.hasNext()) {
    391             Map.Entry<String, AppScanStats> entry = it.next();
    392 
    393             String name = entry.getKey();
    394             AppScanStats appScanStats = entry.getValue();
    395             appScanStats.dumpToString(sb);
    396         }
    397     }
    398 }
    399