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