Home | History | Annotate | Download | only in hdp
      1 /*
      2  * Copyright (C) 2012 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 
     17 package com.android.bluetooth.hdp;
     18 
     19 import android.bluetooth.BluetoothDevice;
     20 import android.bluetooth.BluetoothHealth;
     21 import android.bluetooth.BluetoothHealthAppConfiguration;
     22 import android.bluetooth.BluetoothProfile;
     23 import android.bluetooth.IBluetooth;
     24 import android.bluetooth.IBluetoothHealth;
     25 import android.bluetooth.IBluetoothHealthCallback;
     26 import android.content.Intent;
     27 import android.content.pm.PackageManager;
     28 import android.os.Handler;
     29 import android.os.HandlerThread;
     30 import android.os.IBinder;
     31 import android.os.IBinder.DeathRecipient;
     32 import android.os.Looper;
     33 import android.os.Message;
     34 import android.os.ParcelFileDescriptor;
     35 import android.os.RemoteException;
     36 import android.os.ServiceManager;
     37 import android.util.Log;
     38 import com.android.bluetooth.btservice.ProfileService;
     39 import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
     40 import com.android.bluetooth.Utils;
     41 import java.io.FileDescriptor;
     42 import java.io.IOException;
     43 import java.util.ArrayList;
     44 import java.util.Collections;
     45 import java.util.HashMap;
     46 import java.util.Iterator;
     47 import java.util.List;
     48 import java.util.Map;
     49 import java.util.Map.Entry;
     50 import java.util.NoSuchElementException;
     51 
     52 
     53 /**
     54  * Provides Bluetooth Health Device profile, as a service in
     55  * the Bluetooth application.
     56  * @hide
     57  */
     58 public class HealthService extends ProfileService {
     59     private static final boolean DBG = true;
     60     private static final boolean VDBG = false;
     61     private static final String TAG="HealthService";
     62 
     63     private List<HealthChannel> mHealthChannels;
     64     private Map <BluetoothHealthAppConfiguration, AppInfo> mApps;
     65     private Map <BluetoothDevice, Integer> mHealthDevices;
     66     private boolean mNativeAvailable;
     67     private HealthServiceMessageHandler mHandler;
     68     private static final int MESSAGE_REGISTER_APPLICATION = 1;
     69     private static final int MESSAGE_UNREGISTER_APPLICATION = 2;
     70     private static final int MESSAGE_CONNECT_CHANNEL = 3;
     71     private static final int MESSAGE_DISCONNECT_CHANNEL = 4;
     72     private static final int MESSAGE_APP_REGISTRATION_CALLBACK = 11;
     73     private static final int MESSAGE_CHANNEL_STATE_CALLBACK = 12;
     74 
     75     static {
     76         classInitNative();
     77     }
     78 
     79     protected String getName() {
     80         return TAG;
     81     }
     82 
     83     protected IProfileServiceBinder initBinder() {
     84         return new BluetoothHealthBinder(this);
     85     }
     86 
     87     protected boolean start() {
     88         mHealthChannels = Collections.synchronizedList(new ArrayList<HealthChannel>());
     89         mApps = Collections.synchronizedMap(new HashMap<BluetoothHealthAppConfiguration,
     90                                             AppInfo>());
     91         mHealthDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
     92 
     93         HandlerThread thread = new HandlerThread("BluetoothHdpHandler");
     94         thread.start();
     95         Looper looper = thread.getLooper();
     96         mHandler = new HealthServiceMessageHandler(looper);
     97         initializeNative();
     98         mNativeAvailable=true;
     99         return true;
    100     }
    101 
    102     protected boolean stop() {
    103         mHandler.removeCallbacksAndMessages(null);
    104         Looper looper = mHandler.getLooper();
    105         if (looper != null) {
    106             looper.quit();
    107         }
    108         cleanupApps();
    109         return true;
    110     }
    111 
    112     private void cleanupApps(){
    113         Iterator <Map.Entry<BluetoothHealthAppConfiguration,AppInfo>>it
    114                     = mApps.entrySet().iterator();
    115         while (it.hasNext())
    116         {
    117            Map.Entry<BluetoothHealthAppConfiguration,AppInfo> entry   = it.next();
    118            AppInfo appInfo = entry.getValue();
    119            if (appInfo != null)
    120                appInfo.cleanup();
    121            it.remove();
    122         }
    123     }
    124     protected boolean cleanup() {
    125         mHandler = null;
    126         //Cleanup native
    127         if (mNativeAvailable) {
    128             cleanupNative();
    129             mNativeAvailable=false;
    130         }
    131         if(mHealthChannels != null) {
    132             mHealthChannels.clear();
    133         }
    134         if(mHealthDevices != null) {
    135             mHealthDevices.clear();
    136         }
    137         if(mApps != null) {
    138             mApps.clear();
    139         }
    140         return true;
    141     }
    142 
    143     private final class HealthServiceMessageHandler extends Handler {
    144         private HealthServiceMessageHandler(Looper looper) {
    145             super(looper);
    146         }
    147 
    148         @Override
    149         public void handleMessage(Message msg) {
    150             if (DBG) log("HealthService Handler msg: " + msg.what);
    151             switch (msg.what) {
    152                 case MESSAGE_REGISTER_APPLICATION:
    153                 {
    154                     BluetoothHealthAppConfiguration appConfig =
    155                         (BluetoothHealthAppConfiguration) msg.obj;
    156                     AppInfo appInfo = mApps.get(appConfig);
    157                     if (appInfo == null) break;
    158                     int halRole = convertRoleToHal(appConfig.getRole());
    159                     int halChannelType = convertChannelTypeToHal(appConfig.getChannelType());
    160                     if (VDBG) log("register datatype: " + appConfig.getDataType() + " role: " +
    161                                  halRole + " name: " + appConfig.getName() + " channeltype: " +
    162                                  halChannelType);
    163                     int appId = registerHealthAppNative(appConfig.getDataType(), halRole,
    164                                                         appConfig.getName(), halChannelType);
    165                     if (appId == -1) {
    166                         callStatusCallback(appConfig,
    167                                            BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE);
    168                         appInfo.cleanup();
    169                         mApps.remove(appConfig);
    170                     } else {
    171                         //link to death with a recipient object to implement binderDead()
    172                         appInfo.mRcpObj = new BluetoothHealthDeathRecipient(HealthService.this,appConfig);
    173                         IBinder binder = appInfo.mCallback.asBinder();
    174                         try {
    175                             binder.linkToDeath(appInfo.mRcpObj,0);
    176                         } catch (RemoteException e) {
    177                             Log.e(TAG,"LinktoDeath Exception:"+e);
    178                         }
    179                         appInfo.mAppId = appId;
    180                         callStatusCallback(appConfig,
    181                                            BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS);
    182                     }
    183                 }
    184                     break;
    185                 case MESSAGE_UNREGISTER_APPLICATION:
    186                 {
    187                     BluetoothHealthAppConfiguration appConfig =
    188                         (BluetoothHealthAppConfiguration) msg.obj;
    189                     int appId = (mApps.get(appConfig)).mAppId;
    190                     if (!unregisterHealthAppNative(appId)) {
    191                         Log.e(TAG, "Failed to unregister application: id: " + appId);
    192                         callStatusCallback(appConfig,
    193                                            BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE);
    194                     }
    195                 }
    196                     break;
    197                 case MESSAGE_CONNECT_CHANNEL:
    198                 {
    199                     HealthChannel chan = (HealthChannel) msg.obj;
    200                     byte[] devAddr = Utils.getByteAddress(chan.mDevice);
    201                     int appId = (mApps.get(chan.mConfig)).mAppId;
    202                     chan.mChannelId = connectChannelNative(devAddr, appId);
    203                     if (chan.mChannelId == -1) {
    204                         callHealthChannelCallback(chan.mConfig, chan.mDevice,
    205                                                   BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
    206                                                   BluetoothHealth.STATE_CHANNEL_DISCONNECTED,
    207                                                   chan.mChannelFd, chan.mChannelId);
    208                         callHealthChannelCallback(chan.mConfig, chan.mDevice,
    209                                                   BluetoothHealth.STATE_CHANNEL_DISCONNECTED,
    210                                                   BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
    211                                                   chan.mChannelFd, chan.mChannelId);
    212                     }
    213                 }
    214                     break;
    215                 case MESSAGE_DISCONNECT_CHANNEL:
    216                 {
    217                     HealthChannel chan = (HealthChannel) msg.obj;
    218                     if (!disconnectChannelNative(chan.mChannelId)) {
    219                         callHealthChannelCallback(chan.mConfig, chan.mDevice,
    220                                                   BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
    221                                                   BluetoothHealth.STATE_CHANNEL_CONNECTED,
    222                                                   chan.mChannelFd, chan.mChannelId);
    223                         callHealthChannelCallback(chan.mConfig, chan.mDevice,
    224                                                   BluetoothHealth.STATE_CHANNEL_CONNECTED,
    225                                                   BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
    226                                                   chan.mChannelFd, chan.mChannelId);
    227                     }
    228                 }
    229                     break;
    230                 case MESSAGE_APP_REGISTRATION_CALLBACK:
    231                 {
    232                     BluetoothHealthAppConfiguration appConfig = findAppConfigByAppId(msg.arg1);
    233                     if (appConfig == null) break;
    234 
    235                     int regStatus = convertHalRegStatus(msg.arg2);
    236                     callStatusCallback(appConfig, regStatus);
    237                     if (regStatus == BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE ||
    238                         regStatus == BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS) {
    239                         //unlink to death once app is unregistered
    240                         AppInfo appInfo = mApps.get(appConfig);
    241                         appInfo.cleanup();
    242                         mApps.remove(appConfig);
    243                     }
    244                 }
    245                     break;
    246                 case MESSAGE_CHANNEL_STATE_CALLBACK:
    247                 {
    248                     ChannelStateEvent channelStateEvent = (ChannelStateEvent) msg.obj;
    249                     HealthChannel chan = findChannelById(channelStateEvent.mChannelId);
    250                     BluetoothHealthAppConfiguration appConfig =
    251                             findAppConfigByAppId(channelStateEvent.mAppId);
    252                     int newState;
    253                     newState = convertHalChannelState(channelStateEvent.mState);
    254                     if (newState  ==  BluetoothHealth.STATE_CHANNEL_DISCONNECTED &&
    255                         appConfig == null) {
    256                         Log.e(TAG,"Disconnected for non existing app");
    257                         break;
    258                     }
    259                     if (chan == null) {
    260                         // incoming connection
    261 
    262                         BluetoothDevice device = getDevice(channelStateEvent.mAddr);
    263                         chan = new HealthChannel(device, appConfig, appConfig.getChannelType());
    264                         chan.mChannelId = channelStateEvent.mChannelId;
    265                         mHealthChannels.add(chan);
    266                     }
    267                     newState = convertHalChannelState(channelStateEvent.mState);
    268                     if (newState == BluetoothHealth.STATE_CHANNEL_CONNECTED) {
    269                         try {
    270                             chan.mChannelFd = ParcelFileDescriptor.dup(channelStateEvent.mFd);
    271                         } catch (IOException e) {
    272                             Log.e(TAG, "failed to dup ParcelFileDescriptor");
    273                             break;
    274                         }
    275                     }
    276                     /*set the channel fd to null if channel state isnot equal to connected*/
    277                     else{
    278                         chan.mChannelFd = null;
    279                     }
    280                     callHealthChannelCallback(chan.mConfig, chan.mDevice, newState,
    281                                               chan.mState, chan.mChannelFd, chan.mChannelId);
    282                     chan.mState = newState;
    283                     if (channelStateEvent.mState == CONN_STATE_DESTROYED) {
    284                         mHealthChannels.remove(chan);
    285                     }
    286                 }
    287                     break;
    288             }
    289         }
    290     }
    291 
    292 //Handler for DeathReceipient
    293     private static class BluetoothHealthDeathRecipient implements IBinder.DeathRecipient{
    294         private BluetoothHealthAppConfiguration mConfig;
    295         private HealthService mService;
    296 
    297         public BluetoothHealthDeathRecipient(HealthService service, BluetoothHealthAppConfiguration config) {
    298             mService = service;
    299             mConfig = config;
    300         }
    301 
    302         public void binderDied() {
    303             if (DBG) Log.d(TAG,"Binder is dead.");
    304             mService.unregisterAppConfiguration(mConfig);
    305         }
    306 
    307         public void cleanup(){
    308             mService = null;
    309             mConfig = null;
    310         }
    311     }
    312 
    313     /**
    314      * Handlers for incoming service calls
    315      */
    316     private static class BluetoothHealthBinder extends IBluetoothHealth.Stub implements IProfileServiceBinder {
    317         private HealthService mService;
    318 
    319         public BluetoothHealthBinder(HealthService svc) {
    320             mService = svc;
    321         }
    322 
    323         public boolean cleanup()  {
    324             mService = null;
    325             return true;
    326         }
    327 
    328         private HealthService getService() {
    329             if (!Utils.checkCaller()) {
    330                 Log.w(TAG,"Health call not allowed for non-active user");
    331                 return null;
    332             }
    333 
    334             if (mService  != null && mService.isAvailable()) {
    335                 return mService;
    336             }
    337             return null;
    338         }
    339 
    340         public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
    341                                                 IBluetoothHealthCallback callback) {
    342             HealthService service = getService();
    343             if (service == null) return false;
    344             return service.registerAppConfiguration(config, callback);
    345         }
    346 
    347         public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
    348             HealthService service = getService();
    349             if (service == null) return false;
    350             return service.unregisterAppConfiguration(config);
    351         }
    352 
    353         public boolean connectChannelToSource(BluetoothDevice device,
    354                                               BluetoothHealthAppConfiguration config) {
    355             HealthService service = getService();
    356             if (service == null) return false;
    357             return service.connectChannelToSource(device, config);
    358         }
    359 
    360         public boolean connectChannelToSink(BluetoothDevice device,
    361                            BluetoothHealthAppConfiguration config, int channelType) {
    362             HealthService service = getService();
    363             if (service == null) return false;
    364             return service.connectChannelToSink(device, config, channelType);
    365         }
    366 
    367         public boolean disconnectChannel(BluetoothDevice device,
    368                                          BluetoothHealthAppConfiguration config, int channelId) {
    369             HealthService service = getService();
    370             if (service == null) return false;
    371             return service.disconnectChannel(device, config, channelId);
    372         }
    373 
    374         public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
    375                                                      BluetoothHealthAppConfiguration config) {
    376             HealthService service = getService();
    377             if (service == null) return null;
    378             return service.getMainChannelFd(device, config);
    379         }
    380 
    381         public int getHealthDeviceConnectionState(BluetoothDevice device) {
    382             HealthService service = getService();
    383             if (service == null) return BluetoothHealth.STATE_DISCONNECTED;
    384             return service.getHealthDeviceConnectionState(device);
    385         }
    386 
    387         public List<BluetoothDevice> getConnectedHealthDevices() {
    388             HealthService service = getService();
    389             if (service == null) return new ArrayList<BluetoothDevice> (0);
    390             return service.getConnectedHealthDevices();
    391         }
    392 
    393         public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(int[] states) {
    394             HealthService service = getService();
    395             if (service == null) return new ArrayList<BluetoothDevice> (0);
    396             return service.getHealthDevicesMatchingConnectionStates(states);
    397         }
    398     };
    399 
    400     boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
    401             IBluetoothHealthCallback callback) {
    402         enforceCallingOrSelfPermission(BLUETOOTH_PERM,
    403                 "Need BLUETOOTH permission");
    404         if (mApps.get(config) != null) {
    405             if (DBG) Log.d(TAG, "Config has already been registered");
    406             return false;
    407         }
    408         mApps.put(config, new AppInfo(callback));
    409         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION,config);
    410         mHandler.sendMessage(msg);
    411         return true;
    412     }
    413 
    414     boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
    415         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    416         if (mApps.get(config) == null) {
    417             if (DBG) Log.d(TAG,"unregisterAppConfiguration: no app found");
    418             return false;
    419         }
    420         Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_APPLICATION,config);
    421         mHandler.sendMessage(msg);
    422         return true;
    423     }
    424 
    425     boolean connectChannelToSource(BluetoothDevice device,
    426                                           BluetoothHealthAppConfiguration config) {
    427         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    428         return connectChannel(device, config, BluetoothHealth.CHANNEL_TYPE_ANY);
    429     }
    430 
    431     boolean connectChannelToSink(BluetoothDevice device,
    432                        BluetoothHealthAppConfiguration config, int channelType) {
    433         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    434         return connectChannel(device, config, channelType);
    435     }
    436 
    437     boolean disconnectChannel(BluetoothDevice device,
    438                                      BluetoothHealthAppConfiguration config, int channelId) {
    439         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    440         HealthChannel chan = findChannelById(channelId);
    441         if (chan == null) {
    442             if (DBG) Log.d(TAG,"disconnectChannel: no channel found");
    443             return false;
    444         }
    445         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT_CHANNEL,chan);
    446         mHandler.sendMessage(msg);
    447         return true;
    448     }
    449 
    450     ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
    451                                                  BluetoothHealthAppConfiguration config) {
    452         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    453         HealthChannel healthChan = null;
    454         for (HealthChannel chan: mHealthChannels) {
    455             if (chan.mDevice.equals(device) && chan.mConfig.equals(config)) {
    456                 healthChan = chan;
    457             }
    458         }
    459         if (healthChan == null) {
    460             Log.e(TAG, "No channel found for device: " + device + " config: " + config);
    461             return null;
    462         }
    463         return healthChan.mChannelFd;
    464     }
    465 
    466     int getHealthDeviceConnectionState(BluetoothDevice device) {
    467         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    468         return getConnectionState(device);
    469     }
    470 
    471     List<BluetoothDevice> getConnectedHealthDevices() {
    472         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    473         List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates(
    474                 new int[] {BluetoothHealth.STATE_CONNECTED});
    475         return devices;
    476     }
    477 
    478     List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(int[] states) {
    479         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    480         List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates(states);
    481         return devices;
    482     }
    483 
    484     private void onAppRegistrationState(int appId, int state) {
    485         Message msg = mHandler.obtainMessage(MESSAGE_APP_REGISTRATION_CALLBACK);
    486         msg.arg1 = appId;
    487         msg.arg2 = state;
    488         mHandler.sendMessage(msg);
    489     }
    490 
    491     private void onChannelStateChanged(int appId, byte[] addr, int cfgIndex,
    492                                        int channelId, int state, FileDescriptor pfd) {
    493         Message msg = mHandler.obtainMessage(MESSAGE_CHANNEL_STATE_CALLBACK);
    494         ChannelStateEvent channelStateEvent = new ChannelStateEvent(appId, addr, cfgIndex,
    495                                                                     channelId, state, pfd);
    496         msg.obj = channelStateEvent;
    497         mHandler.sendMessage(msg);
    498     }
    499 
    500     private String getStringChannelType(int type) {
    501         if (type == BluetoothHealth.CHANNEL_TYPE_RELIABLE) {
    502             return "Reliable";
    503         } else if (type == BluetoothHealth.CHANNEL_TYPE_STREAMING) {
    504             return "Streaming";
    505         } else {
    506             return "Any";
    507         }
    508     }
    509 
    510     private void callStatusCallback(BluetoothHealthAppConfiguration config, int status) {
    511         if (VDBG) log ("Health Device Application: " + config + " State Change: status:" + status);
    512         IBluetoothHealthCallback callback = (mApps.get(config)).mCallback;
    513         if (callback == null) {
    514             Log.e(TAG, "Callback object null");
    515         }
    516 
    517         try {
    518             callback.onHealthAppConfigurationStatusChange(config, status);
    519         } catch (RemoteException e) {
    520             Log.e(TAG, "Remote Exception:" + e);
    521         }
    522     }
    523 
    524     private BluetoothHealthAppConfiguration findAppConfigByAppId(int appId) {
    525         BluetoothHealthAppConfiguration appConfig = null;
    526         for (Entry<BluetoothHealthAppConfiguration, AppInfo> e : mApps.entrySet()) {
    527             if (appId == (e.getValue()).mAppId) {
    528                 appConfig = e.getKey();
    529                 break;
    530             }
    531         }
    532         if (appConfig == null) {
    533             Log.e(TAG, "No appConfig found for " + appId);
    534         }
    535         return appConfig;
    536     }
    537 
    538     private int convertHalRegStatus(int halRegStatus) {
    539         switch (halRegStatus) {
    540             case APP_REG_STATE_REG_SUCCESS:
    541                 return BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS;
    542             case APP_REG_STATE_REG_FAILED:
    543                 return BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE;
    544             case APP_REG_STATE_DEREG_SUCCESS:
    545                 return BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS;
    546             case APP_REG_STATE_DEREG_FAILED:
    547                 return BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE;
    548         }
    549         Log.e(TAG, "Unexpected App Registration state: " + halRegStatus);
    550         return BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE;
    551     }
    552 
    553     private int convertHalChannelState(int halChannelState) {
    554         switch (halChannelState) {
    555             case CONN_STATE_CONNECTED:
    556                 return BluetoothHealth.STATE_CHANNEL_CONNECTED;
    557             case CONN_STATE_CONNECTING:
    558                 return BluetoothHealth.STATE_CHANNEL_CONNECTING;
    559             case CONN_STATE_DISCONNECTING:
    560                 return BluetoothHealth.STATE_CHANNEL_DISCONNECTING;
    561             case CONN_STATE_DISCONNECTED:
    562                 return BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
    563             case CONN_STATE_DESTROYED:
    564                 // TODO(BT) add BluetoothHealth.STATE_CHANNEL_DESTROYED;
    565                 return BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
    566             default:
    567                 Log.e(TAG, "Unexpected channel state: " + halChannelState);
    568                 return BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
    569         }
    570     }
    571 
    572     private boolean connectChannel(BluetoothDevice device,
    573                                    BluetoothHealthAppConfiguration config, int channelType) {
    574         if (mApps.get(config) == null) {
    575             Log.e(TAG, "connectChannel fail to get a app id from config");
    576             return false;
    577         }
    578 
    579         HealthChannel chan = new HealthChannel(device, config, channelType);
    580 
    581         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_CHANNEL);
    582         msg.obj = chan;
    583         mHandler.sendMessage(msg);
    584 
    585         return true;
    586     }
    587 
    588     private void callHealthChannelCallback(BluetoothHealthAppConfiguration config,
    589             BluetoothDevice device, int state, int prevState, ParcelFileDescriptor fd, int id) {
    590         broadcastHealthDeviceStateChange(device, state);
    591 
    592         log("Health Device Callback: " + device + " State Change: " + prevState + "->" +
    593                      state);
    594 
    595         ParcelFileDescriptor dupedFd = null;
    596         if (fd != null) {
    597             try {
    598                 dupedFd = fd.dup();
    599             } catch (IOException e) {
    600                 dupedFd = null;
    601                 Log.e(TAG, "Exception while duping: " + e);
    602             }
    603         }
    604 
    605         IBluetoothHealthCallback callback = (mApps.get(config)).mCallback;
    606         if (callback == null) {
    607             Log.e(TAG, "No callback found for config: " + config);
    608             return;
    609         }
    610 
    611         try {
    612             callback.onHealthChannelStateChange(config, device, prevState, state, dupedFd, id);
    613         } catch (RemoteException e) {
    614             Log.e(TAG, "Remote Exception:" + e);
    615         }
    616     }
    617 
    618     /**
    619      * This function sends the intent for the updates on the connection status to the remote device.
    620      * Note that multiple channels can be connected to the remote device by multiple applications.
    621      * This sends an intent for the update to the device connection status and not the channel
    622      * connection status. Only the following state transitions are possible:
    623      *
    624      * {@link BluetoothHealth#STATE_DISCONNECTED} to {@link BluetoothHealth#STATE_CONNECTING}
    625      * {@link BluetoothHealth#STATE_CONNECTING} to {@link BluetoothHealth#STATE_CONNECTED}
    626      * {@link BluetoothHealth#STATE_CONNECTED} to {@link BluetoothHealth#STATE_DISCONNECTING}
    627      * {@link BluetoothHealth#STATE_DISCONNECTING} to {@link BluetoothHealth#STATE_DISCONNECTED}
    628      * {@link BluetoothHealth#STATE_DISCONNECTED} to {@link BluetoothHealth#STATE_CONNECTED}
    629      * {@link BluetoothHealth#STATE_CONNECTED} to {@link BluetoothHealth#STATE_DISCONNECTED}
    630      * {@link BluetoothHealth#STATE_CONNECTING} to {{@link BluetoothHealth#STATE_DISCONNECTED}
    631      *
    632      * @param device
    633      * @param prevChannelState
    634      * @param newChannelState
    635      * @hide
    636      */
    637     private void broadcastHealthDeviceStateChange(BluetoothDevice device, int newChannelState) {
    638         if (mHealthDevices.get(device) == null) {
    639             mHealthDevices.put(device, BluetoothHealth.STATE_DISCONNECTED);
    640         }
    641 
    642         int currDeviceState = mHealthDevices.get(device);
    643         int newDeviceState = convertState(newChannelState);
    644 
    645         if (currDeviceState == newDeviceState) return;
    646 
    647         boolean sendIntent = false;
    648         List<HealthChannel> chan;
    649         switch (currDeviceState) {
    650             case BluetoothHealth.STATE_DISCONNECTED:
    651                 // there was no connection or connect/disconnect attemp with the remote device
    652                 sendIntent = true;
    653                 break;
    654             case BluetoothHealth.STATE_CONNECTING:
    655                 // there was no connection, there was a connecting attempt going on
    656 
    657                 // Channel got connected.
    658                 if (newDeviceState == BluetoothHealth.STATE_CONNECTED) {
    659                     sendIntent = true;
    660                 } else {
    661                     // Channel got disconnected
    662                     chan = findChannelByStates(device, new int [] {
    663                             BluetoothHealth.STATE_CHANNEL_CONNECTING,
    664                             BluetoothHealth.STATE_CHANNEL_DISCONNECTING});
    665                     if (chan.isEmpty()) {
    666                         sendIntent = true;
    667                     }
    668                 }
    669                 break;
    670             case BluetoothHealth.STATE_CONNECTED:
    671                 // there was at least one connection
    672 
    673                 // Channel got disconnected or is in disconnecting state.
    674                 chan = findChannelByStates(device, new int [] {
    675                         BluetoothHealth.STATE_CHANNEL_CONNECTING,
    676                         BluetoothHealth.STATE_CHANNEL_CONNECTED});
    677                 if (chan.isEmpty()) {
    678                     sendIntent = true;
    679                 }
    680                 break;
    681             case BluetoothHealth.STATE_DISCONNECTING:
    682                 // there was no connected channel with the remote device
    683                 // We were disconnecting all the channels with the remote device
    684 
    685                 // Channel got disconnected.
    686                 chan = findChannelByStates(device, new int [] {
    687                         BluetoothHealth.STATE_CHANNEL_CONNECTING,
    688                         BluetoothHealth.STATE_CHANNEL_DISCONNECTING});
    689                 if (chan.isEmpty()) {
    690                     updateAndSendIntent(device, newDeviceState, currDeviceState);
    691                 }
    692                 break;
    693         }
    694         if (sendIntent)
    695             updateAndSendIntent(device, newDeviceState, currDeviceState);
    696     }
    697 
    698     private void updateAndSendIntent(BluetoothDevice device, int newDeviceState,
    699             int prevDeviceState) {
    700         if (newDeviceState == BluetoothHealth.STATE_DISCONNECTED) {
    701             mHealthDevices.remove(device);
    702         } else {
    703             mHealthDevices.put(device, newDeviceState);
    704         }
    705         notifyProfileConnectionStateChanged(device, BluetoothProfile.HEALTH, newDeviceState, prevDeviceState);
    706     }
    707 
    708     /**
    709      * This function converts the channel connection state to device connection state.
    710      *
    711      * @param state
    712      * @return
    713      */
    714     private int convertState(int state) {
    715         switch (state) {
    716             case BluetoothHealth.STATE_CHANNEL_CONNECTED:
    717                 return BluetoothHealth.STATE_CONNECTED;
    718             case BluetoothHealth.STATE_CHANNEL_CONNECTING:
    719                 return BluetoothHealth.STATE_CONNECTING;
    720             case BluetoothHealth.STATE_CHANNEL_DISCONNECTING:
    721                 return BluetoothHealth.STATE_DISCONNECTING;
    722             case BluetoothHealth.STATE_CHANNEL_DISCONNECTED:
    723                 return BluetoothHealth.STATE_DISCONNECTED;
    724         }
    725         Log.e(TAG, "Mismatch in Channel and Health Device State: " + state);
    726         return BluetoothHealth.STATE_DISCONNECTED;
    727     }
    728 
    729     private int convertRoleToHal(int role) {
    730         if (role == BluetoothHealth.SOURCE_ROLE) return MDEP_ROLE_SOURCE;
    731         if (role == BluetoothHealth.SINK_ROLE) return MDEP_ROLE_SINK;
    732         Log.e(TAG, "unkonw role: " + role);
    733         return MDEP_ROLE_SINK;
    734     }
    735 
    736     private int convertChannelTypeToHal(int channelType) {
    737         if (channelType == BluetoothHealth.CHANNEL_TYPE_RELIABLE) return CHANNEL_TYPE_RELIABLE;
    738         if (channelType == BluetoothHealth.CHANNEL_TYPE_STREAMING) return CHANNEL_TYPE_STREAMING;
    739         if (channelType == BluetoothHealth.CHANNEL_TYPE_ANY) return CHANNEL_TYPE_ANY;
    740         Log.e(TAG, "unkonw channel type: " + channelType);
    741         return CHANNEL_TYPE_ANY;
    742     }
    743 
    744     private HealthChannel findChannelById(int id) {
    745         for (HealthChannel chan : mHealthChannels) {
    746             if (chan.mChannelId == id) return chan;
    747         }
    748         Log.e(TAG, "No channel found by id: " + id);
    749         return null;
    750     }
    751 
    752     private List<HealthChannel> findChannelByStates(BluetoothDevice device, int[] states) {
    753         List<HealthChannel> channels = new ArrayList<HealthChannel>();
    754         for (HealthChannel chan: mHealthChannels) {
    755             if (chan.mDevice.equals(device)) {
    756                 for (int state : states) {
    757                     if (chan.mState == state) {
    758                         channels.add(chan);
    759                     }
    760                 }
    761             }
    762         }
    763         return channels;
    764     }
    765 
    766     private int getConnectionState(BluetoothDevice device) {
    767         if (mHealthDevices.get(device) == null) {
    768             return BluetoothHealth.STATE_DISCONNECTED;
    769         }
    770         return mHealthDevices.get(device);
    771     }
    772 
    773     List<BluetoothDevice> lookupHealthDevicesMatchingStates(int[] states) {
    774         List<BluetoothDevice> healthDevices = new ArrayList<BluetoothDevice>();
    775 
    776         for (BluetoothDevice device: mHealthDevices.keySet()) {
    777             int healthDeviceState = getConnectionState(device);
    778             for (int state : states) {
    779                 if (state == healthDeviceState) {
    780                     healthDevices.add(device);
    781                     break;
    782                 }
    783             }
    784         }
    785         return healthDevices;
    786     }
    787 
    788     private static class AppInfo {
    789         private IBluetoothHealthCallback mCallback;
    790         private BluetoothHealthDeathRecipient mRcpObj;
    791         private int mAppId;
    792 
    793         private AppInfo(IBluetoothHealthCallback callback) {
    794             mCallback = callback;
    795             mRcpObj = null;
    796             mAppId = -1;
    797         }
    798 
    799         private void cleanup(){
    800             if(mCallback != null){
    801                 if(mRcpObj != null){
    802                     IBinder binder = mCallback.asBinder();
    803                     try{
    804                         binder.unlinkToDeath(mRcpObj,0);
    805                     }catch(NoSuchElementException e){
    806                         Log.e(TAG,"No death recipient registered"+e);
    807                     }
    808                     mRcpObj.cleanup();
    809                     mRcpObj = null;
    810                 }
    811                 mCallback = null;
    812             }
    813             else if(mRcpObj != null){
    814                     mRcpObj.cleanup();
    815                     mRcpObj = null;
    816             }
    817        }
    818     }
    819 
    820     private class HealthChannel {
    821         private ParcelFileDescriptor mChannelFd;
    822         private BluetoothDevice mDevice;
    823         private BluetoothHealthAppConfiguration mConfig;
    824         // BluetoothHealth channel state
    825         private int mState;
    826         private int mChannelType;
    827         private int mChannelId;
    828 
    829         private HealthChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config,
    830                       int channelType) {
    831              mChannelFd = null;
    832              mDevice = device;
    833              mConfig = config;
    834              mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
    835              mChannelType = channelType;
    836              mChannelId = -1;
    837         }
    838     }
    839 
    840     // Channel state event from Hal
    841     private class ChannelStateEvent {
    842         int mAppId;
    843         byte[] mAddr;
    844         int mCfgIndex;
    845         int mChannelId;
    846         int mState;
    847         FileDescriptor mFd;
    848 
    849         private ChannelStateEvent(int appId, byte[] addr, int cfgIndex,
    850                                   int channelId, int state, FileDescriptor fileDescriptor) {
    851             mAppId = appId;
    852             mAddr = addr;
    853             mCfgIndex = cfgIndex;
    854             mState = state;
    855             mChannelId = channelId;
    856             mFd = fileDescriptor;
    857         }
    858     }
    859 
    860     // Constants matching Hal header file bt_hl.h
    861     // bthl_app_reg_state_t
    862     private static final int APP_REG_STATE_REG_SUCCESS = 0;
    863     private static final int APP_REG_STATE_REG_FAILED = 1;
    864     private static final int APP_REG_STATE_DEREG_SUCCESS = 2;
    865     private static final int APP_REG_STATE_DEREG_FAILED = 3;
    866 
    867     // bthl_channel_state_t
    868     private static final int CONN_STATE_CONNECTING = 0;
    869     private static final int CONN_STATE_CONNECTED = 1;
    870     private static final int CONN_STATE_DISCONNECTING = 2;
    871     private static final int CONN_STATE_DISCONNECTED = 3;
    872     private static final int CONN_STATE_DESTROYED = 4;
    873 
    874     // bthl_mdep_role_t
    875     private static final int MDEP_ROLE_SOURCE = 0;
    876     private static final int MDEP_ROLE_SINK = 1;
    877 
    878     // bthl_channel_type_t
    879     private static final int CHANNEL_TYPE_RELIABLE = 0;
    880     private static final int CHANNEL_TYPE_STREAMING = 1;
    881     private static final int CHANNEL_TYPE_ANY =2;
    882 
    883     private native static void classInitNative();
    884     private native void initializeNative();
    885     private native void cleanupNative();
    886     private native int registerHealthAppNative(int dataType, int role, String name, int channelType);
    887     private native boolean unregisterHealthAppNative(int appId);
    888     private native int connectChannelNative(byte[] btAddress, int appId);
    889     private native boolean disconnectChannelNative(int channelId);
    890 
    891 }
    892