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