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