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 /** 18 * TODO: Move this to 19 * java/services/com/android/server/BluetoothService.java 20 * and make the contructor package private again. 21 * 22 * @hide 23 */ 24 25 package android.server; 26 27 import android.bluetooth.BluetoothAdapter; 28 import android.bluetooth.BluetoothClass; 29 import android.bluetooth.BluetoothDevice; 30 import android.bluetooth.BluetoothHeadset; 31 import android.bluetooth.BluetoothDeviceProfileState; 32 import android.bluetooth.BluetoothProfileState; 33 import android.bluetooth.BluetoothSocket; 34 import android.bluetooth.BluetoothUuid; 35 import android.bluetooth.IBluetooth; 36 import android.bluetooth.IBluetoothCallback; 37 import android.content.BroadcastReceiver; 38 import android.content.ContentResolver; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.SharedPreferences; 43 import android.os.Binder; 44 import android.os.Handler; 45 import android.os.IBinder; 46 import android.os.Message; 47 import android.os.ParcelUuid; 48 import android.os.RemoteException; 49 import android.os.ServiceManager; 50 import android.os.SystemService; 51 import android.provider.Settings; 52 import android.util.Log; 53 import android.util.Pair; 54 55 import com.android.internal.app.IBatteryStats; 56 57 import java.io.BufferedInputStream; 58 import java.io.BufferedReader; 59 import java.io.BufferedWriter; 60 import java.io.DataInputStream; 61 import java.io.File; 62 import java.io.FileDescriptor; 63 import java.io.FileInputStream; 64 import java.io.FileNotFoundException; 65 import java.io.FileOutputStream; 66 import java.io.FileWriter; 67 import java.io.IOException; 68 import java.io.InputStreamReader; 69 import java.io.PrintWriter; 70 import java.io.UnsupportedEncodingException; 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.HashMap; 74 import java.util.Iterator; 75 import java.util.Map; 76 77 public class BluetoothService extends IBluetooth.Stub { 78 private static final String TAG = "BluetoothService"; 79 private static final boolean DBG = true; 80 81 private int mNativeData; 82 private BluetoothEventLoop mEventLoop; 83 private boolean mIsAirplaneSensitive; 84 private boolean mIsAirplaneToggleable; 85 private int mBluetoothState; 86 private boolean mRestart = false; // need to call enable() after disable() 87 private boolean mIsDiscovering; 88 89 private BluetoothAdapter mAdapter; // constant after init() 90 private final BondState mBondState = new BondState(); // local cache of bondings 91 private final IBatteryStats mBatteryStats; 92 private final Context mContext; 93 94 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 95 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 96 97 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr"; 98 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin"; 99 100 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address"; 101 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings"; 102 103 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1; 104 private static final int MESSAGE_FINISH_DISABLE = 2; 105 private static final int MESSAGE_UUID_INTENT = 3; 106 private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4; 107 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 5; 108 109 // The time (in millisecs) to delay the pairing attempt after the first 110 // auto pairing attempt fails. We use an exponential delay with 111 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and 112 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value. 113 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000; 114 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000; 115 116 // The timeout used to sent the UUIDs Intent 117 // This timeout should be greater than the page timeout 118 private static final int UUID_INTENT_DELAY = 6000; 119 120 /** Always retrieve RFCOMM channel for these SDP UUIDs */ 121 private static final ParcelUuid[] RFCOMM_UUIDS = { 122 BluetoothUuid.Handsfree, 123 BluetoothUuid.HSP, 124 BluetoothUuid.ObexObjectPush }; 125 126 // TODO(): Optimize all these string handling 127 private final Map<String, String> mAdapterProperties; 128 private final HashMap<String, Map<String, String>> mDeviceProperties; 129 130 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache; 131 private final ArrayList<String> mUuidIntentTracker; 132 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker; 133 134 private final HashMap<Integer, Integer> mServiceRecordToPid; 135 136 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState; 137 private final BluetoothProfileState mA2dpProfileState; 138 private final BluetoothProfileState mHfpProfileState; 139 140 private BluetoothA2dpService mA2dpService; 141 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData; 142 143 private static String mDockAddress; 144 private String mDockPin; 145 146 private static class RemoteService { 147 public String address; 148 public ParcelUuid uuid; 149 public RemoteService(String address, ParcelUuid uuid) { 150 this.address = address; 151 this.uuid = uuid; 152 } 153 @Override 154 public boolean equals(Object o) { 155 if (o instanceof RemoteService) { 156 RemoteService service = (RemoteService)o; 157 return address.equals(service.address) && uuid.equals(service.uuid); 158 } 159 return false; 160 } 161 162 @Override 163 public int hashCode() { 164 int hash = 1; 165 hash = hash * 31 + (address == null ? 0 : address.hashCode()); 166 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode()); 167 return hash; 168 } 169 } 170 171 static { 172 classInitNative(); 173 } 174 175 public BluetoothService(Context context) { 176 mContext = context; 177 178 // Need to do this in place of: 179 // mBatteryStats = BatteryStatsService.getService(); 180 // Since we can not import BatteryStatsService from here. This class really needs to be 181 // moved to java/services/com/android/server/ 182 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); 183 184 initializeNativeDataNative(); 185 186 if (isEnabledNative() == 1) { 187 Log.w(TAG, "Bluetooth daemons already running - runtime restart? "); 188 disableNative(); 189 } 190 191 mBluetoothState = BluetoothAdapter.STATE_OFF; 192 mIsDiscovering = false; 193 mAdapterProperties = new HashMap<String, String>(); 194 mDeviceProperties = new HashMap<String, Map<String,String>>(); 195 196 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>(); 197 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>(); 198 mUuidIntentTracker = new ArrayList<String>(); 199 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); 200 mServiceRecordToPid = new HashMap<Integer, Integer>(); 201 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>(); 202 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP); 203 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP); 204 205 mHfpProfileState.start(); 206 mA2dpProfileState.start(); 207 208 IntentFilter filter = new IntentFilter(); 209 registerForAirplaneMode(filter); 210 211 filter.addAction(Intent.ACTION_DOCK_EVENT); 212 mContext.registerReceiver(mReceiver, filter); 213 } 214 215 public static synchronized String readDockBluetoothAddress() { 216 if (mDockAddress != null) return mDockAddress; 217 218 BufferedInputStream file = null; 219 String dockAddress; 220 try { 221 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH)); 222 byte[] address = new byte[17]; 223 file.read(address); 224 dockAddress = new String(address); 225 dockAddress = dockAddress.toUpperCase(); 226 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) { 227 mDockAddress = dockAddress; 228 return mDockAddress; 229 } else { 230 log("CheckBluetoothAddress failed for car dock address:" + dockAddress); 231 } 232 } catch (FileNotFoundException e) { 233 log("FileNotFoundException while trying to read dock address"); 234 } catch (IOException e) { 235 log("IOException while trying to read dock address"); 236 } finally { 237 if (file != null) { 238 try { 239 file.close(); 240 } catch (IOException e) { 241 // Ignore 242 } 243 } 244 } 245 mDockAddress = null; 246 return null; 247 } 248 249 private synchronized boolean writeDockPin() { 250 BufferedWriter out = null; 251 try { 252 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH)); 253 254 // Generate a random 4 digit pin between 0000 and 9999 255 // This is not truly random but good enough for our purposes. 256 int pin = (int) Math.floor(Math.random() * 10000); 257 258 mDockPin = String.format("%04d", pin); 259 out.write(mDockPin); 260 return true; 261 } catch (FileNotFoundException e) { 262 log("FileNotFoundException while trying to write dock pairing pin"); 263 } catch (IOException e) { 264 log("IOException while while trying to write dock pairing pin"); 265 } finally { 266 if (out != null) { 267 try { 268 out.close(); 269 } catch (IOException e) { 270 // Ignore 271 } 272 } 273 } 274 mDockPin = null; 275 return false; 276 } 277 278 /*package*/ synchronized String getDockPin() { 279 return mDockPin; 280 } 281 282 public synchronized void initAfterRegistration() { 283 mAdapter = BluetoothAdapter.getDefaultAdapter(); 284 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this); 285 } 286 287 @Override 288 protected void finalize() throws Throwable { 289 mContext.unregisterReceiver(mReceiver); 290 try { 291 cleanupNativeDataNative(); 292 } finally { 293 super.finalize(); 294 } 295 } 296 297 public boolean isEnabled() { 298 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 299 return isEnabledInternal(); 300 } 301 302 private boolean isEnabledInternal() { 303 return mBluetoothState == BluetoothAdapter.STATE_ON; 304 } 305 306 public int getBluetoothState() { 307 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 308 return mBluetoothState; 309 } 310 311 312 /** 313 * Bring down bluetooth and disable BT in settings. Returns true on success. 314 */ 315 public boolean disable() { 316 return disable(true); 317 } 318 319 /** 320 * Bring down bluetooth. Returns true on success. 321 * 322 * @param saveSetting If true, persist the new setting 323 */ 324 public synchronized boolean disable(boolean saveSetting) { 325 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 326 327 switch (mBluetoothState) { 328 case BluetoothAdapter.STATE_OFF: 329 return true; 330 case BluetoothAdapter.STATE_ON: 331 break; 332 default: 333 return false; 334 } 335 if (mEnableThread != null && mEnableThread.isAlive()) { 336 return false; 337 } 338 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF); 339 mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS); 340 341 // Allow 3 seconds for profiles to gracefully disconnect 342 // TODO: Introduce a callback mechanism so that each profile can notify 343 // BluetoothService when it is done shutting down 344 mHandler.sendMessageDelayed( 345 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000); 346 return true; 347 } 348 349 350 private synchronized void finishDisable(boolean saveSetting) { 351 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) { 352 return; 353 } 354 mEventLoop.stop(); 355 tearDownNativeDataNative(); 356 disableNative(); 357 358 // mark in progress bondings as cancelled 359 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) { 360 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 361 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 362 } 363 364 // update mode 365 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); 366 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE); 367 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 368 369 mIsDiscovering = false; 370 mAdapterProperties.clear(); 371 mServiceRecordToPid.clear(); 372 373 if (saveSetting) { 374 persistBluetoothOnSetting(false); 375 } 376 377 setBluetoothState(BluetoothAdapter.STATE_OFF); 378 379 // Log bluetooth off to battery stats. 380 long ident = Binder.clearCallingIdentity(); 381 try { 382 mBatteryStats.noteBluetoothOff(); 383 } catch (RemoteException e) { 384 } finally { 385 Binder.restoreCallingIdentity(ident); 386 } 387 388 if (mRestart) { 389 mRestart = false; 390 enable(); 391 } 392 } 393 394 /** Bring up BT and persist BT on in settings */ 395 public boolean enable() { 396 return enable(true); 397 } 398 399 /** 400 * Enable this Bluetooth device, asynchronously. 401 * This turns on/off the underlying hardware. 402 * 403 * @param saveSetting If true, persist the new state of BT in settings 404 * @return True on success (so far) 405 */ 406 public synchronized boolean enable(boolean saveSetting) { 407 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 408 "Need BLUETOOTH_ADMIN permission"); 409 410 // Airplane mode can prevent Bluetooth radio from being turned on. 411 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { 412 return false; 413 } 414 if (mBluetoothState != BluetoothAdapter.STATE_OFF) { 415 return false; 416 } 417 if (mEnableThread != null && mEnableThread.isAlive()) { 418 return false; 419 } 420 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON); 421 mEnableThread = new EnableThread(saveSetting); 422 mEnableThread.start(); 423 return true; 424 } 425 426 /** Forcibly restart Bluetooth if it is on */ 427 /* package */ synchronized void restart() { 428 if (mBluetoothState != BluetoothAdapter.STATE_ON) { 429 return; 430 } 431 mRestart = true; 432 if (!disable(false)) { 433 mRestart = false; 434 } 435 } 436 437 private synchronized void setBluetoothState(int state) { 438 if (state == mBluetoothState) { 439 return; 440 } 441 442 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state); 443 444 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); 445 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState); 446 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state); 447 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 448 449 mBluetoothState = state; 450 451 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 452 } 453 454 private final Handler mHandler = new Handler() { 455 @Override 456 public void handleMessage(Message msg) { 457 switch (msg.what) { 458 case MESSAGE_REGISTER_SDP_RECORDS: 459 if (!isEnabledInternal()) { 460 return; 461 } 462 // SystemService.start() forks sdptool to register service 463 // records. It can fail to register some records if it is 464 // forked multiple times in a row, probably because there is 465 // some race in sdptool or bluez when operated in parallel. 466 // As a workaround, delay 500ms between each fork of sdptool. 467 // TODO: Don't fork sdptool in order to register service 468 // records, use a DBUS call instead. 469 switch (msg.arg1) { 470 case 1: 471 Log.d(TAG, "Registering hfag record"); 472 SystemService.start("hfag"); 473 mHandler.sendMessageDelayed( 474 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 2, -1), 500); 475 break; 476 case 2: 477 Log.d(TAG, "Registering hsag record"); 478 SystemService.start("hsag"); 479 mHandler.sendMessageDelayed( 480 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 3, -1), 500); 481 break; 482 case 3: 483 Log.d(TAG, "Registering opush record"); 484 SystemService.start("opush"); 485 mHandler.sendMessageDelayed( 486 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 4, -1), 500); 487 break; 488 case 4: 489 Log.d(TAG, "Registering pbap record"); 490 SystemService.start("pbap"); 491 break; 492 } 493 break; 494 case MESSAGE_FINISH_DISABLE: 495 finishDisable(msg.arg1 != 0); 496 break; 497 case MESSAGE_UUID_INTENT: 498 String address = (String)msg.obj; 499 if (address != null) { 500 sendUuidIntent(address); 501 makeServiceChannelCallbacks(address); 502 } 503 break; 504 case MESSAGE_DISCOVERABLE_TIMEOUT: 505 int mode = msg.arg1; 506 if (isEnabledInternal()) { 507 // TODO: Switch back to the previous scan mode 508 // This is ok for now, because we only use 509 // CONNECTABLE and CONNECTABLE_DISCOVERABLE 510 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1); 511 } 512 break; 513 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: 514 address = (String)msg.obj; 515 if (address != null) { 516 createBond(address); 517 return; 518 } 519 break; 520 } 521 } 522 }; 523 524 private EnableThread mEnableThread; 525 526 private class EnableThread extends Thread { 527 private final boolean mSaveSetting; 528 public EnableThread(boolean saveSetting) { 529 mSaveSetting = saveSetting; 530 } 531 public void run() { 532 boolean res = (enableNative() == 0); 533 if (res) { 534 int retryCount = 2; 535 boolean running = false; 536 while ((retryCount-- > 0) && !running) { 537 mEventLoop.start(); 538 // it may take a momement for the other thread to do its 539 // thing. Check periodically for a while. 540 int pollCount = 5; 541 while ((pollCount-- > 0) && !running) { 542 if (mEventLoop.isEventLoopRunning()) { 543 running = true; 544 break; 545 } 546 try { 547 Thread.sleep(100); 548 } catch (InterruptedException e) {} 549 } 550 } 551 if (!running) { 552 log("bt EnableThread giving up"); 553 res = false; 554 disableNative(); 555 } 556 } 557 558 559 if (res) { 560 if (!setupNativeDataNative()) { 561 return; 562 } 563 if (mSaveSetting) { 564 persistBluetoothOnSetting(true); 565 } 566 mIsDiscovering = false; 567 mBondState.readAutoPairingData(); 568 mBondState.loadBondState(); 569 initProfileState(); 570 mHandler.sendMessageDelayed( 571 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000); 572 573 // Log bluetooth on to battery stats. 574 long ident = Binder.clearCallingIdentity(); 575 try { 576 mBatteryStats.noteBluetoothOn(); 577 } catch (RemoteException e) { 578 } finally { 579 Binder.restoreCallingIdentity(ident); 580 } 581 } 582 583 mEnableThread = null; 584 585 setBluetoothState(res ? 586 BluetoothAdapter.STATE_ON : 587 BluetoothAdapter.STATE_OFF); 588 589 if (res) { 590 // Update mode 591 String[] propVal = {"Pairable", getProperty("Pairable")}; 592 mEventLoop.onPropertyChanged(propVal); 593 } 594 595 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { 596 disable(false); 597 } 598 599 } 600 } 601 602 private void persistBluetoothOnSetting(boolean bluetoothOn) { 603 long origCallerIdentityToken = Binder.clearCallingIdentity(); 604 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, 605 bluetoothOn ? 1 : 0); 606 Binder.restoreCallingIdentity(origCallerIdentityToken); 607 } 608 609 /*package*/ synchronized boolean attemptAutoPair(String address) { 610 if (!mBondState.hasAutoPairingFailed(address) && 611 !mBondState.isAutoPairingBlacklisted(address)) { 612 mBondState.attempt(address); 613 setPin(address, BluetoothDevice.convertPinToBytes("0000")); 614 return true; 615 } 616 return false; 617 } 618 619 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) { 620 if (result == BluetoothDevice.BOND_SUCCESS) { 621 setBondState(address, BluetoothDevice.BOND_BONDED); 622 if (mBondState.isAutoPairingAttemptsInProgress(address)) { 623 mBondState.clearPinAttempts(address); 624 } 625 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED && 626 mBondState.getAttempt(address) == 1) { 627 mBondState.addAutoPairingFailure(address); 628 pairingAttempt(address, result); 629 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN && 630 mBondState.isAutoPairingAttemptsInProgress(address)) { 631 pairingAttempt(address, result); 632 } else { 633 setBondState(address, BluetoothDevice.BOND_NONE, result); 634 if (mBondState.isAutoPairingAttemptsInProgress(address)) { 635 mBondState.clearPinAttempts(address); 636 } 637 } 638 } 639 640 /*package*/ synchronized String getPendingOutgoingBonding() { 641 return mBondState.getPendingOutgoingBonding(); 642 } 643 644 private void pairingAttempt(String address, int result) { 645 // This happens when our initial guess of "0000" as the pass key 646 // fails. Try to create the bond again and display the pin dialog 647 // to the user. Use back-off while posting the delayed 648 // message. The initial value is 649 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is 650 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is 651 // reached, display an error to the user. 652 int attempt = mBondState.getAttempt(address); 653 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY > 654 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) { 655 mBondState.clearPinAttempts(address); 656 setBondState(address, BluetoothDevice.BOND_NONE, result); 657 return; 658 } 659 660 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); 661 message.obj = address; 662 boolean postResult = mHandler.sendMessageDelayed(message, 663 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); 664 if (!postResult) { 665 mBondState.clearPinAttempts(address); 666 setBondState(address, 667 BluetoothDevice.BOND_NONE, result); 668 return; 669 } 670 mBondState.attempt(address); 671 } 672 673 /** local cache of bonding state. 674 /* we keep our own state to track the intermediate state BONDING, which 675 /* bluez does not track. 676 * All addresses must be passed in upper case. 677 */ 678 public class BondState { 679 private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); 680 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); 681 682 private static final String AUTO_PAIRING_BLACKLIST = 683 "/etc/bluetooth/auto_pairing.conf"; 684 private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST = 685 "/data/misc/bluetooth/dynamic_auto_pairing.conf"; 686 private ArrayList<String> mAutoPairingAddressBlacklist; 687 private ArrayList<String> mAutoPairingExactNameBlacklist; 688 private ArrayList<String> mAutoPairingPartialNameBlacklist; 689 // Addresses added to blacklist dynamically based on usage. 690 private ArrayList<String> mAutoPairingDynamicAddressBlacklist; 691 692 693 // If this is an outgoing connection, store the address. 694 // There can be only 1 pending outgoing connection at a time, 695 private String mPendingOutgoingBonding; 696 697 private synchronized void setPendingOutgoingBonding(String address) { 698 mPendingOutgoingBonding = address; 699 } 700 701 public synchronized String getPendingOutgoingBonding() { 702 return mPendingOutgoingBonding; 703 } 704 705 public synchronized void loadBondState() { 706 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) { 707 return; 708 } 709 String []bonds = null; 710 String val = getPropertyInternal("Devices"); 711 if (val != null) { 712 bonds = val.split(","); 713 } 714 if (bonds == null) { 715 return; 716 } 717 mState.clear(); 718 if (DBG) log("found " + bonds.length + " bonded devices"); 719 for (String device : bonds) { 720 mState.put(getAddressFromObjectPath(device).toUpperCase(), 721 BluetoothDevice.BOND_BONDED); 722 } 723 } 724 725 public synchronized void setBondState(String address, int state) { 726 setBondState(address, state, 0); 727 } 728 729 /** reason is ignored unless state == BOND_NOT_BONDED */ 730 public synchronized void setBondState(String address, int state, int reason) { 731 int oldState = getBondState(address); 732 if (oldState == state) { 733 return; 734 } 735 736 // Check if this was an pending outgoing bonding. 737 // If yes, reset the state. 738 if (oldState == BluetoothDevice.BOND_BONDING) { 739 if (address.equals(mPendingOutgoingBonding)) { 740 mPendingOutgoingBonding = null; 741 } 742 } 743 744 if (state == BluetoothDevice.BOND_BONDED) { 745 addProfileState(address); 746 } else if (state == BluetoothDevice.BOND_NONE) { 747 removeProfileState(address); 748 } 749 750 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" + 751 reason + ")"); 752 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 753 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 754 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state); 755 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); 756 if (state == BluetoothDevice.BOND_NONE) { 757 if (reason <= 0) { 758 Log.w(TAG, "setBondState() called to unbond device, but reason code is " + 759 "invalid. Overriding reason code with BOND_RESULT_REMOVED"); 760 reason = BluetoothDevice.UNBOND_REASON_REMOVED; 761 } 762 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason); 763 mState.remove(address); 764 } else { 765 mState.put(address, state); 766 } 767 768 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 769 } 770 771 public boolean isAutoPairingBlacklisted(String address) { 772 if (mAutoPairingAddressBlacklist != null) { 773 for (String blacklistAddress : mAutoPairingAddressBlacklist) { 774 if (address.startsWith(blacklistAddress)) return true; 775 } 776 } 777 778 if (mAutoPairingDynamicAddressBlacklist != null) { 779 for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) { 780 if (address.equals(blacklistAddress)) return true; 781 } 782 } 783 String name = getRemoteName(address); 784 if (name != null) { 785 if (mAutoPairingExactNameBlacklist != null) { 786 for (String blacklistName : mAutoPairingExactNameBlacklist) { 787 if (name.equals(blacklistName)) return true; 788 } 789 } 790 791 if (mAutoPairingPartialNameBlacklist != null) { 792 for (String blacklistName : mAutoPairingPartialNameBlacklist) { 793 if (name.startsWith(blacklistName)) return true; 794 } 795 } 796 } 797 return false; 798 } 799 800 public synchronized int getBondState(String address) { 801 Integer state = mState.get(address); 802 if (state == null) { 803 return BluetoothDevice.BOND_NONE; 804 } 805 return state.intValue(); 806 } 807 808 /*package*/ synchronized String[] listInState(int state) { 809 ArrayList<String> result = new ArrayList<String>(mState.size()); 810 for (Map.Entry<String, Integer> e : mState.entrySet()) { 811 if (e.getValue().intValue() == state) { 812 result.add(e.getKey()); 813 } 814 } 815 return result.toArray(new String[result.size()]); 816 } 817 818 public synchronized void addAutoPairingFailure(String address) { 819 if (mAutoPairingDynamicAddressBlacklist == null) { 820 mAutoPairingDynamicAddressBlacklist = new ArrayList<String>(); 821 } 822 823 updateAutoPairingData(address); 824 mAutoPairingDynamicAddressBlacklist.add(address); 825 } 826 827 public synchronized boolean isAutoPairingAttemptsInProgress(String address) { 828 return getAttempt(address) != 0; 829 } 830 831 public synchronized void clearPinAttempts(String address) { 832 mPinAttempt.remove(address); 833 } 834 835 public synchronized boolean hasAutoPairingFailed(String address) { 836 if (mAutoPairingDynamicAddressBlacklist == null) return false; 837 838 return mAutoPairingDynamicAddressBlacklist.contains(address); 839 } 840 841 public synchronized int getAttempt(String address) { 842 Integer attempt = mPinAttempt.get(address); 843 if (attempt == null) { 844 return 0; 845 } 846 return attempt.intValue(); 847 } 848 849 public synchronized void attempt(String address) { 850 Integer attempt = mPinAttempt.get(address); 851 int newAttempt; 852 if (attempt == null) { 853 newAttempt = 1; 854 } else { 855 newAttempt = attempt.intValue() + 1; 856 } 857 mPinAttempt.put(address, new Integer(newAttempt)); 858 } 859 860 private void copyAutoPairingData() { 861 File file = null; 862 FileInputStream in = null; 863 FileOutputStream out = null; 864 try { 865 file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST); 866 if (file.exists()) return; 867 868 in = new FileInputStream(AUTO_PAIRING_BLACKLIST); 869 out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST); 870 871 byte[] buf = new byte[1024]; 872 int len; 873 while ((len = in.read(buf)) > 0) { 874 out.write(buf, 0, len); 875 } 876 } catch (FileNotFoundException e) { 877 log("FileNotFoundException: in copyAutoPairingData"); 878 } catch (IOException e) { 879 log("IOException: in copyAutoPairingData"); 880 } finally { 881 try { 882 if (in != null) in.close(); 883 if (out != null) out.close(); 884 } catch (IOException e) {} 885 } 886 } 887 888 public void readAutoPairingData() { 889 if (mAutoPairingAddressBlacklist != null) return; 890 copyAutoPairingData(); 891 FileInputStream fstream = null; 892 try { 893 fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST); 894 DataInputStream in = new DataInputStream(fstream); 895 BufferedReader file = new BufferedReader(new InputStreamReader(in)); 896 String line; 897 while((line = file.readLine()) != null) { 898 line = line.trim(); 899 if (line.length() == 0 || line.startsWith("//")) continue; 900 String[] value = line.split("="); 901 if (value != null && value.length == 2) { 902 String[] val = value[1].split(","); 903 if (value[0].equalsIgnoreCase("AddressBlacklist")) { 904 mAutoPairingAddressBlacklist = 905 new ArrayList<String>(Arrays.asList(val)); 906 } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) { 907 mAutoPairingExactNameBlacklist = 908 new ArrayList<String>(Arrays.asList(val)); 909 } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) { 910 mAutoPairingPartialNameBlacklist = 911 new ArrayList<String>(Arrays.asList(val)); 912 } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) { 913 mAutoPairingDynamicAddressBlacklist = 914 new ArrayList<String>(Arrays.asList(val)); 915 } else { 916 Log.e(TAG, "Error parsing Auto pairing blacklist file"); 917 } 918 } 919 } 920 } catch (FileNotFoundException e) { 921 log("FileNotFoundException: readAutoPairingData" + e.toString()); 922 } catch (IOException e) { 923 log("IOException: readAutoPairingData" + e.toString()); 924 } finally { 925 if (fstream != null) { 926 try { 927 fstream.close(); 928 } catch (IOException e) { 929 // Ignore 930 } 931 } 932 } 933 } 934 935 // This function adds a bluetooth address to the auto pairing blacklist 936 // file. These addresses are added to DynamicAddressBlacklistSection 937 private void updateAutoPairingData(String address) { 938 BufferedWriter out = null; 939 try { 940 out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true)); 941 StringBuilder str = new StringBuilder(); 942 if (mAutoPairingDynamicAddressBlacklist.size() == 0) { 943 str.append("DynamicAddressBlacklist="); 944 } 945 str.append(address); 946 str.append(","); 947 out.write(str.toString()); 948 } catch (FileNotFoundException e) { 949 log("FileNotFoundException: updateAutoPairingData" + e.toString()); 950 } catch (IOException e) { 951 log("IOException: updateAutoPairingData" + e.toString()); 952 } finally { 953 if (out != null) { 954 try { 955 out.close(); 956 } catch (IOException e) { 957 // Ignore 958 } 959 } 960 } 961 } 962 } 963 964 private static String toBondStateString(int bondState) { 965 switch (bondState) { 966 case BluetoothDevice.BOND_NONE: 967 return "not bonded"; 968 case BluetoothDevice.BOND_BONDING: 969 return "bonding"; 970 case BluetoothDevice.BOND_BONDED: 971 return "bonded"; 972 default: 973 return "??????"; 974 } 975 } 976 977 /*package*/ synchronized boolean isAdapterPropertiesEmpty() { 978 return mAdapterProperties.isEmpty(); 979 } 980 981 /*package*/synchronized void getAllProperties() { 982 983 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 984 mAdapterProperties.clear(); 985 986 String properties[] = (String [])getAdapterPropertiesNative(); 987 // The String Array consists of key-value pairs. 988 if (properties == null) { 989 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL"); 990 return; 991 } 992 993 for (int i = 0; i < properties.length; i++) { 994 String name = properties[i]; 995 String newValue = null; 996 int len; 997 if (name == null) { 998 Log.e(TAG, "Error:Adapter Property at index" + i + "is null"); 999 continue; 1000 } 1001 if (name.equals("Devices") || name.equals("UUIDs")) { 1002 StringBuilder str = new StringBuilder(); 1003 len = Integer.valueOf(properties[++i]); 1004 for (int j = 0; j < len; j++) { 1005 str.append(properties[++i]); 1006 str.append(","); 1007 } 1008 if (len > 0) { 1009 newValue = str.toString(); 1010 } 1011 } else { 1012 newValue = properties[++i]; 1013 } 1014 mAdapterProperties.put(name, newValue); 1015 } 1016 1017 // Add adapter object path property. 1018 String adapterPath = getAdapterPathNative(); 1019 if (adapterPath != null) 1020 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_"); 1021 } 1022 1023 /* package */ synchronized void setProperty(String name, String value) { 1024 mAdapterProperties.put(name, value); 1025 } 1026 1027 public synchronized boolean setName(String name) { 1028 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1029 "Need BLUETOOTH_ADMIN permission"); 1030 if (name == null) { 1031 return false; 1032 } 1033 return setPropertyString("Name", name); 1034 } 1035 1036 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean 1037 // Either have a single property function with Object as the parameter 1038 // or have a function for each property and then obfuscate in the JNI layer. 1039 // The following looks dirty. 1040 private boolean setPropertyString(String key, String value) { 1041 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1042 if (!isEnabledInternal()) return false; 1043 return setAdapterPropertyStringNative(key, value); 1044 } 1045 1046 private boolean setPropertyInteger(String key, int value) { 1047 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1048 if (!isEnabledInternal()) return false; 1049 return setAdapterPropertyIntegerNative(key, value); 1050 } 1051 1052 private boolean setPropertyBoolean(String key, boolean value) { 1053 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1054 if (!isEnabledInternal()) return false; 1055 return setAdapterPropertyBooleanNative(key, value ? 1 : 0); 1056 } 1057 1058 /** 1059 * Set the discoverability window for the device. A timeout of zero 1060 * makes the device permanently discoverable (if the device is 1061 * discoverable). Setting the timeout to a nonzero value does not make 1062 * a device discoverable; you need to call setMode() to make the device 1063 * explicitly discoverable. 1064 * 1065 * @param timeout The discoverable timeout in seconds. 1066 */ 1067 public synchronized boolean setDiscoverableTimeout(int timeout) { 1068 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1069 "Need BLUETOOTH_ADMIN permission"); 1070 return setPropertyInteger("DiscoverableTimeout", timeout); 1071 } 1072 1073 public synchronized boolean setScanMode(int mode, int duration) { 1074 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS, 1075 "Need WRITE_SECURE_SETTINGS permission"); 1076 boolean pairable = false; 1077 boolean discoverable = false; 1078 1079 switch (mode) { 1080 case BluetoothAdapter.SCAN_MODE_NONE: 1081 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT); 1082 pairable = false; 1083 discoverable = false; 1084 break; 1085 case BluetoothAdapter.SCAN_MODE_CONNECTABLE: 1086 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT); 1087 pairable = true; 1088 discoverable = false; 1089 break; 1090 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: 1091 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT); 1092 pairable = true; 1093 discoverable = true; 1094 Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT); 1095 mHandler.sendMessageDelayed(msg, duration * 1000); 1096 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds"); 1097 break; 1098 default: 1099 Log.w(TAG, "Requested invalid scan mode " + mode); 1100 return false; 1101 } 1102 setPropertyBoolean("Pairable", pairable); 1103 setPropertyBoolean("Discoverable", discoverable); 1104 1105 return true; 1106 } 1107 1108 /*package*/ synchronized String getProperty(String name) { 1109 if (!isEnabledInternal()) return null; 1110 return getPropertyInternal(name); 1111 } 1112 1113 /*package*/ synchronized String getPropertyInternal(String name) { 1114 if (!mAdapterProperties.isEmpty()) 1115 return mAdapterProperties.get(name); 1116 getAllProperties(); 1117 return mAdapterProperties.get(name); 1118 } 1119 1120 public synchronized String getAddress() { 1121 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1122 return getProperty("Address"); 1123 } 1124 1125 public synchronized String getName() { 1126 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1127 return getProperty("Name"); 1128 } 1129 1130 /** 1131 * Returns the user-friendly name of a remote device. This value is 1132 * returned from our local cache, which is updated when onPropertyChange 1133 * event is received. 1134 * Do not expect to retrieve the updated remote name immediately after 1135 * changing the name on the remote device. 1136 * 1137 * @param address Bluetooth address of remote device. 1138 * 1139 * @return The user-friendly name of the specified remote device. 1140 */ 1141 public synchronized String getRemoteName(String address) { 1142 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1143 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1144 return null; 1145 } 1146 return getRemoteDeviceProperty(address, "Name"); 1147 } 1148 1149 /** 1150 * Get the discoverability window for the device. A timeout of zero 1151 * means that the device is permanently discoverable (if the device is 1152 * in the discoverable mode). 1153 * 1154 * @return The discoverability window of the device, in seconds. A negative 1155 * value indicates an error. 1156 */ 1157 public synchronized int getDiscoverableTimeout() { 1158 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1159 String timeout = getProperty("DiscoverableTimeout"); 1160 if (timeout != null) 1161 return Integer.valueOf(timeout); 1162 else 1163 return -1; 1164 } 1165 1166 public synchronized int getScanMode() { 1167 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1168 if (!isEnabledInternal()) 1169 return BluetoothAdapter.SCAN_MODE_NONE; 1170 1171 boolean pairable = getProperty("Pairable").equals("true"); 1172 boolean discoverable = getProperty("Discoverable").equals("true"); 1173 return bluezStringToScanMode (pairable, discoverable); 1174 } 1175 1176 public synchronized boolean startDiscovery() { 1177 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1178 "Need BLUETOOTH_ADMIN permission"); 1179 if (!isEnabledInternal()) return false; 1180 1181 return startDiscoveryNative(); 1182 } 1183 1184 public synchronized boolean cancelDiscovery() { 1185 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1186 "Need BLUETOOTH_ADMIN permission"); 1187 if (!isEnabledInternal()) return false; 1188 1189 return stopDiscoveryNative(); 1190 } 1191 1192 public synchronized boolean isDiscovering() { 1193 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1194 return mIsDiscovering; 1195 } 1196 1197 /* package */ void setIsDiscovering(boolean isDiscovering) { 1198 mIsDiscovering = isDiscovering; 1199 } 1200 1201 private boolean isBondingFeasible(String address) { 1202 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1203 "Need BLUETOOTH_ADMIN permission"); 1204 if (!isEnabledInternal()) return false; 1205 1206 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1207 return false; 1208 } 1209 address = address.toUpperCase(); 1210 1211 if (mBondState.getPendingOutgoingBonding() != null) { 1212 log("Ignoring createBond(): another device is bonding"); 1213 // a different device is currently bonding, fail 1214 return false; 1215 } 1216 1217 // Check for bond state only if we are not performing auto 1218 // pairing exponential back-off attempts. 1219 if (!mBondState.isAutoPairingAttemptsInProgress(address) && 1220 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) { 1221 log("Ignoring createBond(): this device is already bonding or bonded"); 1222 return false; 1223 } 1224 1225 if (address.equals(mDockAddress)) { 1226 if (!writeDockPin()) { 1227 log("Error while writing Pin for the dock"); 1228 return false; 1229 } 1230 } 1231 return true; 1232 } 1233 1234 public synchronized boolean createBond(String address) { 1235 if (!isBondingFeasible(address)) return false; 1236 1237 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) { 1238 return false; 1239 } 1240 1241 mBondState.setPendingOutgoingBonding(address); 1242 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); 1243 1244 return true; 1245 } 1246 1247 public synchronized boolean createBondOutOfBand(String address, byte[] hash, 1248 byte[] randomizer) { 1249 if (!isBondingFeasible(address)) return false; 1250 1251 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) { 1252 return false; 1253 } 1254 1255 setDeviceOutOfBandData(address, hash, randomizer); 1256 mBondState.setPendingOutgoingBonding(address); 1257 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); 1258 1259 return true; 1260 } 1261 1262 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash, 1263 byte[] randomizer) { 1264 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1265 "Need BLUETOOTH_ADMIN permission"); 1266 if (!isEnabledInternal()) return false; 1267 1268 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer); 1269 1270 if (DBG) { 1271 log("Setting out of band data for:" + address + ":" + 1272 Arrays.toString(hash) + ":" + Arrays.toString(randomizer)); 1273 } 1274 1275 mDeviceOobData.put(address, value); 1276 return true; 1277 } 1278 1279 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) { 1280 return mDeviceOobData.get(device.getAddress()); 1281 } 1282 1283 1284 public synchronized byte[] readOutOfBandData() { 1285 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 1286 "Need BLUETOOTH permission"); 1287 if (!isEnabledInternal()) return null; 1288 1289 return readAdapterOutOfBandDataNative(); 1290 } 1291 1292 public synchronized boolean cancelBondProcess(String address) { 1293 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1294 "Need BLUETOOTH_ADMIN permission"); 1295 if (!isEnabledInternal()) return false; 1296 1297 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1298 return false; 1299 } 1300 address = address.toUpperCase(); 1301 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) { 1302 return false; 1303 } 1304 1305 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 1306 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 1307 cancelDeviceCreationNative(address); 1308 return true; 1309 } 1310 1311 public synchronized boolean removeBond(String address) { 1312 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1313 "Need BLUETOOTH_ADMIN permission"); 1314 if (!isEnabledInternal()) return false; 1315 1316 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1317 return false; 1318 } 1319 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 1320 if (state != null) { 1321 state.sendMessage(BluetoothDeviceProfileState.UNPAIR); 1322 return true; 1323 } else { 1324 return false; 1325 } 1326 } 1327 1328 public synchronized boolean removeBondInternal(String address) { 1329 return removeDeviceNative(getObjectPathFromAddress(address)); 1330 } 1331 1332 public synchronized String[] listBonds() { 1333 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1334 return mBondState.listInState(BluetoothDevice.BOND_BONDED); 1335 } 1336 1337 /*package*/ synchronized String[] listInState(int state) { 1338 return mBondState.listInState(state); 1339 } 1340 1341 public synchronized int getBondState(String address) { 1342 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1343 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1344 return BluetoothDevice.ERROR; 1345 } 1346 return mBondState.getBondState(address.toUpperCase()); 1347 } 1348 1349 /*package*/ synchronized boolean setBondState(String address, int state) { 1350 return setBondState(address, state, 0); 1351 } 1352 1353 /*package*/ synchronized boolean setBondState(String address, int state, int reason) { 1354 mBondState.setBondState(address.toUpperCase(), state); 1355 return true; 1356 } 1357 1358 public synchronized boolean isBluetoothDock(String address) { 1359 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, 1360 mContext.MODE_PRIVATE); 1361 1362 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address); 1363 } 1364 1365 /*package*/ boolean isRemoteDeviceInCache(String address) { 1366 return (mDeviceProperties.get(address) != null); 1367 } 1368 1369 /*package*/ String[] getRemoteDeviceProperties(String address) { 1370 if (!isEnabledInternal()) return null; 1371 1372 String objectPath = getObjectPathFromAddress(address); 1373 return (String [])getDevicePropertiesNative(objectPath); 1374 } 1375 1376 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) { 1377 Map<String, String> properties = mDeviceProperties.get(address); 1378 if (properties != null) { 1379 return properties.get(property); 1380 } else { 1381 // Query for remote device properties, again. 1382 // We will need to reload the cache when we switch Bluetooth on / off 1383 // or if we crash. 1384 if (updateRemoteDevicePropertiesCache(address)) 1385 return getRemoteDeviceProperty(address, property); 1386 } 1387 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address); 1388 return null; 1389 } 1390 1391 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) { 1392 String[] propValues = getRemoteDeviceProperties(address); 1393 if (propValues != null) { 1394 addRemoteDeviceProperties(address, propValues); 1395 return true; 1396 } 1397 return false; 1398 } 1399 1400 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) { 1401 /* 1402 * We get a DeviceFound signal every time RSSI changes or name changes. 1403 * Don't create a new Map object every time */ 1404 Map<String, String> propertyValues = mDeviceProperties.get(address); 1405 if (propertyValues == null) { 1406 propertyValues = new HashMap<String, String>(); 1407 } 1408 1409 for (int i = 0; i < properties.length; i++) { 1410 String name = properties[i]; 1411 String newValue = null; 1412 int len; 1413 if (name == null) { 1414 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null"); 1415 continue; 1416 } 1417 if (name.equals("UUIDs") || name.equals("Nodes")) { 1418 StringBuilder str = new StringBuilder(); 1419 len = Integer.valueOf(properties[++i]); 1420 for (int j = 0; j < len; j++) { 1421 str.append(properties[++i]); 1422 str.append(","); 1423 } 1424 if (len > 0) { 1425 newValue = str.toString(); 1426 } 1427 } else { 1428 newValue = properties[++i]; 1429 } 1430 1431 propertyValues.put(name, newValue); 1432 } 1433 mDeviceProperties.put(address, propertyValues); 1434 1435 // We have added a new remote device or updated its properties. 1436 // Also update the serviceChannel cache. 1437 updateDeviceServiceChannelCache(address); 1438 } 1439 1440 /* package */ void removeRemoteDeviceProperties(String address) { 1441 mDeviceProperties.remove(address); 1442 } 1443 1444 /* package */ synchronized void setRemoteDeviceProperty(String address, String name, 1445 String value) { 1446 Map <String, String> propVal = mDeviceProperties.get(address); 1447 if (propVal != null) { 1448 propVal.put(name, value); 1449 mDeviceProperties.put(address, propVal); 1450 } else { 1451 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address); 1452 } 1453 } 1454 1455 /** 1456 * Sets the remote device trust state. 1457 * 1458 * @return boolean to indicate operation success or fail 1459 */ 1460 public synchronized boolean setTrust(String address, boolean value) { 1461 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1462 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1463 "Need BLUETOOTH_ADMIN permission"); 1464 return false; 1465 } 1466 1467 if (!isEnabledInternal()) return false; 1468 1469 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted", 1470 value ? 1 : 0); 1471 } 1472 1473 /** 1474 * Gets the remote device trust state as boolean. 1475 * Note: this value may be 1476 * retrieved from cache if we retrieved the data before * 1477 * 1478 * @return boolean to indicate trust or untrust state 1479 */ 1480 public synchronized boolean getTrustState(String address) { 1481 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1482 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1483 return false; 1484 } 1485 1486 String val = getRemoteDeviceProperty(address, "Trusted"); 1487 if (val == null) { 1488 return false; 1489 } else { 1490 return val.equals("true") ? true : false; 1491 } 1492 } 1493 1494 /** 1495 * Gets the remote major, minor classes encoded as a 32-bit 1496 * integer. 1497 * 1498 * Note: this value is retrieved from cache, because we get it during 1499 * remote-device discovery. 1500 * 1501 * @return 32-bit integer encoding the remote major, minor, and service 1502 * classes. 1503 */ 1504 public synchronized int getRemoteClass(String address) { 1505 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1506 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1507 return BluetoothClass.ERROR; 1508 } 1509 String val = getRemoteDeviceProperty(address, "Class"); 1510 if (val == null) 1511 return BluetoothClass.ERROR; 1512 else { 1513 return Integer.valueOf(val); 1514 } 1515 } 1516 1517 1518 /** 1519 * Gets the UUIDs supported by the remote device 1520 * 1521 * @return array of 128bit ParcelUuids 1522 */ 1523 public synchronized ParcelUuid[] getRemoteUuids(String address) { 1524 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1525 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1526 return null; 1527 } 1528 return getUuidFromCache(address); 1529 } 1530 1531 private ParcelUuid[] getUuidFromCache(String address) { 1532 String value = getRemoteDeviceProperty(address, "UUIDs"); 1533 if (value == null) return null; 1534 1535 String[] uuidStrings = null; 1536 // The UUIDs are stored as a "," separated string. 1537 uuidStrings = value.split(","); 1538 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length]; 1539 1540 for (int i = 0; i < uuidStrings.length; i++) { 1541 uuids[i] = ParcelUuid.fromString(uuidStrings[i]); 1542 } 1543 return uuids; 1544 } 1545 1546 /** 1547 * Connect and fetch new UUID's using SDP. 1548 * The UUID's found are broadcast as intents. 1549 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the 1550 * a given uuid. 1551 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success 1552 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for 1553 * callback and broadcast intents. 1554 */ 1555 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid, 1556 IBluetoothCallback callback) { 1557 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1558 if (!isEnabledInternal()) return false; 1559 1560 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1561 return false; 1562 } 1563 1564 RemoteService service = new RemoteService(address, uuid); 1565 if (uuid != null && mUuidCallbackTracker.get(service) != null) { 1566 // An SDP query for this address & uuid is already in progress 1567 // Do not add this callback for the uuid 1568 return false; 1569 } 1570 1571 if (mUuidIntentTracker.contains(address)) { 1572 // An SDP query for this address is already in progress 1573 // Add this uuid onto the in-progress SDP query 1574 if (uuid != null) { 1575 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback); 1576 } 1577 return true; 1578 } 1579 1580 boolean ret; 1581 // Just do the SDP if the device is already created and UUIDs are not 1582 // NULL, else create the device and then do SDP. 1583 if (isRemoteDeviceInCache(address) && getRemoteUuids(address) != null) { 1584 String path = getObjectPathFromAddress(address); 1585 if (path == null) return false; 1586 1587 // Use an empty string for the UUID pattern 1588 ret = discoverServicesNative(path, ""); 1589 } else { 1590 ret = createDeviceNative(address); 1591 } 1592 1593 mUuidIntentTracker.add(address); 1594 if (uuid != null) { 1595 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback); 1596 } 1597 1598 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 1599 message.obj = address; 1600 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY); 1601 return ret; 1602 } 1603 1604 /** 1605 * Gets the rfcomm channel associated with the UUID. 1606 * Pulls records from the cache only. 1607 * 1608 * @param address Address of the remote device 1609 * @param uuid ParcelUuid of the service attribute 1610 * 1611 * @return rfcomm channel associated with the service attribute 1612 * -1 on error 1613 */ 1614 public int getRemoteServiceChannel(String address, ParcelUuid uuid) { 1615 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1616 if (!isEnabledInternal()) return -1; 1617 1618 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1619 return BluetoothDevice.ERROR; 1620 } 1621 // Check if we are recovering from a crash. 1622 if (mDeviceProperties.isEmpty()) { 1623 if (!updateRemoteDevicePropertiesCache(address)) 1624 return -1; 1625 } 1626 1627 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address); 1628 if (value != null && value.containsKey(uuid)) 1629 return value.get(uuid); 1630 return -1; 1631 } 1632 1633 public synchronized boolean setPin(String address, byte[] pin) { 1634 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1635 "Need BLUETOOTH_ADMIN permission"); 1636 if (!isEnabledInternal()) return false; 1637 1638 if (pin == null || pin.length <= 0 || pin.length > 16 || 1639 !BluetoothAdapter.checkBluetoothAddress(address)) { 1640 return false; 1641 } 1642 address = address.toUpperCase(); 1643 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1644 if (data == null) { 1645 Log.w(TAG, "setPin(" + address + ") called but no native data available, " + 1646 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1647 " or by bluez.\n"); 1648 return false; 1649 } 1650 // bluez API wants pin as a string 1651 String pinString; 1652 try { 1653 pinString = new String(pin, "UTF8"); 1654 } catch (UnsupportedEncodingException uee) { 1655 Log.e(TAG, "UTF8 not supported?!?"); 1656 return false; 1657 } 1658 return setPinNative(address, pinString, data.intValue()); 1659 } 1660 1661 public synchronized boolean setPasskey(String address, int passkey) { 1662 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1663 "Need BLUETOOTH_ADMIN permission"); 1664 if (!isEnabledInternal()) return false; 1665 1666 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) { 1667 return false; 1668 } 1669 address = address.toUpperCase(); 1670 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1671 if (data == null) { 1672 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + 1673 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1674 " or by bluez.\n"); 1675 return false; 1676 } 1677 return setPasskeyNative(address, passkey, data.intValue()); 1678 } 1679 1680 public synchronized boolean setPairingConfirmation(String address, boolean confirm) { 1681 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1682 "Need BLUETOOTH_ADMIN permission"); 1683 if (!isEnabledInternal()) return false; 1684 1685 address = address.toUpperCase(); 1686 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1687 if (data == null) { 1688 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + 1689 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1690 " or by bluez.\n"); 1691 return false; 1692 } 1693 return setPairingConfirmationNative(address, confirm, data.intValue()); 1694 } 1695 1696 public synchronized boolean setRemoteOutOfBandData(String address) { 1697 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1698 "Need BLUETOOTH_ADMIN permission"); 1699 if (!isEnabledInternal()) return false; 1700 address = address.toUpperCase(); 1701 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1702 if (data == null) { 1703 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " + 1704 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1705 " or by bluez.\n"); 1706 return false; 1707 } 1708 1709 Pair<byte[], byte[]> val = mDeviceOobData.get(address); 1710 byte[] hash, randomizer; 1711 if (val == null) { 1712 // TODO: check what should be passed in this case. 1713 hash = new byte[16]; 1714 randomizer = new byte[16]; 1715 } else { 1716 hash = val.first; 1717 randomizer = val.second; 1718 } 1719 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue()); 1720 } 1721 1722 public synchronized boolean cancelPairingUserInput(String address) { 1723 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1724 "Need BLUETOOTH_ADMIN permission"); 1725 if (!isEnabledInternal()) return false; 1726 1727 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1728 return false; 1729 } 1730 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 1731 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 1732 address = address.toUpperCase(); 1733 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1734 if (data == null) { 1735 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " + 1736 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " + 1737 "by the remote or by bluez.\n"); 1738 return false; 1739 } 1740 return cancelPairingUserInputNative(address, data.intValue()); 1741 } 1742 1743 /*package*/ void updateDeviceServiceChannelCache(String address) { 1744 ParcelUuid[] deviceUuids = getRemoteUuids(address); 1745 // We are storing the rfcomm channel numbers only for the uuids 1746 // we are interested in. 1747 int channel; 1748 if (DBG) log("updateDeviceServiceChannelCache(" + address + ")"); 1749 1750 ArrayList<ParcelUuid> applicationUuids = new ArrayList(); 1751 1752 synchronized (this) { 1753 for (RemoteService service : mUuidCallbackTracker.keySet()) { 1754 if (service.address.equals(address)) { 1755 applicationUuids.add(service.uuid); 1756 } 1757 } 1758 } 1759 1760 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>(); 1761 1762 // Retrieve RFCOMM channel for default uuids 1763 for (ParcelUuid uuid : RFCOMM_UUIDS) { 1764 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) { 1765 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address), 1766 uuid.toString(), 0x0004); 1767 if (DBG) log("\tuuid(system): " + uuid + " " + channel); 1768 value.put(uuid, channel); 1769 } 1770 } 1771 // Retrieve RFCOMM channel for application requested uuids 1772 for (ParcelUuid uuid : applicationUuids) { 1773 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) { 1774 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address), 1775 uuid.toString(), 0x0004); 1776 if (DBG) log("\tuuid(application): " + uuid + " " + channel); 1777 value.put(uuid, channel); 1778 } 1779 } 1780 1781 synchronized (this) { 1782 // Make application callbacks 1783 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator(); 1784 iter.hasNext();) { 1785 RemoteService service = iter.next(); 1786 if (service.address.equals(address)) { 1787 channel = -1; 1788 if (value.get(service.uuid) != null) { 1789 channel = value.get(service.uuid); 1790 } 1791 if (channel != -1) { 1792 if (DBG) log("Making callback for " + service.uuid + " with result " + 1793 channel); 1794 IBluetoothCallback callback = mUuidCallbackTracker.get(service); 1795 if (callback != null) { 1796 try { 1797 callback.onRfcommChannelFound(channel); 1798 } catch (RemoteException e) {Log.e(TAG, "", e);} 1799 } 1800 1801 iter.remove(); 1802 } 1803 } 1804 } 1805 1806 // Update cache 1807 mDeviceServiceChannelCache.put(address, value); 1808 } 1809 } 1810 1811 /** 1812 * b is a handle to a Binder instance, so that this service can be notified 1813 * for Applications that terminate unexpectedly, to clean there service 1814 * records 1815 */ 1816 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid, 1817 int channel, IBinder b) { 1818 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1819 if (!isEnabledInternal()) return -1; 1820 1821 if (serviceName == null || uuid == null || channel < 1 || 1822 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) { 1823 return -1; 1824 } 1825 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) { 1826 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid); 1827 return -1; 1828 } 1829 int handle = addRfcommServiceRecordNative(serviceName, 1830 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(), 1831 (short)channel); 1832 if (DBG) log("new handle " + Integer.toHexString(handle)); 1833 if (handle == -1) { 1834 return -1; 1835 } 1836 1837 int pid = Binder.getCallingPid(); 1838 mServiceRecordToPid.put(new Integer(handle), new Integer(pid)); 1839 try { 1840 b.linkToDeath(new Reaper(handle, pid), 0); 1841 } catch (RemoteException e) {} 1842 return handle; 1843 } 1844 1845 public void removeServiceRecord(int handle) { 1846 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 1847 "Need BLUETOOTH permission"); 1848 checkAndRemoveRecord(handle, Binder.getCallingPid()); 1849 } 1850 1851 private synchronized void checkAndRemoveRecord(int handle, int pid) { 1852 Integer handleInt = new Integer(handle); 1853 Integer owner = mServiceRecordToPid.get(handleInt); 1854 if (owner != null && pid == owner.intValue()) { 1855 if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " + 1856 pid); 1857 mServiceRecordToPid.remove(handleInt); 1858 removeServiceRecordNative(handle); 1859 } 1860 } 1861 1862 private class Reaper implements IBinder.DeathRecipient { 1863 int pid; 1864 int handle; 1865 Reaper(int handle, int pid) { 1866 this.pid = pid; 1867 this.handle = handle; 1868 } 1869 public void binderDied() { 1870 synchronized (BluetoothService.this) { 1871 if (DBG) log("Tracked app " + pid + " died"); 1872 checkAndRemoveRecord(handle, pid); 1873 } 1874 } 1875 } 1876 1877 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 1878 @Override 1879 public void onReceive(Context context, Intent intent) { 1880 if (intent == null) return; 1881 1882 String action = intent.getAction(); 1883 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 1884 ContentResolver resolver = context.getContentResolver(); 1885 // Query the airplane mode from Settings.System just to make sure that 1886 // some random app is not sending this intent and disabling bluetooth 1887 boolean enabled = !isAirplaneModeOn(); 1888 // If bluetooth is currently expected to be on, then enable or disable bluetooth 1889 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) { 1890 if (enabled) { 1891 enable(false); 1892 } else { 1893 disable(false); 1894 } 1895 } 1896 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) { 1897 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 1898 Intent.EXTRA_DOCK_STATE_UNDOCKED); 1899 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state); 1900 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) { 1901 mDockAddress = null; 1902 mDockPin = null; 1903 } else { 1904 SharedPreferences.Editor editor = 1905 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, 1906 mContext.MODE_PRIVATE).edit(); 1907 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true); 1908 editor.apply(); 1909 } 1910 } 1911 } 1912 }; 1913 1914 private void registerForAirplaneMode(IntentFilter filter) { 1915 final ContentResolver resolver = mContext.getContentResolver(); 1916 final String airplaneModeRadios = Settings.System.getString(resolver, 1917 Settings.System.AIRPLANE_MODE_RADIOS); 1918 final String toggleableRadios = Settings.System.getString(resolver, 1919 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 1920 1921 mIsAirplaneSensitive = airplaneModeRadios == null ? true : 1922 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); 1923 mIsAirplaneToggleable = toggleableRadios == null ? false : 1924 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH); 1925 1926 if (mIsAirplaneSensitive) { 1927 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 1928 } 1929 } 1930 1931 /* Returns true if airplane mode is currently on */ 1932 private final boolean isAirplaneModeOn() { 1933 return Settings.System.getInt(mContext.getContentResolver(), 1934 Settings.System.AIRPLANE_MODE_ON, 0) == 1; 1935 } 1936 1937 /* Broadcast the Uuid intent */ 1938 /*package*/ synchronized void sendUuidIntent(String address) { 1939 ParcelUuid[] uuid = getUuidFromCache(address); 1940 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 1941 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 1942 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid); 1943 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 1944 1945 if (mUuidIntentTracker.contains(address)) 1946 mUuidIntentTracker.remove(address); 1947 1948 } 1949 1950 /*package*/ synchronized void makeServiceChannelCallbacks(String address) { 1951 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator(); 1952 iter.hasNext();) { 1953 RemoteService service = iter.next(); 1954 if (service.address.equals(address)) { 1955 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address + 1956 " " + service.uuid); 1957 IBluetoothCallback callback = mUuidCallbackTracker.get(service); 1958 if (callback != null) { 1959 try { 1960 callback.onRfcommChannelFound(-1); 1961 } catch (RemoteException e) {Log.e(TAG, "", e);} 1962 } 1963 1964 iter.remove(); 1965 } 1966 } 1967 } 1968 1969 @Override 1970 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1971 switch(mBluetoothState) { 1972 case BluetoothAdapter.STATE_OFF: 1973 pw.println("Bluetooth OFF\n"); 1974 return; 1975 case BluetoothAdapter.STATE_TURNING_ON: 1976 pw.println("Bluetooth TURNING ON\n"); 1977 return; 1978 case BluetoothAdapter.STATE_TURNING_OFF: 1979 pw.println("Bluetooth TURNING OFF\n"); 1980 return; 1981 case BluetoothAdapter.STATE_ON: 1982 pw.println("Bluetooth ON\n"); 1983 } 1984 1985 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive); 1986 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable); 1987 1988 pw.println("Local address = " + getAddress()); 1989 pw.println("Local name = " + getName()); 1990 pw.println("isDiscovering() = " + isDiscovering()); 1991 1992 BluetoothHeadset headset = new BluetoothHeadset(mContext, null); 1993 1994 pw.println("\n--Known devices--"); 1995 for (String address : mDeviceProperties.keySet()) { 1996 int bondState = mBondState.getBondState(address); 1997 pw.printf("%s %10s (%d) %s\n", address, 1998 toBondStateString(bondState), 1999 mBondState.getAttempt(address), 2000 getRemoteName(address)); 2001 2002 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address); 2003 if (uuidChannels == null) { 2004 pw.println("\tuuids = null"); 2005 } else { 2006 for (ParcelUuid uuid : uuidChannels.keySet()) { 2007 Integer channel = uuidChannels.get(uuid); 2008 if (channel == null) { 2009 pw.println("\t" + uuid); 2010 } else { 2011 pw.println("\t" + uuid + " RFCOMM channel = " + channel); 2012 } 2013 } 2014 } 2015 for (RemoteService service : mUuidCallbackTracker.keySet()) { 2016 if (service.address.equals(address)) { 2017 pw.println("\tPENDING CALLBACK: " + service.uuid); 2018 } 2019 } 2020 } 2021 2022 String value = getProperty("Devices"); 2023 String[] devicesObjectPath = null; 2024 if (value != null) { 2025 devicesObjectPath = value.split(","); 2026 } 2027 pw.println("\n--ACL connected devices--"); 2028 if (devicesObjectPath != null) { 2029 for (String device : devicesObjectPath) { 2030 pw.println(getAddressFromObjectPath(device)); 2031 } 2032 } 2033 2034 // Rather not do this from here, but no-where else and I need this 2035 // dump 2036 pw.println("\n--Headset Service--"); 2037 switch (headset.getState(headset.getCurrentHeadset())) { 2038 case BluetoothHeadset.STATE_DISCONNECTED: 2039 pw.println("getState() = STATE_DISCONNECTED"); 2040 break; 2041 case BluetoothHeadset.STATE_CONNECTING: 2042 pw.println("getState() = STATE_CONNECTING"); 2043 break; 2044 case BluetoothHeadset.STATE_CONNECTED: 2045 pw.println("getState() = STATE_CONNECTED"); 2046 break; 2047 case BluetoothHeadset.STATE_ERROR: 2048 pw.println("getState() = STATE_ERROR"); 2049 break; 2050 } 2051 2052 pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset()); 2053 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); 2054 headset.close(); 2055 pw.println("\n--Application Service Records--"); 2056 for (Integer handle : mServiceRecordToPid.keySet()) { 2057 Integer pid = mServiceRecordToPid.get(handle); 2058 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); 2059 } 2060 } 2061 2062 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) { 2063 if (pairable && discoverable) 2064 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; 2065 else if (pairable && !discoverable) 2066 return BluetoothAdapter.SCAN_MODE_CONNECTABLE; 2067 else 2068 return BluetoothAdapter.SCAN_MODE_NONE; 2069 } 2070 2071 /* package */ static String scanModeToBluezString(int mode) { 2072 switch (mode) { 2073 case BluetoothAdapter.SCAN_MODE_NONE: 2074 return "off"; 2075 case BluetoothAdapter.SCAN_MODE_CONNECTABLE: 2076 return "connectable"; 2077 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: 2078 return "discoverable"; 2079 } 2080 return null; 2081 } 2082 2083 /*package*/ String getAddressFromObjectPath(String objectPath) { 2084 String adapterObjectPath = getPropertyInternal("ObjectPath"); 2085 if (adapterObjectPath == null || objectPath == null) { 2086 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath + 2087 " or deviceObjectPath:" + objectPath + " is null"); 2088 return null; 2089 } 2090 if (!objectPath.startsWith(adapterObjectPath)) { 2091 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath + 2092 " is not a prefix of deviceObjectPath:" + objectPath + 2093 "bluetoothd crashed ?"); 2094 return null; 2095 } 2096 String address = objectPath.substring(adapterObjectPath.length()); 2097 if (address != null) return address.replace('_', ':'); 2098 2099 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null"); 2100 return null; 2101 } 2102 2103 /*package*/ String getObjectPathFromAddress(String address) { 2104 String path = getPropertyInternal("ObjectPath"); 2105 if (path == null) { 2106 Log.e(TAG, "Error: Object Path is null"); 2107 return null; 2108 } 2109 path = path + address.replace(":", "_"); 2110 return path; 2111 } 2112 2113 /*package */ void setLinkTimeout(String address, int num_slots) { 2114 String path = getObjectPathFromAddress(address); 2115 boolean result = setLinkTimeoutNative(path, num_slots); 2116 2117 if (!result) log("Set Link Timeout to:" + num_slots + " slots failed"); 2118 } 2119 2120 public boolean connectHeadset(String address) { 2121 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2122 if (state != null) { 2123 Message msg = new Message(); 2124 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING; 2125 msg.obj = state; 2126 mHfpProfileState.sendMessage(msg); 2127 return true; 2128 } 2129 return false; 2130 } 2131 2132 public boolean disconnectHeadset(String address) { 2133 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2134 if (state != null) { 2135 Message msg = new Message(); 2136 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING; 2137 msg.obj = state; 2138 mHfpProfileState.sendMessage(msg); 2139 return true; 2140 } 2141 return false; 2142 } 2143 2144 public boolean connectSink(String address) { 2145 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2146 if (state != null) { 2147 Message msg = new Message(); 2148 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING; 2149 msg.obj = state; 2150 mA2dpProfileState.sendMessage(msg); 2151 return true; 2152 } 2153 return false; 2154 } 2155 2156 public boolean disconnectSink(String address) { 2157 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2158 if (state != null) { 2159 Message msg = new Message(); 2160 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING; 2161 msg.obj = state; 2162 mA2dpProfileState.sendMessage(msg); 2163 return true; 2164 } 2165 return false; 2166 } 2167 2168 private BluetoothDeviceProfileState addProfileState(String address) { 2169 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2170 if (state != null) return state; 2171 2172 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService); 2173 mDeviceProfileState.put(address, state); 2174 state.start(); 2175 return state; 2176 } 2177 2178 private void removeProfileState(String address) { 2179 mDeviceProfileState.remove(address); 2180 } 2181 2182 private void initProfileState() { 2183 String []bonds = null; 2184 String val = getPropertyInternal("Devices"); 2185 if (val != null) { 2186 bonds = val.split(","); 2187 } 2188 if (bonds == null) { 2189 return; 2190 } 2191 2192 for (String path : bonds) { 2193 String address = getAddressFromObjectPath(path); 2194 BluetoothDeviceProfileState state = addProfileState(address); 2195 // Allow 8 secs for SDP records to get registered. 2196 Message msg = new Message(); 2197 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES; 2198 state.sendMessageDelayed(msg, 8000); 2199 } 2200 } 2201 2202 public boolean notifyIncomingConnection(String address) { 2203 BluetoothDeviceProfileState state = 2204 mDeviceProfileState.get(address); 2205 if (state != null) { 2206 Message msg = new Message(); 2207 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING; 2208 state.sendMessage(msg); 2209 return true; 2210 } 2211 return false; 2212 } 2213 2214 /*package*/ boolean notifyIncomingA2dpConnection(String address) { 2215 BluetoothDeviceProfileState state = 2216 mDeviceProfileState.get(address); 2217 if (state != null) { 2218 Message msg = new Message(); 2219 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING; 2220 state.sendMessage(msg); 2221 return true; 2222 } 2223 return false; 2224 } 2225 2226 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) { 2227 mA2dpService = a2dpService; 2228 } 2229 2230 public void sendProfileStateMessage(int profile, int cmd) { 2231 Message msg = new Message(); 2232 msg.what = cmd; 2233 if (profile == BluetoothProfileState.HFP) { 2234 mHfpProfileState.sendMessage(msg); 2235 } else if (profile == BluetoothProfileState.A2DP) { 2236 mA2dpProfileState.sendMessage(msg); 2237 } 2238 } 2239 2240 private static void log(String msg) { 2241 Log.d(TAG, msg); 2242 } 2243 2244 private native static void classInitNative(); 2245 private native void initializeNativeDataNative(); 2246 private native boolean setupNativeDataNative(); 2247 private native boolean tearDownNativeDataNative(); 2248 private native void cleanupNativeDataNative(); 2249 private native String getAdapterPathNative(); 2250 2251 private native int isEnabledNative(); 2252 private native int enableNative(); 2253 private native int disableNative(); 2254 2255 private native Object[] getAdapterPropertiesNative(); 2256 private native Object[] getDevicePropertiesNative(String objectPath); 2257 private native boolean setAdapterPropertyStringNative(String key, String value); 2258 private native boolean setAdapterPropertyIntegerNative(String key, int value); 2259 private native boolean setAdapterPropertyBooleanNative(String key, int value); 2260 2261 private native boolean startDiscoveryNative(); 2262 private native boolean stopDiscoveryNative(); 2263 2264 private native boolean createPairedDeviceNative(String address, int timeout_ms); 2265 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms); 2266 private native byte[] readAdapterOutOfBandDataNative(); 2267 2268 private native boolean cancelDeviceCreationNative(String address); 2269 private native boolean removeDeviceNative(String objectPath); 2270 private native int getDeviceServiceChannelNative(String objectPath, String uuid, 2271 int attributeId); 2272 2273 private native boolean cancelPairingUserInputNative(String address, int nativeData); 2274 private native boolean setPinNative(String address, String pin, int nativeData); 2275 private native boolean setPasskeyNative(String address, int passkey, int nativeData); 2276 private native boolean setPairingConfirmationNative(String address, boolean confirm, 2277 int nativeData); 2278 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash, 2279 byte[] randomizer, int nativeData); 2280 2281 private native boolean setDevicePropertyBooleanNative(String objectPath, String key, 2282 int value); 2283 private native boolean createDeviceNative(String address); 2284 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern); 2285 2286 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb, 2287 short channel); 2288 private native boolean removeServiceRecordNative(int handle); 2289 private native boolean setLinkTimeoutNative(String path, int num_slots); 2290 } 2291