BluetoothService.java revision 2d3b98d868cda30535505b2a2fba47aa1c9c052b
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 25package android.server; 26 27import android.bluetooth.BluetoothAdapter; 28import android.bluetooth.BluetoothClass; 29import android.bluetooth.BluetoothDevice; 30import android.bluetooth.BluetoothHeadset; 31import android.bluetooth.BluetoothUuid; 32import android.bluetooth.IBluetooth; 33import android.bluetooth.ParcelUuid; 34import android.content.BroadcastReceiver; 35import android.content.ContentResolver; 36import android.content.Context; 37import android.content.Intent; 38import android.content.IntentFilter; 39import android.os.Binder; 40import android.os.Handler; 41import android.os.Message; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.os.SystemService; 45import android.provider.Settings; 46import android.util.Log; 47 48import com.android.internal.app.IBatteryStats; 49 50import java.io.FileDescriptor; 51import java.io.PrintWriter; 52import java.io.UnsupportedEncodingException; 53import java.util.ArrayList; 54import java.util.Arrays; 55import java.util.HashMap; 56import java.util.Map; 57 58public class BluetoothService extends IBluetooth.Stub { 59 private static final String TAG = "BluetoothService"; 60 private static final boolean DBG = true; 61 62 private int mNativeData; 63 private BluetoothEventLoop mEventLoop; 64 private IntentFilter mIntentFilter; 65 private boolean mIsAirplaneSensitive; 66 private int mBluetoothState; 67 private boolean mRestart = false; // need to call enable() after disable() 68 private boolean mIsDiscovering; 69 70 private BluetoothAdapter mAdapter; // constant after init() 71 private final BondState mBondState = new BondState(); // local cache of bondings 72 private final IBatteryStats mBatteryStats; 73 private final Context mContext; 74 75 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 76 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 77 78 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1; 79 private static final int MESSAGE_FINISH_DISABLE = 2; 80 private static final int MESSAGE_UUID_INTENT = 3; 81 82 // The timeout used to sent the UUIDs Intent 83 // This timeout should be greater than the page timeout 84 private static final int UUID_INTENT_DELAY = 6000; 85 86 private final Map<String, String> mAdapterProperties; 87 private final HashMap <String, Map<String, String>> mDeviceProperties; 88 89 private final HashMap <String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache; 90 private final ArrayList <String> mUuidIntentTracker; 91 92 static { 93 classInitNative(); 94 } 95 96 public BluetoothService(Context context) { 97 mContext = context; 98 99 // Need to do this in place of: 100 // mBatteryStats = BatteryStatsService.getService(); 101 // Since we can not import BatteryStatsService from here. This class really needs to be 102 // moved to java/services/com/android/server/ 103 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); 104 105 initializeNativeDataNative(); 106 107 if (isEnabledNative() == 1) { 108 Log.w(TAG, "Bluetooth daemons already running - runtime restart? "); 109 disableNative(); 110 } 111 112 mBluetoothState = BluetoothAdapter.STATE_OFF; 113 mIsDiscovering = false; 114 mAdapterProperties = new HashMap<String, String>(); 115 mDeviceProperties = new HashMap<String, Map<String,String>>(); 116 117 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>(); 118 mUuidIntentTracker = new ArrayList<String>(); 119 registerForAirplaneMode(); 120 } 121 122 public synchronized void initAfterRegistration() { 123 mAdapter = (BluetoothAdapter) mContext.getSystemService(Context.BLUETOOTH_SERVICE); 124 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this); 125 } 126 127 @Override 128 protected void finalize() throws Throwable { 129 if (mIsAirplaneSensitive) { 130 mContext.unregisterReceiver(mReceiver); 131 } 132 try { 133 cleanupNativeDataNative(); 134 } finally { 135 super.finalize(); 136 } 137 } 138 139 public boolean isEnabled() { 140 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 141 return mBluetoothState == BluetoothAdapter.STATE_ON; 142 } 143 144 public int getBluetoothState() { 145 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 146 return mBluetoothState; 147 } 148 149 150 /** 151 * Bring down bluetooth and disable BT in settings. Returns true on success. 152 */ 153 public boolean disable() { 154 return disable(true); 155 } 156 157 /** 158 * Bring down bluetooth. Returns true on success. 159 * 160 * @param saveSetting If true, disable BT in settings 161 */ 162 public synchronized boolean disable(boolean saveSetting) { 163 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 164 165 switch (mBluetoothState) { 166 case BluetoothAdapter.STATE_OFF: 167 return true; 168 case BluetoothAdapter.STATE_ON: 169 break; 170 default: 171 return false; 172 } 173 if (mEnableThread != null && mEnableThread.isAlive()) { 174 return false; 175 } 176 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF); 177 178 // Allow 3 seconds for profiles to gracefully disconnect 179 // TODO: Introduce a callback mechanism so that each profile can notify 180 // BluetoothService when it is done shutting down 181 mHandler.sendMessageDelayed( 182 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000); 183 return true; 184 } 185 186 187 private synchronized void finishDisable(boolean saveSetting) { 188 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) { 189 return; 190 } 191 mEventLoop.stop(); 192 tearDownNativeDataNative(); 193 disableNative(); 194 195 // mark in progress bondings as cancelled 196 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) { 197 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 198 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 199 } 200 201 // update mode 202 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); 203 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE); 204 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 205 206 mIsDiscovering = false; 207 mAdapterProperties.clear(); 208 209 if (saveSetting) { 210 persistBluetoothOnSetting(false); 211 } 212 213 setBluetoothState(BluetoothAdapter.STATE_OFF); 214 215 // Log bluetooth off to battery stats. 216 long ident = Binder.clearCallingIdentity(); 217 try { 218 mBatteryStats.noteBluetoothOff(); 219 } catch (RemoteException e) { 220 } finally { 221 Binder.restoreCallingIdentity(ident); 222 } 223 224 if (mRestart) { 225 mRestart = false; 226 enable(); 227 } 228 } 229 230 /** Bring up BT and persist BT on in settings */ 231 public boolean enable() { 232 return enable(true); 233 } 234 235 /** 236 * Enable this Bluetooth device, asynchronously. 237 * This turns on/off the underlying hardware. 238 * 239 * @param saveSetting If true, persist the new state of BT in settings 240 * @return True on success (so far) 241 */ 242 public synchronized boolean enable(boolean saveSetting) { 243 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 244 "Need BLUETOOTH_ADMIN permission"); 245 246 // Airplane mode can prevent Bluetooth radio from being turned on. 247 if (mIsAirplaneSensitive && isAirplaneModeOn()) { 248 return false; 249 } 250 if (mBluetoothState != BluetoothAdapter.STATE_OFF) { 251 return false; 252 } 253 if (mEnableThread != null && mEnableThread.isAlive()) { 254 return false; 255 } 256 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON); 257 mEnableThread = new EnableThread(saveSetting); 258 mEnableThread.start(); 259 return true; 260 } 261 262 /** Forcibly restart Bluetooth if it is on */ 263 /* package */ synchronized void restart() { 264 if (mBluetoothState != BluetoothAdapter.STATE_ON) { 265 return; 266 } 267 mRestart = true; 268 if (!disable(false)) { 269 mRestart = false; 270 } 271 } 272 273 private synchronized void setBluetoothState(int state) { 274 if (state == mBluetoothState) { 275 return; 276 } 277 278 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state); 279 280 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); 281 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState); 282 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state); 283 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 284 285 mBluetoothState = state; 286 287 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 288 } 289 290 private final Handler mHandler = new Handler() { 291 @Override 292 public void handleMessage(Message msg) { 293 switch (msg.what) { 294 case MESSAGE_REGISTER_SDP_RECORDS: 295 //TODO: Don't assume HSP/HFP is running, don't use sdptool, 296 if (isEnabled()) { 297 SystemService.start("hsag"); 298 SystemService.start("hfag"); 299 SystemService.start("opush"); 300 SystemService.start("pbap"); 301 } 302 break; 303 case MESSAGE_FINISH_DISABLE: 304 finishDisable(msg.arg1 != 0); 305 break; 306 case MESSAGE_UUID_INTENT: 307 String address = (String)msg.obj; 308 if (address != null) 309 sendUuidIntent(address); 310 break; 311 } 312 } 313 }; 314 315 private EnableThread mEnableThread; 316 317 private class EnableThread extends Thread { 318 private final boolean mSaveSetting; 319 public EnableThread(boolean saveSetting) { 320 mSaveSetting = saveSetting; 321 } 322 public void run() { 323 boolean res = (enableNative() == 0); 324 if (res) { 325 int retryCount = 2; 326 boolean running = false; 327 while ((retryCount-- > 0) && !running) { 328 mEventLoop.start(); 329 // it may take a momement for the other thread to do its 330 // thing. Check periodically for a while. 331 int pollCount = 5; 332 while ((pollCount-- > 0) && !running) { 333 if (mEventLoop.isEventLoopRunning()) { 334 running = true; 335 break; 336 } 337 try { 338 Thread.sleep(100); 339 } catch (InterruptedException e) {} 340 } 341 } 342 if (!running) { 343 log("bt EnableThread giving up"); 344 res = false; 345 disableNative(); 346 } 347 } 348 349 350 if (res) { 351 if (!setupNativeDataNative()) { 352 return; 353 } 354 if (mSaveSetting) { 355 persistBluetoothOnSetting(true); 356 } 357 mIsDiscovering = false; 358 mBondState.loadBondState(); 359 mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS), 360 3000); 361 362 // Log bluetooth on to battery stats. 363 long ident = Binder.clearCallingIdentity(); 364 try { 365 mBatteryStats.noteBluetoothOn(); 366 } catch (RemoteException e) { 367 } finally { 368 Binder.restoreCallingIdentity(ident); 369 } 370 } 371 372 mEnableThread = null; 373 374 setBluetoothState(res ? 375 BluetoothAdapter.STATE_ON : 376 BluetoothAdapter.STATE_OFF); 377 378 if (res) { 379 // Update mode 380 String[] propVal = {"Pairable", getProperty("Pairable")}; 381 mEventLoop.onPropertyChanged(propVal); 382 } 383 384 if (mIsAirplaneSensitive && isAirplaneModeOn()) { 385 disable(false); 386 } 387 388 } 389 } 390 391 private void persistBluetoothOnSetting(boolean bluetoothOn) { 392 long origCallerIdentityToken = Binder.clearCallingIdentity(); 393 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, 394 bluetoothOn ? 1 : 0); 395 Binder.restoreCallingIdentity(origCallerIdentityToken); 396 } 397 398 /* package */ BondState getBondState() { 399 return mBondState; 400 } 401 402 /** local cache of bonding state. 403 /* we keep our own state to track the intermediate state BONDING, which 404 /* bluez does not track. 405 * All addreses must be passed in upper case. 406 */ 407 public class BondState { 408 private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); 409 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); 410 private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>(); 411 // List of all the vendor_id prefix of Bluetooth addresses for 412 // which auto pairing is not attempted. 413 // The following companies are included in the list below: 414 // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi), 415 // Parrot, Zhongshan General K-mate Electronics, Great Well 416 // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi, 417 // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura), 418 // Continental Automotive, Harman/Becker, Panasonic/Kyushu Ten 419 private final ArrayList<String> mAutoPairingAddressBlacklist = 420 new ArrayList<String>(Arrays.asList( 421 "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F", 422 "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8", 423 "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04", 424 "00:16:FD", "00:22:A0", "00:0B:4C", "00:60:6F", "00:23:3D", "00:C0:59", 425 "00:0A:30", "00:1E:AE", "00:1C:D7", "00:80:F0" 426 )); 427 428 // List of names of Bluetooth devices for which auto pairing should be 429 // disabled. 430 private final ArrayList<String> mAutoPairingNameBlacklist = 431 new ArrayList<String>(Arrays.asList( 432 "Motorola IHF1000", "i.TechBlueBAND", "X5 Stereo v1.3")); 433 434 // If this is an outgoing connection, store the address. 435 // There can be only 1 pending outgoing connection at a time, 436 private String mPendingOutgoingBonding; 437 438 private synchronized void setPendingOutgoingBonding(String address) { 439 mPendingOutgoingBonding = address; 440 } 441 442 public synchronized String getPendingOutgoingBonding() { 443 return mPendingOutgoingBonding; 444 } 445 446 public synchronized void loadBondState() { 447 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) { 448 return; 449 } 450 String []bonds = null; 451 String val = getProperty("Devices"); 452 if (val != null) { 453 bonds = val.split(","); 454 } 455 if (bonds == null) { 456 return; 457 } 458 mState.clear(); 459 if (DBG) log("found " + bonds.length + " bonded devices"); 460 for (String device : bonds) { 461 mState.put(getAddressFromObjectPath(device).toUpperCase(), 462 BluetoothDevice.BOND_BONDED); 463 } 464 } 465 466 public synchronized void setBondState(String address, int state) { 467 setBondState(address, state, 0); 468 } 469 470 /** reason is ignored unless state == BOND_NOT_BONDED */ 471 public synchronized void setBondState(String address, int state, int reason) { 472 int oldState = getBondState(address); 473 if (oldState == state) { 474 return; 475 } 476 477 // Check if this was an pending outgoing bonding. 478 // If yes, reset the state. 479 if (oldState == BluetoothDevice.BOND_BONDING) { 480 if (address.equals(mPendingOutgoingBonding)) { 481 mPendingOutgoingBonding = null; 482 } 483 } 484 485 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" + 486 reason + ")"); 487 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 488 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 489 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state); 490 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); 491 if (state == BluetoothDevice.BOND_NONE) { 492 if (reason <= 0) { 493 Log.w(TAG, "setBondState() called to unbond device, but reason code is " + 494 "invalid. Overriding reason code with BOND_RESULT_REMOVED"); 495 reason = BluetoothDevice.UNBOND_REASON_REMOVED; 496 } 497 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason); 498 mState.remove(address); 499 } else { 500 mState.put(address, state); 501 } 502 503 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 504 } 505 506 public boolean isAutoPairingBlacklisted(String address) { 507 for (String blacklistAddress : mAutoPairingAddressBlacklist) { 508 if (address.startsWith(blacklistAddress)) return true; 509 } 510 511 String name = getRemoteName(address); 512 if (name != null) { 513 for (String blacklistName : mAutoPairingNameBlacklist) { 514 if (name.equals(blacklistName)) return true; 515 } 516 } 517 return false; 518 } 519 520 public synchronized int getBondState(String address) { 521 Integer state = mState.get(address); 522 if (state == null) { 523 return BluetoothDevice.BOND_NONE; 524 } 525 return state.intValue(); 526 } 527 528 private synchronized String[] listInState(int state) { 529 ArrayList<String> result = new ArrayList<String>(mState.size()); 530 for (Map.Entry<String, Integer> e : mState.entrySet()) { 531 if (e.getValue().intValue() == state) { 532 result.add(e.getKey()); 533 } 534 } 535 return result.toArray(new String[result.size()]); 536 } 537 538 public synchronized void addAutoPairingFailure(String address) { 539 if (!mAutoPairingFailures.contains(address)) { 540 mAutoPairingFailures.add(address); 541 } 542 } 543 544 public synchronized boolean isAutoPairingAttemptsInProgress(String address) { 545 return getAttempt(address) != 0; 546 } 547 548 public synchronized void clearPinAttempts(String address) { 549 mPinAttempt.remove(address); 550 } 551 552 public synchronized boolean hasAutoPairingFailed(String address) { 553 return mAutoPairingFailures.contains(address); 554 } 555 556 public synchronized int getAttempt(String address) { 557 Integer attempt = mPinAttempt.get(address); 558 if (attempt == null) { 559 return 0; 560 } 561 return attempt.intValue(); 562 } 563 564 public synchronized void attempt(String address) { 565 Integer attempt = mPinAttempt.get(address); 566 int newAttempt; 567 if (attempt == null) { 568 newAttempt = 1; 569 } else { 570 newAttempt = attempt.intValue() + 1; 571 } 572 mPinAttempt.put(address, new Integer(newAttempt)); 573 } 574 575 } 576 577 private static String toBondStateString(int bondState) { 578 switch (bondState) { 579 case BluetoothDevice.BOND_NONE: 580 return "not bonded"; 581 case BluetoothDevice.BOND_BONDING: 582 return "bonding"; 583 case BluetoothDevice.BOND_BONDED: 584 return "bonded"; 585 default: 586 return "??????"; 587 } 588 } 589 590 /*package*/ synchronized boolean isAdapterPropertiesEmpty() { 591 return mAdapterProperties.isEmpty(); 592 } 593 594 /*package*/synchronized void getAllProperties() { 595 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 596 mAdapterProperties.clear(); 597 598 String properties[] = (String [])getAdapterPropertiesNative(); 599 // The String Array consists of key-value pairs. 600 if (properties == null) { 601 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL"); 602 return; 603 } 604 605 for (int i = 0; i < properties.length; i++) { 606 String name = properties[i]; 607 String newValue = null; 608 int len; 609 if (name == null) { 610 Log.e(TAG, "Error:Adapter Property at index" + i + "is null"); 611 continue; 612 } 613 if (name.equals("Devices")) { 614 StringBuilder str = new StringBuilder(); 615 len = Integer.valueOf(properties[++i]); 616 for (int j = 0; j < len; j++) { 617 str.append(properties[++i]); 618 str.append(","); 619 } 620 if (len > 0) { 621 newValue = str.toString(); 622 } 623 } else { 624 newValue = properties[++i]; 625 } 626 mAdapterProperties.put(name, newValue); 627 } 628 629 // Add adapter object path property. 630 String adapterPath = getAdapterPathNative(); 631 if (adapterPath != null) 632 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_"); 633 } 634 635 /* package */ synchronized void setProperty(String name, String value) { 636 mAdapterProperties.put(name, value); 637 } 638 639 public synchronized boolean setName(String name) { 640 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 641 "Need BLUETOOTH_ADMIN permission"); 642 if (name == null) { 643 return false; 644 } 645 return setPropertyString("Name", name); 646 } 647 648 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean 649 // Either have a single property function with Object as the parameter 650 // or have a function for each property and then obfuscate in the JNI layer. 651 // The following looks dirty. 652 private boolean setPropertyString(String key, String value) { 653 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 654 return setAdapterPropertyStringNative(key, value); 655 } 656 657 private boolean setPropertyInteger(String key, int value) { 658 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 659 return setAdapterPropertyIntegerNative(key, value); 660 } 661 662 private boolean setPropertyBoolean(String key, boolean value) { 663 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 664 return setAdapterPropertyBooleanNative(key, value ? 1 : 0); 665 } 666 667 /** 668 * Set the discoverability window for the device. A timeout of zero 669 * makes the device permanently discoverable (if the device is 670 * discoverable). Setting the timeout to a nonzero value does not make 671 * a device discoverable; you need to call setMode() to make the device 672 * explicitly discoverable. 673 * 674 * @param timeout_s The discoverable timeout in seconds. 675 */ 676 public synchronized boolean setDiscoverableTimeout(int timeout) { 677 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 678 "Need BLUETOOTH_ADMIN permission"); 679 return setPropertyInteger("DiscoverableTimeout", timeout); 680 } 681 682 public synchronized boolean setScanMode(int mode) { 683 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 684 "Need BLUETOOTH_ADMIN permission"); 685 boolean pairable = false; 686 boolean discoverable = false; 687 switch (mode) { 688 case BluetoothAdapter.SCAN_MODE_NONE: 689 pairable = false; 690 discoverable = false; 691 break; 692 case BluetoothAdapter.SCAN_MODE_CONNECTABLE: 693 pairable = true; 694 discoverable = false; 695 break; 696 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: 697 pairable = true; 698 discoverable = true; 699 break; 700 default: 701 Log.w(TAG, "Requested invalid scan mode " + mode); 702 return false; 703 } 704 setPropertyBoolean("Pairable", pairable); 705 setPropertyBoolean("Discoverable", discoverable); 706 707 return true; 708 } 709 710 /*package*/ synchronized String getProperty (String name) { 711 if (!mAdapterProperties.isEmpty()) 712 return mAdapterProperties.get(name); 713 getAllProperties(); 714 return mAdapterProperties.get(name); 715 } 716 717 public synchronized String getAddress() { 718 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 719 return getProperty("Address"); 720 } 721 722 public synchronized String getName() { 723 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 724 return getProperty("Name"); 725 } 726 727 /** 728 * Returns the user-friendly name of a remote device. This value is 729 * returned from our local cache, which is updated when onPropertyChange 730 * event is received. 731 * Do not expect to retrieve the updated remote name immediately after 732 * changing the name on the remote device. 733 * 734 * @param address Bluetooth address of remote device. 735 * 736 * @return The user-friendly name of the specified remote device. 737 */ 738 public synchronized String getRemoteName(String address) { 739 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 740 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 741 return null; 742 } 743 Map <String, String> properties = mDeviceProperties.get(address); 744 if (properties != null) return properties.get("Name"); 745 return null; 746 } 747 748 /** 749 * Get the discoverability window for the device. A timeout of zero 750 * means that the device is permanently discoverable (if the device is 751 * in the discoverable mode). 752 * 753 * @return The discoverability window of the device, in seconds. A negative 754 * value indicates an error. 755 */ 756 public synchronized int getDiscoverableTimeout() { 757 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 758 String timeout = getProperty("DiscoverableTimeout"); 759 if (timeout != null) 760 return Integer.valueOf(timeout); 761 else 762 return -1; 763 } 764 765 public synchronized int getScanMode() { 766 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 767 if (!isEnabled()) 768 return BluetoothAdapter.SCAN_MODE_NONE; 769 770 boolean pairable = getProperty("Pairable").equals("true"); 771 boolean discoverable = getProperty("Discoverable").equals("true"); 772 return bluezStringToScanMode (pairable, discoverable); 773 } 774 775 public synchronized boolean startDiscovery() { 776 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 777 "Need BLUETOOTH_ADMIN permission"); 778 if (!isEnabled()) { 779 return false; 780 } 781 return startDiscoveryNative(); 782 } 783 784 public synchronized boolean cancelDiscovery() { 785 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 786 "Need BLUETOOTH_ADMIN permission"); 787 return stopDiscoveryNative(); 788 } 789 790 public synchronized boolean isDiscovering() { 791 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 792 return mIsDiscovering; 793 } 794 795 /* package */ void setIsDiscovering(boolean isDiscovering) { 796 mIsDiscovering = isDiscovering; 797 } 798 799 public synchronized boolean createBond(String address) { 800 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 801 "Need BLUETOOTH_ADMIN permission"); 802 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 803 return false; 804 } 805 address = address.toUpperCase(); 806 807 if (mBondState.getPendingOutgoingBonding() != null) { 808 log("Ignoring createBond(): another device is bonding"); 809 // a different device is currently bonding, fail 810 return false; 811 } 812 813 // Check for bond state only if we are not performing auto 814 // pairing exponential back-off attempts. 815 if (!mBondState.isAutoPairingAttemptsInProgress(address) && 816 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) { 817 log("Ignoring createBond(): this device is already bonding or bonded"); 818 return false; 819 } 820 821 if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) { 822 return false; 823 } 824 825 mBondState.setPendingOutgoingBonding(address); 826 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); 827 828 return true; 829 } 830 831 public synchronized boolean cancelBondProcess(String address) { 832 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 833 "Need BLUETOOTH_ADMIN permission"); 834 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 835 return false; 836 } 837 address = address.toUpperCase(); 838 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) { 839 return false; 840 } 841 842 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 843 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 844 cancelDeviceCreationNative(address); 845 return true; 846 } 847 848 public synchronized boolean removeBond(String address) { 849 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 850 "Need BLUETOOTH_ADMIN permission"); 851 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 852 return false; 853 } 854 return removeDeviceNative(getObjectPathFromAddress(address)); 855 } 856 857 public synchronized String[] listBonds() { 858 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 859 return mBondState.listInState(BluetoothDevice.BOND_BONDED); 860 } 861 862 public synchronized int getBondState(String address) { 863 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 864 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 865 return BluetoothDevice.ERROR; 866 } 867 return mBondState.getBondState(address.toUpperCase()); 868 } 869 870 /*package*/ boolean isRemoteDeviceInCache(String address) { 871 return (mDeviceProperties.get(address) != null); 872 } 873 874 /*package*/ String[] getRemoteDeviceProperties(String address) { 875 String objectPath = getObjectPathFromAddress(address); 876 return (String [])getDevicePropertiesNative(objectPath); 877 } 878 879 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) { 880 Map<String, String> properties = mDeviceProperties.get(address); 881 if (properties != null) { 882 return properties.get(property); 883 } else { 884 // Query for remote device properties, again. 885 // We will need to reload the cache when we switch Bluetooth on / off 886 // or if we crash. 887 if (updateRemoteDevicePropertiesCache(address)) 888 return getRemoteDeviceProperty(address, property); 889 } 890 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address); 891 return null; 892 } 893 894 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) { 895 String[] propValues = getRemoteDeviceProperties(address); 896 if (propValues != null) { 897 addRemoteDeviceProperties(address, propValues); 898 return true; 899 } 900 return false; 901 } 902 903 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) { 904 /* 905 * We get a DeviceFound signal every time RSSI changes or name changes. 906 * Don't create a new Map object every time */ 907 Map<String, String> propertyValues = mDeviceProperties.get(address); 908 if (propertyValues == null) { 909 propertyValues = new HashMap<String, String>(); 910 } 911 912 for (int i = 0; i < properties.length; i++) { 913 String name = properties[i]; 914 String newValue = null; 915 int len; 916 if (name == null) { 917 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null"); 918 continue; 919 } 920 if (name.equals("UUIDs") || name.equals("Nodes")) { 921 StringBuilder str = new StringBuilder(); 922 len = Integer.valueOf(properties[++i]); 923 for (int j = 0; j < len; j++) { 924 str.append(properties[++i]); 925 str.append(","); 926 } 927 if (len > 0) { 928 newValue = str.toString(); 929 } 930 } else { 931 newValue = properties[++i]; 932 } 933 934 propertyValues.put(name, newValue); 935 } 936 mDeviceProperties.put(address, propertyValues); 937 938 // We have added a new remote device or updated its properties. 939 // Also update the serviceChannel cache. 940 updateDeviceServiceChannelCache(address); 941 } 942 943 /* package */ void removeRemoteDeviceProperties(String address) { 944 mDeviceProperties.remove(address); 945 } 946 947 /* package */ synchronized void setRemoteDeviceProperty(String address, String name, 948 String value) { 949 Map <String, String> propVal = mDeviceProperties.get(address); 950 if (propVal != null) { 951 propVal.put(name, value); 952 mDeviceProperties.put(address, propVal); 953 } else { 954 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address); 955 } 956 } 957 958 /** 959 * Sets the remote device trust state. 960 * 961 * @return boolean to indicate operation success or fail 962 */ 963 public synchronized boolean setTrust(String address, boolean value) { 964 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 965 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 966 return false; 967 } 968 969 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted", 970 value ? 1 : 0); 971 } 972 973 /** 974 * Gets the remote device trust state as boolean. 975 * Note: this value may be 976 * retrieved from cache if we retrieved the data before * 977 * 978 * @return boolean to indicate trust or untrust state 979 */ 980 public synchronized boolean getTrustState(String address) { 981 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 982 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 983 return false; 984 } 985 986 String val = getRemoteDeviceProperty(address, "Trusted"); 987 if (val == null) { 988 return false; 989 } else { 990 return val.equals("true") ? true : false; 991 } 992 } 993 994 /** 995 * Gets the remote major, minor classes encoded as a 32-bit 996 * integer. 997 * 998 * Note: this value is retrieved from cache, because we get it during 999 * remote-device discovery. 1000 * 1001 * @return 32-bit integer encoding the remote major, minor, and service 1002 * classes. 1003 */ 1004 public synchronized int getRemoteClass(String address) { 1005 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1006 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1007 return BluetoothClass.ERROR; 1008 } 1009 String val = getRemoteDeviceProperty(address, "Class"); 1010 if (val == null) 1011 return BluetoothClass.ERROR; 1012 else { 1013 return Integer.valueOf(val); 1014 } 1015 } 1016 1017 1018 /** 1019 * Gets the UUIDs supported by the remote device 1020 * 1021 * @return array of 128bit ParcelUuids 1022 */ 1023 public synchronized ParcelUuid[] getRemoteUuids(String address) { 1024 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1025 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1026 return null; 1027 } 1028 return getUuidFromCache(address); 1029 } 1030 1031 private ParcelUuid[] getUuidFromCache(String address) { 1032 String value = getRemoteDeviceProperty(address, "UUIDs"); 1033 if (value == null) return null; 1034 1035 String[] uuidStrings = null; 1036 // The UUIDs are stored as a "," separated string. 1037 uuidStrings = value.split(","); 1038 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length]; 1039 1040 for (int i = 0; i < uuidStrings.length; i++) { 1041 uuids[i] = ParcelUuid.fromString(uuidStrings[i]); 1042 } 1043 return uuids; 1044 } 1045 1046 public synchronized boolean fetchRemoteUuidsWithSdp(String address) { 1047 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1048 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1049 return false; 1050 } 1051 1052 if (mUuidIntentTracker.contains(address)) { 1053 // An SDP query for this address is already in progress 1054 return true; 1055 } 1056 1057 boolean ret; 1058 if (getBondState(address) == BluetoothDevice.BOND_BONDED) { 1059 String path = getObjectPathFromAddress(address); 1060 if (path == null) return false; 1061 1062 // Use an empty string for the UUID pattern 1063 ret = discoverServicesNative(path, ""); 1064 } else { 1065 ret = createDeviceNative(address); 1066 } 1067 1068 mUuidIntentTracker.add(address); 1069 1070 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 1071 message.obj = address; 1072 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY); 1073 return ret; 1074 } 1075 1076 /** 1077 * Gets the rfcomm channel associated with the UUID. 1078 * 1079 * @param address Address of the remote device 1080 * @param uuid ParcelUuid of the service attribute 1081 * 1082 * @return rfcomm channel associated with the service attribute 1083 * -1 on error 1084 */ 1085 public int getRemoteServiceChannel(String address, ParcelUuid uuid) { 1086 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1087 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1088 return BluetoothDevice.ERROR; 1089 } 1090 // Check if we are recovering from a crash. 1091 if (mDeviceProperties.isEmpty()) { 1092 if (!updateRemoteDevicePropertiesCache(address)) 1093 return -1; 1094 } 1095 1096 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address); 1097 if (value != null && value.containsKey(uuid)) 1098 return value.get(uuid); 1099 return -1; 1100 } 1101 1102 public synchronized boolean setPin(String address, byte[] pin) { 1103 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1104 "Need BLUETOOTH_ADMIN permission"); 1105 if (pin == null || pin.length <= 0 || pin.length > 16 || 1106 !BluetoothAdapter.checkBluetoothAddress(address)) { 1107 return false; 1108 } 1109 address = address.toUpperCase(); 1110 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1111 if (data == null) { 1112 Log.w(TAG, "setPin(" + address + ") called but no native data available, " + 1113 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1114 " or by bluez.\n"); 1115 return false; 1116 } 1117 // bluez API wants pin as a string 1118 String pinString; 1119 try { 1120 pinString = new String(pin, "UTF8"); 1121 } catch (UnsupportedEncodingException uee) { 1122 Log.e(TAG, "UTF8 not supported?!?"); 1123 return false; 1124 } 1125 return setPinNative(address, pinString, data.intValue()); 1126 } 1127 1128 public synchronized boolean setPasskey(String address, int passkey) { 1129 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1130 "Need BLUETOOTH_ADMIN permission"); 1131 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) { 1132 return false; 1133 } 1134 address = address.toUpperCase(); 1135 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1136 if (data == null) { 1137 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + 1138 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1139 " or by bluez.\n"); 1140 return false; 1141 } 1142 return setPasskeyNative(address, passkey, data.intValue()); 1143 } 1144 1145 public synchronized boolean setPairingConfirmation(String address, boolean confirm) { 1146 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1147 "Need BLUETOOTH_ADMIN permission"); 1148 address = address.toUpperCase(); 1149 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1150 if (data == null) { 1151 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + 1152 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1153 " or by bluez.\n"); 1154 return false; 1155 } 1156 return setPairingConfirmationNative(address, confirm, data.intValue()); 1157 } 1158 1159 public synchronized boolean cancelPairingUserInput(String address) { 1160 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1161 "Need BLUETOOTH_ADMIN permission"); 1162 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1163 return false; 1164 } 1165 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 1166 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 1167 address = address.toUpperCase(); 1168 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1169 if (data == null) { 1170 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " + 1171 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " + 1172 "by the remote or by bluez.\n"); 1173 return false; 1174 } 1175 return cancelPairingUserInputNative(address, data.intValue()); 1176 } 1177 1178 public void updateDeviceServiceChannelCache(String address) { 1179 ParcelUuid[] deviceUuids = getRemoteUuids(address); 1180 // We are storing the rfcomm channel numbers only for the uuids 1181 // we are interested in. 1182 int channel; 1183 ParcelUuid[] interestedUuids = {BluetoothUuid.Handsfree, 1184 BluetoothUuid.HSP, 1185 BluetoothUuid.ObexObjectPush}; 1186 1187 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>(); 1188 for (ParcelUuid uuid: interestedUuids) { 1189 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) { 1190 channel = 1191 getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(), 1192 0x0004); 1193 value.put(uuid, channel); 1194 } 1195 } 1196 mDeviceServiceChannelCache.put(address, value); 1197 } 1198 1199 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 1200 @Override 1201 public void onReceive(Context context, Intent intent) { 1202 String action = intent.getAction(); 1203 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 1204 ContentResolver resolver = context.getContentResolver(); 1205 // Query the airplane mode from Settings.System just to make sure that 1206 // some random app is not sending this intent and disabling bluetooth 1207 boolean enabled = !isAirplaneModeOn(); 1208 // If bluetooth is currently expected to be on, then enable or disable bluetooth 1209 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) { 1210 if (enabled) { 1211 enable(false); 1212 } else { 1213 disable(false); 1214 } 1215 } 1216 } 1217 } 1218 }; 1219 1220 private void registerForAirplaneMode() { 1221 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(), 1222 Settings.System.AIRPLANE_MODE_RADIOS); 1223 mIsAirplaneSensitive = airplaneModeRadios == null 1224 ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); 1225 if (mIsAirplaneSensitive) { 1226 mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); 1227 mContext.registerReceiver(mReceiver, mIntentFilter); 1228 } 1229 } 1230 1231 /* Returns true if airplane mode is currently on */ 1232 private final boolean isAirplaneModeOn() { 1233 return Settings.System.getInt(mContext.getContentResolver(), 1234 Settings.System.AIRPLANE_MODE_ON, 0) == 1; 1235 } 1236 1237 /* Broadcast the Uuid intent */ 1238 /*package*/ synchronized void sendUuidIntent(String address) { 1239 ParcelUuid[] uuid = getUuidFromCache(address); 1240 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 1241 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 1242 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid); 1243 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 1244 1245 if (mUuidIntentTracker.contains(address)) 1246 mUuidIntentTracker.remove(address); 1247 } 1248 1249 @Override 1250 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1251 pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n"); 1252 1253 switch(mBluetoothState) { 1254 case BluetoothAdapter.STATE_OFF: 1255 pw.println("\nBluetooth OFF\n"); 1256 return; 1257 case BluetoothAdapter.STATE_TURNING_ON: 1258 pw.println("\nBluetooth TURNING ON\n"); 1259 return; 1260 case BluetoothAdapter.STATE_TURNING_OFF: 1261 pw.println("\nBluetooth TURNING OFF\n"); 1262 return; 1263 case BluetoothAdapter.STATE_ON: 1264 pw.println("\nBluetooth ON\n"); 1265 } 1266 1267 pw.println("\nLocal address = " + getAddress()); 1268 pw.println("\nLocal name = " + getName()); 1269 pw.println("\nisDiscovering() = " + isDiscovering()); 1270 1271 BluetoothHeadset headset = new BluetoothHeadset(mContext, null); 1272 1273 pw.println("\n--Known devices--"); 1274 for (String address : mDeviceProperties.keySet()) { 1275 int bondState = mBondState.getBondState(address); 1276 pw.printf("%s %10s (%d) %s\n", address, 1277 toBondStateString(bondState), 1278 mBondState.getAttempt(address), 1279 getRemoteName(address)); 1280 if (bondState == BluetoothDevice.BOND_BONDED) { 1281 ParcelUuid[] uuids = getRemoteUuids(address); 1282 if (uuids == null) { 1283 pw.printf("\tuuids = null\n"); 1284 } else { 1285 for (ParcelUuid uuid : uuids) { 1286 pw.printf("\t" + uuid + "\n"); 1287 } 1288 } 1289 } 1290 } 1291 1292 String value = getProperty("Devices"); 1293 String[] devicesObjectPath = null; 1294 if (value != null) { 1295 devicesObjectPath = value.split(","); 1296 } 1297 pw.println("\n--ACL connected devices--"); 1298 for (String device : devicesObjectPath) { 1299 pw.println(getAddressFromObjectPath(device)); 1300 } 1301 1302 // Rather not do this from here, but no-where else and I need this 1303 // dump 1304 pw.println("\n--Headset Service--"); 1305 switch (headset.getState()) { 1306 case BluetoothHeadset.STATE_DISCONNECTED: 1307 pw.println("getState() = STATE_DISCONNECTED"); 1308 break; 1309 case BluetoothHeadset.STATE_CONNECTING: 1310 pw.println("getState() = STATE_CONNECTING"); 1311 break; 1312 case BluetoothHeadset.STATE_CONNECTED: 1313 pw.println("getState() = STATE_CONNECTED"); 1314 break; 1315 case BluetoothHeadset.STATE_ERROR: 1316 pw.println("getState() = STATE_ERROR"); 1317 break; 1318 } 1319 pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset()); 1320 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); 1321 1322 headset.close(); 1323 } 1324 1325 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) { 1326 if (pairable && discoverable) 1327 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; 1328 else if (pairable && !discoverable) 1329 return BluetoothAdapter.SCAN_MODE_CONNECTABLE; 1330 else 1331 return BluetoothAdapter.SCAN_MODE_NONE; 1332 } 1333 1334 /* package */ static String scanModeToBluezString(int mode) { 1335 switch (mode) { 1336 case BluetoothAdapter.SCAN_MODE_NONE: 1337 return "off"; 1338 case BluetoothAdapter.SCAN_MODE_CONNECTABLE: 1339 return "connectable"; 1340 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: 1341 return "discoverable"; 1342 } 1343 return null; 1344 } 1345 1346 /*package*/ String getAddressFromObjectPath(String objectPath) { 1347 String adapterObjectPath = getProperty("ObjectPath"); 1348 if (adapterObjectPath == null || objectPath == null) { 1349 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath + 1350 " or deviceObjectPath:" + objectPath + " is null"); 1351 return null; 1352 } 1353 if (!objectPath.startsWith(adapterObjectPath)) { 1354 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath + 1355 " is not a prefix of deviceObjectPath:" + objectPath + 1356 "bluetoothd crashed ?"); 1357 return null; 1358 } 1359 String address = objectPath.substring(adapterObjectPath.length()); 1360 if (address != null) return address.replace('_', ':'); 1361 1362 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null"); 1363 return null; 1364 } 1365 1366 /*package*/ String getObjectPathFromAddress(String address) { 1367 String path = getProperty("ObjectPath"); 1368 if (path == null) { 1369 Log.e(TAG, "Error: Object Path is null"); 1370 return null; 1371 } 1372 path = path + address.replace(":", "_"); 1373 return path; 1374 } 1375 1376 private static void log(String msg) { 1377 Log.d(TAG, msg); 1378 } 1379 1380 private native static void classInitNative(); 1381 private native void initializeNativeDataNative(); 1382 private native boolean setupNativeDataNative(); 1383 private native boolean tearDownNativeDataNative(); 1384 private native void cleanupNativeDataNative(); 1385 private native String getAdapterPathNative(); 1386 1387 private native int isEnabledNative(); 1388 private native int enableNative(); 1389 private native int disableNative(); 1390 1391 private native Object[] getAdapterPropertiesNative(); 1392 private native Object[] getDevicePropertiesNative(String objectPath); 1393 private native boolean setAdapterPropertyStringNative(String key, String value); 1394 private native boolean setAdapterPropertyIntegerNative(String key, int value); 1395 private native boolean setAdapterPropertyBooleanNative(String key, int value); 1396 1397 private native boolean startDiscoveryNative(); 1398 private native boolean stopDiscoveryNative(); 1399 1400 private native boolean createPairedDeviceNative(String address, int timeout_ms); 1401 private native boolean cancelDeviceCreationNative(String address); 1402 private native boolean removeDeviceNative(String objectPath); 1403 private native int getDeviceServiceChannelNative(String objectPath, String uuid, 1404 int attributeId); 1405 1406 private native boolean cancelPairingUserInputNative(String address, int nativeData); 1407 private native boolean setPinNative(String address, String pin, int nativeData); 1408 private native boolean setPasskeyNative(String address, int passkey, int nativeData); 1409 private native boolean setPairingConfirmationNative(String address, boolean confirm, 1410 int nativeData); 1411 private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value); 1412 private native boolean createDeviceNative(String address); 1413 private native boolean discoverServicesNative(String objectPath, String pattern); 1414 1415} 1416