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