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