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