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