1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.server; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothDevice; 21 import android.bluetooth.BluetoothHealth; 22 import android.bluetooth.BluetoothHealthAppConfiguration; 23 import android.bluetooth.BluetoothProfile; 24 import android.bluetooth.IBluetoothHealthCallback; 25 import android.content.Context; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.ParcelFileDescriptor; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import java.io.IOException; 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map.Entry; 37 import java.util.concurrent.atomic.AtomicInteger; 38 39 /** 40 * This handles all the operations on the Bluetooth Health profile. 41 * All functions are called by BluetoothService, as Bluetooth Service 42 * is the Service handler for the HDP profile. 43 * 44 * @hide 45 */ 46 final class BluetoothHealthProfileHandler { 47 private static final String TAG = "BluetoothHealthProfileHandler"; 48 private static final boolean DBG = false; 49 50 private static BluetoothHealthProfileHandler sInstance; 51 private BluetoothService mBluetoothService; 52 private ArrayList<HealthChannel> mHealthChannels; 53 private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs; 54 private HashMap <BluetoothDevice, Integer> mHealthDevices; 55 private HashMap <BluetoothHealthAppConfiguration, IBluetoothHealthCallback> mCallbacks; 56 57 private static final int MESSAGE_REGISTER_APPLICATION = 0; 58 private static final int MESSAGE_UNREGISTER_APPLICATION = 1; 59 private static final int MESSAGE_CONNECT_CHANNEL = 2; 60 private static final AtomicInteger sChannelId = new AtomicInteger(); 61 62 class HealthChannel { 63 private ParcelFileDescriptor mChannelFd; 64 private boolean mMainChannel; 65 private String mChannelPath; 66 private BluetoothDevice mDevice; 67 private BluetoothHealthAppConfiguration mConfig; 68 private int mState; 69 private int mChannelType; 70 private int mId; 71 72 HealthChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, 73 ParcelFileDescriptor fd, boolean mainChannel, String channelPath) { 74 mChannelFd = fd; 75 mMainChannel = mainChannel; 76 mChannelPath = channelPath; 77 mDevice = device; 78 mConfig = config; 79 mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; 80 mId = getChannelId(); 81 } 82 } 83 84 private final Handler mHandler = new Handler() { 85 @Override 86 public void handleMessage(Message msg) { 87 switch (msg.what) { 88 case MESSAGE_REGISTER_APPLICATION: 89 BluetoothHealthAppConfiguration registerApp = 90 (BluetoothHealthAppConfiguration) msg.obj; 91 int role = registerApp.getRole(); 92 String path = null; 93 94 if (role == BluetoothHealth.SINK_ROLE) { 95 path = mBluetoothService.registerHealthApplicationNative( 96 registerApp.getDataType(), getStringRole(role), registerApp.getName()); 97 } else { 98 path = mBluetoothService.registerHealthApplicationNative( 99 registerApp.getDataType(), getStringRole(role), registerApp.getName(), 100 getStringChannelType(registerApp.getChannelType())); 101 } 102 103 if (path == null) { 104 callHealthApplicationStatusCallback(registerApp, 105 BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE); 106 mCallbacks.remove(registerApp); 107 } else { 108 mHealthAppConfigs.put(registerApp, path); 109 callHealthApplicationStatusCallback(registerApp, 110 BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS); 111 } 112 113 break; 114 case MESSAGE_UNREGISTER_APPLICATION: 115 BluetoothHealthAppConfiguration unregisterApp = 116 (BluetoothHealthAppConfiguration) msg.obj; 117 118 // Disconnect all the channels 119 for (HealthChannel chan : mHealthChannels) { 120 if (chan.mConfig.equals(unregisterApp) && 121 chan.mState != BluetoothHealth.STATE_CHANNEL_DISCONNECTED) { 122 disconnectChannel(chan.mDevice, unregisterApp, chan.mId); 123 } 124 } 125 126 boolean result = mBluetoothService.unregisterHealthApplicationNative( 127 mHealthAppConfigs.get(unregisterApp)); 128 if (result) { 129 callHealthApplicationStatusCallback(unregisterApp, 130 BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS); 131 mCallbacks.remove(unregisterApp); 132 mHealthAppConfigs.remove(unregisterApp); 133 } else { 134 callHealthApplicationStatusCallback(unregisterApp, 135 BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE); 136 } 137 break; 138 case MESSAGE_CONNECT_CHANNEL: 139 HealthChannel chan = (HealthChannel)msg.obj; 140 String deviceObjectPath = 141 mBluetoothService.getObjectPathFromAddress(chan.mDevice.getAddress()); 142 String configPath = mHealthAppConfigs.get(chan.mConfig); 143 String channelType = getStringChannelType(chan.mChannelType); 144 145 if (!mBluetoothService.createChannelNative(deviceObjectPath, configPath, 146 channelType, chan.mId)) { 147 int prevState = chan.mState; 148 int state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; 149 callHealthChannelCallback(chan.mConfig, chan.mDevice, prevState, state, null, 150 chan.mId); 151 mHealthChannels.remove(chan); 152 } 153 } 154 } 155 }; 156 157 private BluetoothHealthProfileHandler(Context context, BluetoothService service) { 158 mBluetoothService = service; 159 mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>(); 160 mHealthChannels = new ArrayList<HealthChannel>(); 161 mHealthDevices = new HashMap<BluetoothDevice, Integer>(); 162 mCallbacks = new HashMap<BluetoothHealthAppConfiguration, IBluetoothHealthCallback>(); 163 } 164 165 static synchronized BluetoothHealthProfileHandler getInstance(Context context, 166 BluetoothService service) { 167 if (sInstance == null) sInstance = new BluetoothHealthProfileHandler(context, service); 168 return sInstance; 169 } 170 171 boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, 172 IBluetoothHealthCallback callback) { 173 Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION); 174 msg.obj = config; 175 mHandler.sendMessage(msg); 176 mCallbacks.put(config, callback); 177 return true; 178 } 179 180 boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { 181 String path = mHealthAppConfigs.get(config); 182 if (path == null) return false; 183 184 Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_APPLICATION); 185 msg.obj = config; 186 mHandler.sendMessage(msg); 187 return true; 188 } 189 190 boolean connectChannelToSource(BluetoothDevice device, 191 BluetoothHealthAppConfiguration config) { 192 return connectChannel(device, config, BluetoothHealth.CHANNEL_TYPE_ANY); 193 } 194 195 private HealthChannel getMainChannel(BluetoothDevice device, 196 BluetoothHealthAppConfiguration config) { 197 for (HealthChannel chan: mHealthChannels) { 198 if (chan.mDevice.equals(device) && chan.mConfig.equals(config)) { 199 if (chan.mMainChannel) return chan; 200 } 201 } 202 return null; 203 } 204 205 boolean connectChannel(BluetoothDevice device, 206 BluetoothHealthAppConfiguration config, int channelType) { 207 String deviceObjectPath = 208 mBluetoothService.getObjectPathFromAddress(device.getAddress()); 209 if (deviceObjectPath == null) return false; 210 211 String configPath = mHealthAppConfigs.get(config); 212 if (configPath == null) return false; 213 214 HealthChannel chan = new HealthChannel(device, config, null, false, null); 215 chan.mState = BluetoothHealth.STATE_CHANNEL_CONNECTING; 216 chan.mChannelType = channelType; 217 mHealthChannels.add(chan); 218 219 int prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; 220 int state = BluetoothHealth.STATE_CHANNEL_CONNECTING; 221 callHealthChannelCallback(config, device, prevState, state, null, chan.mId); 222 223 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_CHANNEL); 224 msg.obj = chan; 225 mHandler.sendMessage(msg); 226 227 return true; 228 } 229 230 private String getStringChannelType(int type) { 231 if (type == BluetoothHealth.CHANNEL_TYPE_RELIABLE) { 232 return "Reliable"; 233 } else if (type == BluetoothHealth.CHANNEL_TYPE_STREAMING) { 234 return "Streaming"; 235 } else { 236 return "Any"; 237 } 238 } 239 240 private String getStringRole(int role) { 241 if (role == BluetoothHealth.SINK_ROLE) { 242 return "Sink"; 243 } else if (role == BluetoothHealth.SOURCE_ROLE) { 244 return "Streaming"; 245 } else { 246 return null; 247 } 248 } 249 250 private int getChannelId() { 251 // The function doesn't need to be synchronized, as the health profile handler 252 // will only allow one health channel object creation at a time. 253 // In the worst case the while loop will have to break out at some point of 254 // time, because only a limited number of L2CAP channels are possible. 255 int id; 256 boolean found; 257 do { 258 id = sChannelId.incrementAndGet(); 259 found = false; 260 for (HealthChannel chan: mHealthChannels) { 261 if (chan.mId == id) found = true; 262 } 263 } while (found); 264 return id; 265 } 266 267 boolean disconnectChannel(BluetoothDevice device, 268 BluetoothHealthAppConfiguration config, int id) { 269 HealthChannel chan = findChannelById(id); 270 if (chan == null) { 271 return false; 272 } 273 274 String deviceObjectPath = 275 mBluetoothService.getObjectPathFromAddress(device.getAddress()); 276 277 mBluetoothService.releaseChannelFdNative(chan.mChannelPath); 278 279 int prevState = chan.mState; 280 chan.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTING; 281 callHealthChannelCallback(config, device, prevState, chan.mState, 282 null, chan.mId); 283 284 if (!mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath, 285 chan.mId)) { 286 prevState = chan.mState; 287 chan.mState = BluetoothHealth.STATE_CHANNEL_CONNECTED; 288 callHealthChannelCallback(config, device, prevState, chan.mState, 289 chan.mChannelFd, chan.mId); 290 return false; 291 } else { 292 return true; 293 } 294 } 295 296 private HealthChannel findChannelById(int id) { 297 for (HealthChannel chan : mHealthChannels) { 298 if (chan.mId == id) return chan; 299 } 300 return null; 301 } 302 303 private HealthChannel findChannelByPath(BluetoothDevice device, String path) { 304 for (HealthChannel chan : mHealthChannels) { 305 if (path.equals(chan.mChannelPath) && device.equals(chan.mDevice)) return chan; 306 } 307 return null; 308 } 309 310 private List<HealthChannel> findChannelByStates(BluetoothDevice device, int[] states) { 311 List<HealthChannel> channels = new ArrayList<HealthChannel>(); 312 for (HealthChannel chan: mHealthChannels) { 313 if (chan.mDevice.equals(device)) { 314 for (int state : states) { 315 if (chan.mState == state) { 316 channels.add(chan); 317 } 318 } 319 } 320 } 321 return channels; 322 } 323 324 private HealthChannel findConnectingChannel(BluetoothDevice device, 325 BluetoothHealthAppConfiguration config) { 326 for (HealthChannel chan : mHealthChannels) { 327 if (chan.mDevice.equals(device) && chan.mConfig.equals(config) && 328 chan.mState == BluetoothHealth.STATE_CHANNEL_CONNECTING) return chan; 329 } 330 return null; 331 } 332 333 ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, 334 BluetoothHealthAppConfiguration config) { 335 HealthChannel chan = getMainChannel(device, config); 336 if (chan != null) { 337 ParcelFileDescriptor pfd = null; 338 try { 339 pfd = chan.mChannelFd.dup(); 340 return pfd; 341 } catch (IOException e) { 342 return null; 343 } 344 } 345 346 String objectPath = 347 mBluetoothService.getObjectPathFromAddress(device.getAddress()); 348 if (objectPath == null) return null; 349 350 String mainChannelPath = mBluetoothService.getMainChannelNative(objectPath); 351 if (mainChannelPath == null) return null; 352 353 // We had no record of the main channel but querying Bluez we got a 354 // main channel. We might not have received the PropertyChanged yet for 355 // the main channel creation so update our data structure here. 356 chan = findChannelByPath(device, mainChannelPath); 357 if (chan == null) { 358 errorLog("Main Channel present but we don't have any account of it:" + 359 device +":" + config); 360 return null; 361 } 362 chan.mMainChannel = true; 363 try { 364 return chan.mChannelFd.dup(); 365 } catch (IOException e) { 366 return null; 367 } 368 } 369 370 /*package*/ void onHealthDevicePropertyChanged(String devicePath, 371 String channelPath) { 372 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 373 String address = mBluetoothService.getAddressFromObjectPath(devicePath); 374 if (address == null) return; 375 376 //TODO: Fix this in Bluez 377 if (channelPath.equals("/")) { 378 // This means that the main channel is being destroyed. 379 return; 380 } 381 382 BluetoothDevice device = adapter.getRemoteDevice(address); 383 BluetoothHealthAppConfiguration config = findHealthApplication(device, 384 channelPath); 385 if (config != null) { 386 HealthChannel chan = findChannelByPath(device, channelPath); 387 if (chan == null) { 388 errorLog("Health Channel is not present:" + channelPath); 389 } else { 390 chan.mMainChannel = true; 391 } 392 } 393 } 394 395 /*package*/ void onHealthDeviceChannelConnectionError(int chanCode, 396 int state) { 397 HealthChannel channel = findChannelById(chanCode); 398 if (channel == null) errorLog("No record of this channel:" + chanCode); 399 400 callHealthChannelCallback(channel.mConfig, channel.mDevice, channel.mState, state, null, 401 chanCode); 402 } 403 404 private BluetoothHealthAppConfiguration findHealthApplication( 405 BluetoothDevice device, String channelPath) { 406 BluetoothHealthAppConfiguration config = null; 407 HealthChannel chan = findChannelByPath(device, channelPath); 408 409 if (chan != null) { 410 config = chan.mConfig; 411 } else { 412 String configPath = mBluetoothService.getChannelApplicationNative(channelPath); 413 if (configPath == null) { 414 errorLog("Config path is null for application"); 415 } else { 416 for (Entry<BluetoothHealthAppConfiguration, String> e : 417 mHealthAppConfigs.entrySet()) { 418 if (e.getValue().equals(configPath)) { 419 config = e.getKey(); 420 } 421 } 422 if (config == null) errorLog("No associated application for path:" + configPath); 423 } 424 } 425 return config; 426 } 427 428 /*package*/ void onHealthDeviceChannelChanged(String devicePath, 429 String channelPath, boolean exists) { 430 debugLog("onHealthDeviceChannelChanged: devicePath: " + devicePath + 431 "ChannelPath: " + channelPath + "Exists: " + exists); 432 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 433 String address = mBluetoothService.getAddressFromObjectPath(devicePath); 434 if (address == null) return; 435 436 BluetoothDevice device = adapter.getRemoteDevice(address); 437 BluetoothHealthAppConfiguration config; 438 int state, prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; 439 ParcelFileDescriptor fd; 440 HealthChannel channel; 441 config = findHealthApplication(device, channelPath); 442 443 if (exists) { 444 channel = findConnectingChannel(device, config); 445 if (channel == null) { 446 channel = new HealthChannel(device, config, null, false, 447 channelPath); 448 channel.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; 449 mHealthChannels.add(channel); 450 } 451 channel.mChannelPath = channelPath; 452 453 fd = mBluetoothService.getChannelFdNative(channelPath); 454 if (fd == null) { 455 errorLog("Error obtaining fd for channel:" + channelPath); 456 disconnectChannel(device, config, channel.mId); 457 return; 458 } 459 boolean mainChannel = 460 getMainChannel(device, config) == null ? false : true; 461 if (!mainChannel) { 462 String mainChannelPath = 463 mBluetoothService.getMainChannelNative(devicePath); 464 if (mainChannelPath == null) { 465 errorLog("Main Channel Path is null for devicePath:" + devicePath); 466 return; 467 } 468 if (mainChannelPath.equals(channelPath)) mainChannel = true; 469 } 470 471 channel.mChannelFd = fd; 472 channel.mMainChannel = mainChannel; 473 prevState = channel.mState; 474 state = BluetoothHealth.STATE_CHANNEL_CONNECTED; 475 } else { 476 channel = findChannelByPath(device, channelPath); 477 if (channel == null) { 478 errorLog("Channel not found:" + config + ":" + channelPath); 479 return; 480 } 481 mHealthChannels.remove(channel); 482 483 channel.mChannelFd = null; 484 prevState = channel.mState; 485 state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; 486 } 487 channel.mState = state; 488 callHealthChannelCallback(config, device, prevState, state, channel.mChannelFd, 489 channel.mId); 490 } 491 492 private void callHealthChannelCallback(BluetoothHealthAppConfiguration config, 493 BluetoothDevice device, int prevState, int state, ParcelFileDescriptor fd, int id) { 494 broadcastHealthDeviceStateChange(device, prevState, state); 495 496 debugLog("Health Device Callback: " + device + " State Change: " 497 + prevState + "->" + state); 498 499 ParcelFileDescriptor dupedFd = null; 500 if (fd != null) { 501 try { 502 dupedFd = fd.dup(); 503 } catch (IOException e) { 504 dupedFd = null; 505 errorLog("Exception while duping: " + e); 506 } 507 } 508 509 IBluetoothHealthCallback callback = mCallbacks.get(config); 510 if (callback != null) { 511 try { 512 callback.onHealthChannelStateChange(config, device, prevState, state, dupedFd, id); 513 } catch (RemoteException e) { 514 errorLog("Remote Exception:" + e); 515 } 516 } 517 } 518 519 private void callHealthApplicationStatusCallback( 520 BluetoothHealthAppConfiguration config, int status) { 521 debugLog("Health Device Application: " + config + " State Change: status:" 522 + status); 523 IBluetoothHealthCallback callback = mCallbacks.get(config); 524 if (callback != null) { 525 try { 526 callback.onHealthAppConfigurationStatusChange(config, status); 527 } catch (RemoteException e) { 528 errorLog("Remote Exception:" + e); 529 } 530 } 531 } 532 533 int getHealthDeviceConnectionState(BluetoothDevice device) { 534 if (mHealthDevices.get(device) == null) { 535 return BluetoothHealth.STATE_DISCONNECTED; 536 } 537 return mHealthDevices.get(device); 538 } 539 540 List<BluetoothDevice> getConnectedHealthDevices() { 541 List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates( 542 new int[] {BluetoothHealth.STATE_CONNECTED}); 543 return devices; 544 } 545 546 List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(int[] states) { 547 List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates(states); 548 return devices; 549 } 550 551 List<BluetoothDevice> lookupHealthDevicesMatchingStates(int[] states) { 552 List<BluetoothDevice> healthDevices = new ArrayList<BluetoothDevice>(); 553 554 for (BluetoothDevice device: mHealthDevices.keySet()) { 555 int healthDeviceState = getHealthDeviceConnectionState(device); 556 for (int state : states) { 557 if (state == healthDeviceState) { 558 healthDevices.add(device); 559 break; 560 } 561 } 562 } 563 return healthDevices; 564 } 565 566 /** 567 * This function sends the intent for the updates on the connection status to the remote device. 568 * Note that multiple channels can be connected to the remote device by multiple applications. 569 * This sends an intent for the update to the device connection status and not the channel 570 * connection status. Only the following state transitions are possible: 571 * 572 * {@link BluetoothHealth#STATE_DISCONNECTED} to {@link BluetoothHealth#STATE_CONNECTING} 573 * {@link BluetoothHealth#STATE_CONNECTING} to {@link BluetoothHealth#STATE_CONNECTED} 574 * {@link BluetoothHealth#STATE_CONNECTED} to {@link BluetoothHealth#STATE_DISCONNECTING} 575 * {@link BluetoothHealth#STATE_DISCONNECTING} to {@link BluetoothHealth#STATE_DISCONNECTED} 576 * {@link BluetoothHealth#STATE_DISCONNECTED} to {@link BluetoothHealth#STATE_CONNECTED} 577 * {@link BluetoothHealth#STATE_CONNECTED} to {@link BluetoothHealth#STATE_DISCONNECTED} 578 * {@link BluetoothHealth#STATE_CONNECTING} to {{@link BluetoothHealth#STATE_DISCONNECTED} 579 * 580 * @param device 581 * @param prevChannelState 582 * @param newChannelState 583 * @hide 584 */ 585 private void broadcastHealthDeviceStateChange(BluetoothDevice device, int prevChannelState, 586 int newChannelState) { 587 if (mHealthDevices.get(device) == null) { 588 mHealthDevices.put(device, BluetoothHealth.STATE_DISCONNECTED); 589 } 590 591 int currDeviceState = mHealthDevices.get(device); 592 int newDeviceState = convertState(newChannelState); 593 594 if (currDeviceState != newDeviceState) { 595 List<HealthChannel> chan; 596 switch (currDeviceState) { 597 case BluetoothHealth.STATE_DISCONNECTED: 598 updateAndSendIntent(device, currDeviceState, newDeviceState); 599 break; 600 case BluetoothHealth.STATE_CONNECTING: 601 // Channel got connected. 602 if (newDeviceState == BluetoothHealth.STATE_CONNECTED) { 603 updateAndSendIntent(device, currDeviceState, newDeviceState); 604 } else { 605 // Channel got disconnected 606 chan = findChannelByStates(device, new int [] { 607 BluetoothHealth.STATE_CHANNEL_CONNECTING, 608 BluetoothHealth.STATE_CHANNEL_DISCONNECTING}); 609 if (chan.isEmpty()) { 610 updateAndSendIntent(device, currDeviceState, newDeviceState); 611 } 612 } 613 break; 614 case BluetoothHealth.STATE_CONNECTED: 615 // Channel got disconnected or is in disconnecting state. 616 chan = findChannelByStates(device, new int [] { 617 BluetoothHealth.STATE_CHANNEL_CONNECTING, 618 BluetoothHealth.STATE_CHANNEL_CONNECTED}); 619 if (chan.isEmpty()) { 620 updateAndSendIntent(device, currDeviceState, newDeviceState); 621 } 622 break; 623 case BluetoothHealth.STATE_DISCONNECTING: 624 // Channel got disconnected. 625 chan = findChannelByStates(device, new int [] { 626 BluetoothHealth.STATE_CHANNEL_CONNECTING, 627 BluetoothHealth.STATE_CHANNEL_DISCONNECTING}); 628 if (chan.isEmpty()) { 629 updateAndSendIntent(device, currDeviceState, newDeviceState); 630 } 631 break; 632 } 633 } 634 } 635 636 private void updateAndSendIntent(BluetoothDevice device, int prevDeviceState, 637 int newDeviceState) { 638 mHealthDevices.put(device, newDeviceState); 639 mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.HEALTH, 640 newDeviceState, prevDeviceState); 641 } 642 643 /** 644 * This function converts the channel connection state to device connection state. 645 * 646 * @param state 647 * @return 648 */ 649 private int convertState(int state) { 650 switch (state) { 651 case BluetoothHealth.STATE_CHANNEL_CONNECTED: 652 return BluetoothHealth.STATE_CONNECTED; 653 case BluetoothHealth.STATE_CHANNEL_CONNECTING: 654 return BluetoothHealth.STATE_CONNECTING; 655 case BluetoothHealth.STATE_CHANNEL_DISCONNECTING: 656 return BluetoothHealth.STATE_DISCONNECTING; 657 case BluetoothHealth.STATE_CHANNEL_DISCONNECTED: 658 return BluetoothHealth.STATE_DISCONNECTED; 659 } 660 errorLog("Mismatch in Channel and Health Device State"); 661 return -1; 662 } 663 664 private static void debugLog(String msg) { 665 if (DBG) Log.d(TAG, msg); 666 } 667 668 private static void errorLog(String msg) { 669 Log.e(TAG, msg); 670 } 671 } 672