1 /* 2 * Copyright (C) 2008 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.BluetoothA2dp; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothClass; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothUuid; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.ParcelUuid; 29 import android.util.Log; 30 31 import java.util.HashMap; 32 import java.util.Set; 33 34 /** 35 * TODO: Move this to 36 * java/services/com/android/server/BluetoothEventLoop.java 37 * and make the constructor package private again. 38 * 39 * @hide 40 */ 41 class BluetoothEventLoop { 42 private static final String TAG = "BluetoothEventLoop"; 43 private static final boolean DBG = false; 44 45 private int mNativeData; 46 private Thread mThread; 47 private boolean mStarted; 48 private boolean mInterrupted; 49 50 private final HashMap<String, Integer> mPasskeyAgentRequestData; 51 private final BluetoothService mBluetoothService; 52 private final BluetoothAdapter mAdapter; 53 private final Context mContext; 54 55 private static final int EVENT_RESTART_BLUETOOTH = 1; 56 private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 2; 57 private static final int EVENT_AGENT_CANCEL = 3; 58 59 private static final int CREATE_DEVICE_ALREADY_EXISTS = 1; 60 private static final int CREATE_DEVICE_SUCCESS = 0; 61 private static final int CREATE_DEVICE_FAILED = -1; 62 63 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 64 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 65 66 private final Handler mHandler = new Handler() { 67 @Override 68 public void handleMessage(Message msg) { 69 String address = null; 70 switch (msg.what) { 71 case EVENT_RESTART_BLUETOOTH: 72 mBluetoothService.restart(); 73 break; 74 case EVENT_PAIRING_CONSENT_DELAYED_ACCEPT: 75 address = (String)msg.obj; 76 if (address != null) { 77 mBluetoothService.setPairingConfirmation(address, true); 78 } 79 break; 80 case EVENT_AGENT_CANCEL: 81 // Set the Bond State to BOND_NONE. 82 // We always have only 1 device in BONDING state. 83 String[] devices = mBluetoothService.listInState(BluetoothDevice.BOND_BONDING); 84 if (devices.length == 0) { 85 break; 86 } else if (devices.length > 1) { 87 Log.e(TAG, " There is more than one device in the Bonding State"); 88 break; 89 } 90 address = devices[0]; 91 mBluetoothService.setBondState(address, 92 BluetoothDevice.BOND_NONE, 93 BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED); 94 break; 95 } 96 } 97 }; 98 99 static { classInitNative(); } 100 private static native void classInitNative(); 101 102 /* package */ BluetoothEventLoop(Context context, BluetoothAdapter adapter, 103 BluetoothService bluetoothService) { 104 mBluetoothService = bluetoothService; 105 mContext = context; 106 mPasskeyAgentRequestData = new HashMap(); 107 mAdapter = adapter; 108 initializeNativeDataNative(); 109 } 110 111 protected void finalize() throws Throwable { 112 try { 113 cleanupNativeDataNative(); 114 } finally { 115 super.finalize(); 116 } 117 } 118 119 /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() { 120 return mPasskeyAgentRequestData; 121 } 122 123 /* package */ void start() { 124 125 if (!isEventLoopRunningNative()) { 126 if (DBG) log("Starting Event Loop thread"); 127 startEventLoopNative(); 128 } 129 } 130 131 public void stop() { 132 if (isEventLoopRunningNative()) { 133 if (DBG) log("Stopping Event Loop thread"); 134 stopEventLoopNative(); 135 } 136 } 137 138 public boolean isEventLoopRunning() { 139 return isEventLoopRunningNative(); 140 } 141 142 private void addDevice(String address, String[] properties) { 143 mBluetoothService.addRemoteDeviceProperties(address, properties); 144 String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI"); 145 String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class"); 146 String name = mBluetoothService.getRemoteDeviceProperty(address, "Name"); 147 short rssiValue; 148 // For incoming connections, we don't get the RSSI value. Use a default of MIN_VALUE. 149 // If we accept the pairing, we will automatically show it at the top of the list. 150 if (rssi != null) { 151 rssiValue = (short)Integer.valueOf(rssi).intValue(); 152 } else { 153 rssiValue = Short.MIN_VALUE; 154 } 155 if (classValue != null) { 156 Intent intent = new Intent(BluetoothDevice.ACTION_FOUND); 157 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 158 intent.putExtra(BluetoothDevice.EXTRA_CLASS, 159 new BluetoothClass(Integer.valueOf(classValue))); 160 intent.putExtra(BluetoothDevice.EXTRA_RSSI, rssiValue); 161 intent.putExtra(BluetoothDevice.EXTRA_NAME, name); 162 163 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 164 } else { 165 log ("ClassValue: " + classValue + " for remote device: " + address + " is null"); 166 } 167 } 168 169 private void onDeviceFound(String address, String[] properties) { 170 if (properties == null) { 171 Log.e(TAG, "ERROR: Remote device properties are null"); 172 return; 173 } 174 addDevice(address, properties); 175 } 176 177 private void onDeviceDisappeared(String address) { 178 Intent intent = new Intent(BluetoothDevice.ACTION_DISAPPEARED); 179 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 180 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 181 } 182 183 private void onDeviceDisconnectRequested(String deviceObjectPath) { 184 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); 185 if (address == null) { 186 Log.e(TAG, "onDeviceDisconnectRequested: Address of the remote device in null"); 187 return; 188 } 189 Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED); 190 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 191 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 192 } 193 194 private void onCreatePairedDeviceResult(String address, int result) { 195 address = address.toUpperCase(); 196 mBluetoothService.onCreatePairedDeviceResult(address, result); 197 } 198 199 private void onDeviceCreated(String deviceObjectPath) { 200 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); 201 if (!mBluetoothService.isRemoteDeviceInCache(address)) { 202 // Incoming connection, we haven't seen this device, add to cache. 203 String[] properties = mBluetoothService.getRemoteDeviceProperties(address); 204 if (properties != null) { 205 addDevice(address, properties); 206 } 207 } 208 return; 209 } 210 211 private void onDeviceRemoved(String deviceObjectPath) { 212 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); 213 if (address != null) { 214 mBluetoothService.setBondState(address.toUpperCase(), BluetoothDevice.BOND_NONE, 215 BluetoothDevice.UNBOND_REASON_REMOVED); 216 mBluetoothService.setRemoteDeviceProperty(address, "UUIDs", null); 217 } 218 } 219 220 /*package*/ void onPropertyChanged(String[] propValues) { 221 if (mBluetoothService.isAdapterPropertiesEmpty()) { 222 // We have got a property change before 223 // we filled up our cache. 224 mBluetoothService.getAllProperties(); 225 } 226 String name = propValues[0]; 227 if (name.equals("Name")) { 228 mBluetoothService.setProperty(name, propValues[1]); 229 Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); 230 intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, propValues[1]); 231 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 232 } else if (name.equals("Pairable") || name.equals("Discoverable")) { 233 String pairable = name.equals("Pairable") ? propValues[1] : 234 mBluetoothService.getPropertyInternal("Pairable"); 235 String discoverable = name.equals("Discoverable") ? propValues[1] : 236 mBluetoothService.getPropertyInternal("Discoverable"); 237 238 // This shouldn't happen, unless Adapter Properties are null. 239 if (pairable == null || discoverable == null) 240 return; 241 242 mBluetoothService.setProperty(name, propValues[1]); 243 int mode = BluetoothService.bluezStringToScanMode( 244 pairable.equals("true"), 245 discoverable.equals("true")); 246 if (mode >= 0) { 247 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); 248 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mode); 249 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 250 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 251 } 252 } else if (name.equals("Discovering")) { 253 Intent intent; 254 mBluetoothService.setProperty(name, propValues[1]); 255 if (propValues[1].equals("true")) { 256 mBluetoothService.setIsDiscovering(true); 257 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED); 258 } else { 259 // Stop the discovery. 260 mBluetoothService.cancelDiscovery(); 261 mBluetoothService.setIsDiscovering(false); 262 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 263 } 264 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 265 } else if (name.equals("Devices")) { 266 String value = null; 267 int len = Integer.valueOf(propValues[1]); 268 if (len > 0) { 269 StringBuilder str = new StringBuilder(); 270 for (int i = 2; i < propValues.length; i++) { 271 str.append(propValues[i]); 272 str.append(","); 273 } 274 value = str.toString(); 275 } 276 mBluetoothService.setProperty(name, value); 277 } else if (name.equals("Powered")) { 278 // bluetoothd has restarted, re-read all our properties. 279 // Note: bluez only sends this property change when it restarts. 280 if (propValues[1].equals("true")) 281 onRestartRequired(); 282 } 283 } 284 285 private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) { 286 String name = propValues[0]; 287 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); 288 if (address == null) { 289 Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null"); 290 return; 291 } 292 if (DBG) { 293 log("Device property changed:" + address + "property:" + name); 294 } 295 BluetoothDevice device = mAdapter.getRemoteDevice(address); 296 if (name.equals("Name")) { 297 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); 298 Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); 299 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 300 intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]); 301 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 302 } else if (name.equals("Class")) { 303 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); 304 Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED); 305 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 306 intent.putExtra(BluetoothDevice.EXTRA_CLASS, 307 new BluetoothClass(Integer.valueOf(propValues[1]))); 308 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 309 } else if (name.equals("Connected")) { 310 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); 311 Intent intent = null; 312 if (propValues[1].equals("true")) { 313 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED); 314 // Set the link timeout to 8000 slots (5 sec timeout) 315 // for bluetooth docks. 316 if (mBluetoothService.isBluetoothDock(address)) { 317 mBluetoothService.setLinkTimeout(address, 8000); 318 } 319 } else { 320 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); 321 } 322 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 323 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 324 } else if (name.equals("UUIDs")) { 325 String uuid = null; 326 int len = Integer.valueOf(propValues[1]); 327 if (len > 0) { 328 StringBuilder str = new StringBuilder(); 329 for (int i = 2; i < propValues.length; i++) { 330 str.append(propValues[i]); 331 str.append(","); 332 } 333 uuid = str.toString(); 334 } 335 mBluetoothService.setRemoteDeviceProperty(address, name, uuid); 336 337 // UUIDs have changed, query remote service channel and update cache. 338 mBluetoothService.updateDeviceServiceChannelCache(address); 339 340 mBluetoothService.sendUuidIntent(address); 341 } else if (name.equals("Paired")) { 342 if (propValues[1].equals("true")) { 343 // If locally initiated pairing, we will 344 // not go to BOND_BONDED state until we have received a 345 // successful return value in onCreatePairedDeviceResult 346 if (null == mBluetoothService.getPendingOutgoingBonding()) { 347 mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDED); 348 } 349 } else { 350 mBluetoothService.setBondState(address, BluetoothDevice.BOND_NONE); 351 mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false"); 352 } 353 } else if (name.equals("Trusted")) { 354 if (DBG) 355 log("set trust state succeded, value is " + propValues[1]); 356 mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); 357 } 358 } 359 360 private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) { 361 String address = mBluetoothService.getAddressFromObjectPath(objectPath); 362 if (address == null) { 363 Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " + 364 "returning null"); 365 return null; 366 } 367 address = address.toUpperCase(); 368 mPasskeyAgentRequestData.put(address, new Integer(nativeData)); 369 370 if (mBluetoothService.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF) { 371 // shutdown path 372 mBluetoothService.cancelPairingUserInput(address); 373 return null; 374 } 375 // Set state to BONDING. For incoming connections it will be set here. 376 // For outgoing connections, it gets set when we call createBond. 377 // Also set it only when the state is not already Bonded, we can sometimes 378 // get an authorization request from the remote end if it doesn't have the link key 379 // while we still have it. 380 if (mBluetoothService.getBondState(address) != BluetoothDevice.BOND_BONDED) 381 mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDING); 382 return address; 383 } 384 385 private void onRequestPairingConsent(String objectPath, int nativeData) { 386 String address = checkPairingRequestAndGetAddress(objectPath, nativeData); 387 if (address == null) return; 388 389 /* The link key will not be stored if the incoming request has MITM 390 * protection switched on. Unfortunately, some devices have MITM 391 * switched on even though their capabilities are NoInputNoOutput, 392 * so we may get this request many times. Also if we respond immediately, 393 * the other end is unable to handle it. Delay sending the message. 394 */ 395 if (mBluetoothService.getBondState(address) == BluetoothDevice.BOND_BONDED) { 396 Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT); 397 message.obj = address; 398 mHandler.sendMessageDelayed(message, 1500); 399 return; 400 } 401 402 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); 403 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 404 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, 405 BluetoothDevice.PAIRING_VARIANT_CONSENT); 406 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 407 return; 408 } 409 410 private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) { 411 String address = checkPairingRequestAndGetAddress(objectPath, nativeData); 412 if (address == null) return; 413 414 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); 415 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 416 intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey); 417 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, 418 BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION); 419 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 420 return; 421 } 422 423 private void onRequestPasskey(String objectPath, int nativeData) { 424 String address = checkPairingRequestAndGetAddress(objectPath, nativeData); 425 if (address == null) return; 426 427 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); 428 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 429 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, 430 BluetoothDevice.PAIRING_VARIANT_PASSKEY); 431 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 432 return; 433 } 434 435 private void onRequestPinCode(String objectPath, int nativeData) { 436 String address = checkPairingRequestAndGetAddress(objectPath, nativeData); 437 if (address == null) return; 438 439 String pendingOutgoingAddress = 440 mBluetoothService.getPendingOutgoingBonding(); 441 if (address.equals(pendingOutgoingAddress)) { 442 // we initiated the bonding 443 444 // Check if its a dock 445 if (mBluetoothService.isBluetoothDock(address)) { 446 String pin = mBluetoothService.getDockPin(); 447 mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes(pin)); 448 return; 449 } 450 451 BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address)); 452 453 // try 0000 once if the device looks dumb 454 switch (btClass.getDeviceClass()) { 455 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 456 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 457 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: 458 case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: 459 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 460 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: 461 if (mBluetoothService.attemptAutoPair(address)) return; 462 } 463 } 464 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); 465 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 466 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN); 467 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 468 return; 469 } 470 471 private void onDisplayPasskey(String objectPath, int passkey, int nativeData) { 472 String address = checkPairingRequestAndGetAddress(objectPath, nativeData); 473 if (address == null) return; 474 475 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); 476 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 477 intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey); 478 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, 479 BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY); 480 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 481 } 482 483 private void onRequestOobData(String objectPath , int nativeData) { 484 String address = checkPairingRequestAndGetAddress(objectPath, nativeData); 485 if (address == null) return; 486 487 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); 488 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 489 intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, 490 BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT); 491 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 492 } 493 494 private boolean onAgentAuthorize(String objectPath, String deviceUuid) { 495 String address = mBluetoothService.getAddressFromObjectPath(objectPath); 496 if (address == null) { 497 Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize"); 498 return false; 499 } 500 501 boolean authorized = false; 502 ParcelUuid uuid = ParcelUuid.fromString(deviceUuid); 503 BluetoothA2dp a2dp = new BluetoothA2dp(mContext); 504 505 // Bluez sends the UUID of the local service being accessed, _not_ the 506 // remote service 507 if (mBluetoothService.isEnabled() && 508 (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid) 509 || BluetoothUuid.isAdvAudioDist(uuid)) && 510 !isOtherSinkInNonDisconnectingState(address)) { 511 BluetoothDevice device = mAdapter.getRemoteDevice(address); 512 authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF; 513 if (authorized) { 514 Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address); 515 // Some headsets try to connect AVCTP before AVDTP - against the recommendation 516 // If AVCTP connection fails, we get stuck in IncomingA2DP state in the state 517 // machine. We don't handle AVCTP signals currently. We only send 518 // intents for AVDTP state changes. We need to handle both of them in 519 // some cases. For now, just don't move to incoming state in this case. 520 if (!BluetoothUuid.isAvrcpTarget(uuid)) { 521 mBluetoothService.notifyIncomingA2dpConnection(address); 522 } 523 } else { 524 Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address); 525 } 526 } else { 527 Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address); 528 } 529 log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized); 530 return authorized; 531 } 532 533 private boolean onAgentOutOfBandDataAvailable(String objectPath) { 534 if (!mBluetoothService.isEnabled()) return false; 535 536 String address = mBluetoothService.getAddressFromObjectPath(objectPath); 537 if (address == null) return false; 538 539 if (mBluetoothService.getDeviceOutOfBandData( 540 mAdapter.getRemoteDevice(address)) != null) { 541 return true; 542 } 543 return false; 544 545 } 546 547 private boolean isOtherSinkInNonDisconnectingState(String address) { 548 BluetoothA2dp a2dp = new BluetoothA2dp(mContext); 549 Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks(); 550 if (devices.size() == 0) return false; 551 for(BluetoothDevice dev: devices) { 552 if (!dev.getAddress().equals(address)) return true; 553 } 554 return false; 555 } 556 557 private void onAgentCancel() { 558 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL); 559 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 560 561 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_AGENT_CANCEL), 562 1500); 563 564 return; 565 } 566 567 private void onDiscoverServicesResult(String deviceObjectPath, boolean result) { 568 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); 569 // We don't parse the xml here, instead just query Bluez for the properties. 570 if (result) { 571 mBluetoothService.updateRemoteDevicePropertiesCache(address); 572 } 573 mBluetoothService.sendUuidIntent(address); 574 mBluetoothService.makeServiceChannelCallbacks(address); 575 } 576 577 private void onCreateDeviceResult(String address, int result) { 578 if (DBG) log("Result of onCreateDeviceResult:" + result); 579 580 switch (result) { 581 case CREATE_DEVICE_ALREADY_EXISTS: 582 String path = mBluetoothService.getObjectPathFromAddress(address); 583 if (path != null) { 584 mBluetoothService.discoverServicesNative(path, ""); 585 break; 586 } 587 Log.w(TAG, "Device exists, but we dont have the bluez path, failing"); 588 // fall-through 589 case CREATE_DEVICE_FAILED: 590 mBluetoothService.sendUuidIntent(address); 591 mBluetoothService.makeServiceChannelCallbacks(address); 592 break; 593 case CREATE_DEVICE_SUCCESS: 594 // nothing to do, UUID intent's will be sent via property changed 595 } 596 } 597 598 private void onRestartRequired() { 599 if (mBluetoothService.isEnabled()) { 600 Log.e(TAG, "*** A serious error occurred (did bluetoothd crash?) - " + 601 "restarting Bluetooth ***"); 602 mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH); 603 } 604 } 605 606 private static void log(String msg) { 607 Log.d(TAG, msg); 608 } 609 610 private native void initializeNativeDataNative(); 611 private native void startEventLoopNative(); 612 private native void stopEventLoopNative(); 613 private native boolean isEventLoopRunningNative(); 614 private native void cleanupNativeDataNative(); 615 } 616