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