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