1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /** 18 * TODO: Move this to 19 * java/services/com/android/server/BluetoothService.java 20 * and make the contructor package private again. 21 * 22 * @hide 23 */ 24 25 package android.server; 26 27 import android.bluetooth.BluetoothAdapter; 28 import android.bluetooth.BluetoothClass; 29 import android.bluetooth.BluetoothDevice; 30 import android.bluetooth.BluetoothDeviceProfileState; 31 import android.bluetooth.BluetoothHeadset; 32 import android.bluetooth.BluetoothHealthAppConfiguration; 33 import android.bluetooth.BluetoothInputDevice; 34 import android.bluetooth.BluetoothPan; 35 import android.bluetooth.BluetoothProfile; 36 import android.bluetooth.BluetoothProfileState; 37 import android.bluetooth.BluetoothSocket; 38 import android.bluetooth.BluetoothUuid; 39 import android.bluetooth.IBluetooth; 40 import android.bluetooth.IBluetoothCallback; 41 import android.bluetooth.IBluetoothHealthCallback; 42 import android.bluetooth.IBluetoothStateChangeCallback; 43 import android.content.BroadcastReceiver; 44 import android.content.ContentResolver; 45 import android.content.Context; 46 import android.content.Intent; 47 import android.content.IntentFilter; 48 import android.content.SharedPreferences; 49 import android.content.res.Resources; 50 import android.os.Binder; 51 import android.os.Handler; 52 import android.os.IBinder; 53 import android.os.Message; 54 import android.os.ParcelFileDescriptor; 55 import android.os.ParcelUuid; 56 import android.os.RemoteException; 57 import android.os.ServiceManager; 58 import android.provider.Settings; 59 import android.util.Log; 60 import android.util.Pair; 61 62 import com.android.internal.app.IBatteryStats; 63 64 import java.io.BufferedInputStream; 65 import java.io.BufferedReader; 66 import java.io.BufferedWriter; 67 import java.io.DataInputStream; 68 import java.io.File; 69 import java.io.FileDescriptor; 70 import java.io.FileInputStream; 71 import java.io.FileNotFoundException; 72 import java.io.FileWriter; 73 import java.io.IOException; 74 import java.io.InputStreamReader; 75 import java.io.PrintWriter; 76 import java.io.RandomAccessFile; 77 import java.io.UnsupportedEncodingException; 78 import java.util.ArrayList; 79 import java.util.Arrays; 80 import java.util.Collection; 81 import java.util.Collections; 82 import java.util.HashMap; 83 import java.util.Iterator; 84 import java.util.List; 85 import java.util.Map; 86 87 public class BluetoothService extends IBluetooth.Stub { 88 private static final String TAG = "BluetoothService"; 89 private static final boolean DBG = true; 90 91 private int mNativeData; 92 private BluetoothEventLoop mEventLoop; 93 private BluetoothHeadset mHeadsetProxy; 94 private BluetoothInputDevice mInputDevice; 95 private BluetoothPan mPan; 96 private boolean mIsAirplaneSensitive; 97 private boolean mIsAirplaneToggleable; 98 private BluetoothAdapterStateMachine mBluetoothState; 99 private int[] mAdapterSdpHandles; 100 private ParcelUuid[] mAdapterUuids; 101 102 private BluetoothAdapter mAdapter; // constant after init() 103 private final BluetoothBondState mBondState; // local cache of bondings 104 private final IBatteryStats mBatteryStats; 105 private final Context mContext; 106 private Map<Integer, IBluetoothStateChangeCallback> mStateChangeTracker = 107 Collections.synchronizedMap(new HashMap<Integer, IBluetoothStateChangeCallback>()); 108 109 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 110 static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 111 112 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr"; 113 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin"; 114 115 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address"; 116 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings"; 117 118 private static final int MESSAGE_UUID_INTENT = 1; 119 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2; 120 private static final int MESSAGE_REMOVE_SERVICE_RECORD = 3; 121 122 private static final int RFCOMM_RECORD_REAPER = 10; 123 private static final int STATE_CHANGE_REAPER = 11; 124 125 // The time (in millisecs) to delay the pairing attempt after the first 126 // auto pairing attempt fails. We use an exponential delay with 127 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and 128 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value. 129 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000; 130 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000; 131 132 // The timeout used to sent the UUIDs Intent 133 // This timeout should be greater than the page timeout 134 private static final int UUID_INTENT_DELAY = 6000; 135 136 /** Always retrieve RFCOMM channel for these SDP UUIDs */ 137 private static final ParcelUuid[] RFCOMM_UUIDS = { 138 BluetoothUuid.Handsfree, 139 BluetoothUuid.HSP, 140 BluetoothUuid.ObexObjectPush }; 141 142 private final BluetoothAdapterProperties mAdapterProperties; 143 private final BluetoothDeviceProperties mDeviceProperties; 144 145 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache; 146 private final ArrayList<String> mUuidIntentTracker; 147 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker; 148 149 private static class ServiceRecordClient { 150 int pid; 151 IBinder binder; 152 IBinder.DeathRecipient death; 153 } 154 private final HashMap<Integer, ServiceRecordClient> mServiceRecordToPid; 155 156 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState; 157 private final BluetoothProfileState mA2dpProfileState; 158 private final BluetoothProfileState mHfpProfileState; 159 160 private BluetoothA2dpService mA2dpService; 161 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData; 162 163 private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0; 164 165 private static String mDockAddress; 166 private String mDockPin; 167 168 private boolean mAllowConnect = true; 169 170 private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED; 171 private BluetoothPanProfileHandler mBluetoothPanProfileHandler; 172 private BluetoothInputProfileHandler mBluetoothInputProfileHandler; 173 private BluetoothHealthProfileHandler mBluetoothHealthProfileHandler; 174 private static final String INCOMING_CONNECTION_FILE = 175 "/data/misc/bluetooth/incoming_connection.conf"; 176 private HashMap<String, Pair<Integer, String>> mIncomingConnections; 177 private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState; 178 179 private static class RemoteService { 180 public String address; 181 public ParcelUuid uuid; 182 public RemoteService(String address, ParcelUuid uuid) { 183 this.address = address; 184 this.uuid = uuid; 185 } 186 @Override 187 public boolean equals(Object o) { 188 if (o instanceof RemoteService) { 189 RemoteService service = (RemoteService)o; 190 return address.equals(service.address) && uuid.equals(service.uuid); 191 } 192 return false; 193 } 194 195 @Override 196 public int hashCode() { 197 int hash = 1; 198 hash = hash * 31 + (address == null ? 0 : address.hashCode()); 199 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode()); 200 return hash; 201 } 202 } 203 204 static { 205 classInitNative(); 206 } 207 208 public BluetoothService(Context context) { 209 mContext = context; 210 211 // Need to do this in place of: 212 // mBatteryStats = BatteryStatsService.getService(); 213 // Since we can not import BatteryStatsService from here. This class really needs to be 214 // moved to java/services/com/android/server/ 215 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); 216 217 initializeNativeDataNative(); 218 219 if (isEnabledNative() == 1) { 220 Log.w(TAG, "Bluetooth daemons already running - runtime restart? "); 221 disableNative(); 222 } 223 224 mBondState = new BluetoothBondState(context, this); 225 mAdapterProperties = new BluetoothAdapterProperties(context, this); 226 mDeviceProperties = new BluetoothDeviceProperties(this); 227 228 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>(); 229 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>(); 230 mUuidIntentTracker = new ArrayList<String>(); 231 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>(); 232 mServiceRecordToPid = new HashMap<Integer, ServiceRecordClient>(); 233 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>(); 234 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP); 235 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP); 236 237 mHfpProfileState.start(); 238 mA2dpProfileState.start(); 239 240 IntentFilter filter = new IntentFilter(); 241 registerForAirplaneMode(filter); 242 243 filter.addAction(Intent.ACTION_DOCK_EVENT); 244 mContext.registerReceiver(mReceiver, filter); 245 mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this); 246 mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this); 247 mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this); 248 mIncomingConnections = new HashMap<String, Pair<Integer, String>>(); 249 mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>(); 250 } 251 252 public static synchronized String readDockBluetoothAddress() { 253 if (mDockAddress != null) return mDockAddress; 254 255 BufferedInputStream file = null; 256 String dockAddress; 257 try { 258 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH)); 259 byte[] address = new byte[17]; 260 file.read(address); 261 dockAddress = new String(address); 262 dockAddress = dockAddress.toUpperCase(); 263 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) { 264 mDockAddress = dockAddress; 265 return mDockAddress; 266 } else { 267 Log.e(TAG, "CheckBluetoothAddress failed for car dock address: " 268 + dockAddress); 269 } 270 } catch (FileNotFoundException e) { 271 Log.e(TAG, "FileNotFoundException while trying to read dock address"); 272 } catch (IOException e) { 273 Log.e(TAG, "IOException while trying to read dock address"); 274 } finally { 275 if (file != null) { 276 try { 277 file.close(); 278 } catch (IOException e) { 279 // Ignore 280 } 281 } 282 } 283 mDockAddress = null; 284 return null; 285 } 286 287 private synchronized boolean writeDockPin() { 288 BufferedWriter out = null; 289 try { 290 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH)); 291 292 // Generate a random 4 digit pin between 0000 and 9999 293 // This is not truly random but good enough for our purposes. 294 int pin = (int) Math.floor(Math.random() * 10000); 295 296 mDockPin = String.format("%04d", pin); 297 out.write(mDockPin); 298 return true; 299 } catch (FileNotFoundException e) { 300 Log.e(TAG, "FileNotFoundException while trying to write dock pairing pin"); 301 } catch (IOException e) { 302 Log.e(TAG, "IOException while while trying to write dock pairing pin"); 303 } finally { 304 if (out != null) { 305 try { 306 out.close(); 307 } catch (IOException e) { 308 // Ignore 309 } 310 } 311 } 312 mDockPin = null; 313 return false; 314 } 315 316 /*package*/ synchronized String getDockPin() { 317 return mDockPin; 318 } 319 320 public synchronized void initAfterRegistration() { 321 mAdapter = BluetoothAdapter.getDefaultAdapter(); 322 mBluetoothState = new BluetoothAdapterStateMachine(mContext, this, mAdapter); 323 mBluetoothState.start(); 324 if (mContext.getResources().getBoolean 325 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { 326 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.TURN_HOT); 327 } 328 mEventLoop = mBluetoothState.getBluetoothEventLoop(); 329 } 330 331 public synchronized void initAfterA2dpRegistration() { 332 mEventLoop.getProfileProxy(); 333 } 334 335 @Override 336 protected void finalize() throws Throwable { 337 mContext.unregisterReceiver(mReceiver); 338 try { 339 cleanupNativeDataNative(); 340 } finally { 341 super.finalize(); 342 } 343 } 344 345 public boolean isEnabled() { 346 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 347 return isEnabledInternal(); 348 } 349 350 private boolean isEnabledInternal() { 351 return (getBluetoothStateInternal() == BluetoothAdapter.STATE_ON); 352 } 353 354 public int getBluetoothState() { 355 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 356 return getBluetoothStateInternal(); 357 } 358 359 int getBluetoothStateInternal() { 360 return mBluetoothState.getBluetoothAdapterState(); 361 } 362 363 /** 364 * Bring down bluetooth and disable BT in settings. Returns true on success. 365 */ 366 public boolean disable() { 367 return disable(true); 368 } 369 370 /** 371 * Bring down bluetooth. Returns true on success. 372 * 373 * @param saveSetting If true, persist the new setting 374 */ 375 public synchronized boolean disable(boolean saveSetting) { 376 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 377 378 int adapterState = getBluetoothStateInternal(); 379 380 switch (adapterState) { 381 case BluetoothAdapter.STATE_OFF: 382 return true; 383 case BluetoothAdapter.STATE_ON: 384 break; 385 default: 386 return false; 387 } 388 389 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_OFF, saveSetting); 390 return true; 391 } 392 393 synchronized void disconnectDevices() { 394 // Disconnect devices handled by BluetoothService. 395 for (BluetoothDevice device: getConnectedInputDevices()) { 396 disconnectInputDevice(device); 397 } 398 399 for (BluetoothDevice device: getConnectedPanDevices()) { 400 disconnectPanDevice(device); 401 } 402 } 403 404 /** 405 * The Bluetooth has been turned off, but hot. Do bonding, profile cleanup 406 */ 407 synchronized void finishDisable() { 408 // mark in progress bondings as cancelled 409 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) { 410 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 411 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 412 } 413 414 // Stop the profile state machine for bonded devices. 415 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDED)) { 416 removeProfileState(address); 417 } 418 419 // update mode 420 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); 421 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE); 422 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 423 } 424 425 /** 426 * Local clean up after broadcasting STATE_OFF intent 427 */ 428 synchronized void cleanupAfterFinishDisable() { 429 mAdapterProperties.clear(); 430 431 for (Integer srHandle : mServiceRecordToPid.keySet()) { 432 removeServiceRecordNative(srHandle); 433 } 434 mServiceRecordToPid.clear(); 435 436 mProfilesConnected = 0; 437 mProfilesConnecting = 0; 438 mProfilesDisconnecting = 0; 439 mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED; 440 mAdapterUuids = null; 441 mAdapterSdpHandles = null; 442 443 // Log bluetooth off to battery stats. 444 long ident = Binder.clearCallingIdentity(); 445 try { 446 mBatteryStats.noteBluetoothOff(); 447 } catch (RemoteException e) { 448 } finally { 449 Binder.restoreCallingIdentity(ident); 450 } 451 } 452 453 /** 454 * power off Bluetooth 455 */ 456 synchronized void shutoffBluetooth() { 457 if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles); 458 setBluetoothTetheringNative(false, BluetoothPanProfileHandler.NAP_ROLE, 459 BluetoothPanProfileHandler.NAP_BRIDGE); 460 tearDownNativeDataNative(); 461 } 462 463 /** 464 * Data clean up after Bluetooth shutoff 465 */ 466 synchronized void cleanNativeAfterShutoffBluetooth() { 467 // Ths method is called after shutdown of event loop in the Bluetooth shut down 468 // procedure 469 470 // the adapter property could be changed before event loop is stoped, clear it again 471 mAdapterProperties.clear(); 472 disableNative(); 473 } 474 475 /** Bring up BT and persist BT on in settings */ 476 public boolean enable() { 477 return enable(true, true); 478 } 479 480 /** 481 * Enable this Bluetooth device, asynchronously. 482 * This turns on/off the underlying hardware. 483 * 484 * @param saveSetting If true, persist the new state of BT in settings 485 * @param allowConnect If true, auto-connects device when BT is turned on 486 * and allows incoming A2DP/HSP connections 487 * @return True on success (so far) 488 */ 489 public synchronized boolean enable(boolean saveSetting, boolean allowConnect) { 490 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 491 "Need BLUETOOTH_ADMIN permission"); 492 493 // Airplane mode can prevent Bluetooth radio from being turned on. 494 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { 495 return false; 496 } 497 mAllowConnect = allowConnect; 498 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting); 499 return true; 500 } 501 502 /** 503 * Enable this Bluetooth device, asynchronously, but does not 504 * auto-connect devices. In this state the Bluetooth adapter 505 * also does not allow incoming A2DP/HSP connections (that 506 * must go through this service), but does allow communication 507 * on RFCOMM sockets implemented outside of this service (ie BTOPP). 508 * This method is used to temporarily enable Bluetooth 509 * for data transfer, without changing 510 * 511 * This turns on/off the underlying hardware. 512 * 513 * @return True on success (so far) 514 */ 515 public boolean enableNoAutoConnect() { 516 return enable(false, false); 517 } 518 519 /** 520 * Turn on Bluetooth Module, Load firmware, and do all the preparation 521 * needed to get the Bluetooth Module ready but keep it not discoverable 522 * and not connectable. 523 */ 524 /* package */ synchronized boolean prepareBluetooth() { 525 if (!setupNativeDataNative()) { 526 return false; 527 } 528 switchConnectable(false); 529 530 // Bluetooth stack needs a small delay here before adding 531 // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs 532 try { 533 Thread.sleep(50); 534 } catch (InterruptedException e) {} 535 updateSdpRecords(); 536 return true; 537 } 538 539 private final Handler mHandler = new Handler() { 540 @Override 541 public void handleMessage(Message msg) { 542 switch (msg.what) { 543 case MESSAGE_UUID_INTENT: 544 String address = (String)msg.obj; 545 if (address != null) { 546 sendUuidIntent(address); 547 makeServiceChannelCallbacks(address); 548 } 549 break; 550 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: 551 address = (String)msg.obj; 552 if (address == null) return; 553 int attempt = mBondState.getAttempt(address); 554 555 // Try only if attemps are in progress and cap it 2 attempts 556 // The 2 attempts cap is a fail safe if the stack returns 557 // an incorrect error code for bonding failures and if the pin 558 // is entered wrongly twice we should abort. 559 if (attempt > 0 && attempt <= 2) { 560 mBondState.attempt(address); 561 createBond(address); 562 return; 563 } 564 if (attempt > 0) mBondState.clearPinAttempts(address); 565 break; 566 case MESSAGE_REMOVE_SERVICE_RECORD: 567 Pair<Integer, Integer> pair = (Pair<Integer, Integer>) msg.obj; 568 checkAndRemoveRecord(pair.first, pair.second); 569 break; 570 } 571 } 572 }; 573 574 private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) { 575 //Register SDP records. 576 int[] svcIdentifiers = new int[uuids.size()]; 577 for (int i = 0; i < uuids.size(); i++) { 578 svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i)); 579 } 580 mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers); 581 } 582 583 private synchronized void updateSdpRecords() { 584 ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(); 585 586 Resources R = mContext.getResources(); 587 588 // Add the default records 589 if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) { 590 uuids.add(BluetoothUuid.HSP_AG); 591 uuids.add(BluetoothUuid.ObexObjectPush); 592 } 593 594 if (R.getBoolean(com.android.internal.R.bool.config_voice_capable)) { 595 uuids.add(BluetoothUuid.Handsfree_AG); 596 uuids.add(BluetoothUuid.PBAP_PSE); 597 } 598 599 // Add SDP records for profiles maintained by Android userspace 600 addReservedSdpRecords(uuids); 601 602 // Bluetooth stack need some a small delay here before adding more 603 // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs 604 try { 605 Thread.sleep(50); 606 } catch (InterruptedException e) {} 607 608 if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) { 609 // Enable profiles maintained by Bluez userspace. 610 setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE, 611 BluetoothPanProfileHandler.NAP_BRIDGE); 612 613 // Add SDP records for profiles maintained by Bluez userspace 614 uuids.add(BluetoothUuid.AudioSource); 615 uuids.add(BluetoothUuid.AvrcpTarget); 616 uuids.add(BluetoothUuid.NAP); 617 } 618 619 // Cannot cast uuids.toArray directly since ParcelUuid is parcelable 620 mAdapterUuids = new ParcelUuid[uuids.size()]; 621 for (int i = 0; i < uuids.size(); i++) { 622 mAdapterUuids[i] = uuids.get(i); 623 } 624 } 625 626 /** 627 * This function is called from Bluetooth Event Loop when onPropertyChanged 628 * for adapter comes in with UUID property. 629 * @param uuidsThe uuids of adapter as reported by Bluez. 630 */ 631 /*package*/ synchronized void updateBluetoothState(String uuids) { 632 ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids); 633 634 if (mAdapterUuids != null && 635 BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) { 636 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SERVICE_RECORD_LOADED); 637 } 638 } 639 640 /** 641 * This method is called immediately before Bluetooth module is turned on after 642 * the adapter became pariable. 643 * It inits bond state and profile state before STATE_ON intent is broadcasted. 644 */ 645 /*package*/ void initBluetoothAfterTurningOn() { 646 String discoverable = getProperty("Discoverable", false); 647 String timeout = getProperty("DiscoverableTimeout", false); 648 if (timeout == null) { 649 Log.w(TAG, "Null DiscoverableTimeout property"); 650 // assign a number, anything not 0 651 timeout = "1"; 652 } 653 if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) { 654 setAdapterPropertyBooleanNative("Discoverable", 0); 655 } 656 mBondState.initBondState(); 657 initProfileState(); 658 getProfileProxy(); 659 } 660 661 /** 662 * This method is called immediately after Bluetooth module is turned on. 663 * It starts auto-connection and places bluetooth on sign onto the battery 664 * stats 665 */ 666 /*package*/ void runBluetooth() { 667 autoConnect(); 668 669 // Log bluetooth on to battery stats. 670 long ident = Binder.clearCallingIdentity(); 671 try { 672 mBatteryStats.noteBluetoothOn(); 673 } catch (RemoteException e) { 674 Log.e(TAG, "", e); 675 } finally { 676 Binder.restoreCallingIdentity(ident); 677 } 678 } 679 680 /*package*/ synchronized boolean attemptAutoPair(String address) { 681 if (!mBondState.hasAutoPairingFailed(address) && 682 !mBondState.isAutoPairingBlacklisted(address)) { 683 mBondState.attempt(address); 684 setPin(address, BluetoothDevice.convertPinToBytes("0000")); 685 return true; 686 } 687 return false; 688 } 689 690 /*package*/ synchronized boolean isFixedPinZerosAutoPairKeyboard(String address) { 691 // Check for keyboards which have fixed PIN 0000 as the pairing pin 692 return mBondState.isFixedPinZerosAutoPairKeyboard(address); 693 } 694 695 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) { 696 if (result == BluetoothDevice.BOND_SUCCESS) { 697 setBondState(address, BluetoothDevice.BOND_BONDED); 698 if (mBondState.isAutoPairingAttemptsInProgress(address)) { 699 mBondState.clearPinAttempts(address); 700 } 701 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED && 702 mBondState.getAttempt(address) == 1) { 703 mBondState.addAutoPairingFailure(address); 704 pairingAttempt(address, result); 705 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN && 706 mBondState.isAutoPairingAttemptsInProgress(address)) { 707 pairingAttempt(address, result); 708 } else { 709 setBondState(address, BluetoothDevice.BOND_NONE, result); 710 if (mBondState.isAutoPairingAttemptsInProgress(address)) { 711 mBondState.clearPinAttempts(address); 712 } 713 } 714 } 715 716 /*package*/ synchronized String getPendingOutgoingBonding() { 717 return mBondState.getPendingOutgoingBonding(); 718 } 719 720 private void pairingAttempt(String address, int result) { 721 // This happens when our initial guess of "0000" as the pass key 722 // fails. Try to create the bond again and display the pin dialog 723 // to the user. Use back-off while posting the delayed 724 // message. The initial value is 725 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is 726 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is 727 // reached, display an error to the user. 728 int attempt = mBondState.getAttempt(address); 729 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY > 730 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) { 731 mBondState.clearPinAttempts(address); 732 setBondState(address, BluetoothDevice.BOND_NONE, result); 733 return; 734 } 735 736 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); 737 message.obj = address; 738 boolean postResult = mHandler.sendMessageDelayed(message, 739 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); 740 if (!postResult) { 741 mBondState.clearPinAttempts(address); 742 setBondState(address, 743 BluetoothDevice.BOND_NONE, result); 744 return; 745 } 746 } 747 748 /*package*/ BluetoothDevice getRemoteDevice(String address) { 749 return mAdapter.getRemoteDevice(address); 750 } 751 752 private static String toBondStateString(int bondState) { 753 switch (bondState) { 754 case BluetoothDevice.BOND_NONE: 755 return "not bonded"; 756 case BluetoothDevice.BOND_BONDING: 757 return "bonding"; 758 case BluetoothDevice.BOND_BONDED: 759 return "bonded"; 760 default: 761 return "??????"; 762 } 763 } 764 765 public synchronized boolean setName(String name) { 766 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 767 "Need BLUETOOTH_ADMIN permission"); 768 if (name == null) { 769 return false; 770 } 771 return setPropertyString("Name", name); 772 } 773 774 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean 775 // Either have a single property function with Object as the parameter 776 // or have a function for each property and then obfuscate in the JNI layer. 777 // The following looks dirty. 778 private boolean setPropertyString(String key, String value) { 779 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 780 if (!isEnabledInternal()) return false; 781 return setAdapterPropertyStringNative(key, value); 782 } 783 784 private boolean setPropertyInteger(String key, int value) { 785 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 786 if (!isEnabledInternal()) return false; 787 return setAdapterPropertyIntegerNative(key, value); 788 } 789 790 private boolean setPropertyBoolean(String key, boolean value) { 791 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 792 if (!isEnabledInternal()) return false; 793 return setAdapterPropertyBooleanNative(key, value ? 1 : 0); 794 } 795 796 /** 797 * Set the discoverability window for the device. A timeout of zero 798 * makes the device permanently discoverable (if the device is 799 * discoverable). Setting the timeout to a nonzero value does not make 800 * a device discoverable; you need to call setMode() to make the device 801 * explicitly discoverable. 802 * 803 * @param timeout The discoverable timeout in seconds. 804 */ 805 public synchronized boolean setDiscoverableTimeout(int timeout) { 806 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 807 "Need BLUETOOTH_ADMIN permission"); 808 return setPropertyInteger("DiscoverableTimeout", timeout); 809 } 810 811 public synchronized boolean setScanMode(int mode, int duration) { 812 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS, 813 "Need WRITE_SECURE_SETTINGS permission"); 814 boolean pairable; 815 boolean discoverable; 816 817 switch (mode) { 818 case BluetoothAdapter.SCAN_MODE_NONE: 819 pairable = false; 820 discoverable = false; 821 break; 822 case BluetoothAdapter.SCAN_MODE_CONNECTABLE: 823 pairable = true; 824 discoverable = false; 825 break; 826 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: 827 pairable = true; 828 discoverable = true; 829 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds"); 830 break; 831 default: 832 Log.w(TAG, "Requested invalid scan mode " + mode); 833 return false; 834 } 835 836 setPropertyBoolean("Discoverable", discoverable); 837 setPropertyBoolean("Pairable", pairable); 838 return true; 839 } 840 841 /** 842 * @param on true set the local Bluetooth module to be connectable 843 * The dicoverability is recovered to what it was before 844 * switchConnectable(false) call 845 * false set the local Bluetooth module to be not connectable 846 * and not dicoverable 847 */ 848 /*package*/ synchronized void switchConnectable(boolean on) { 849 setAdapterPropertyBooleanNative("Powered", on ? 1 : 0); 850 } 851 852 /*package*/ synchronized void setPairable() { 853 String pairableString = getProperty("Pairable", false); 854 if (pairableString == null) { 855 Log.e(TAG, "null pairableString"); 856 return; 857 } 858 if (pairableString.equals("false")) { 859 setAdapterPropertyBooleanNative("Pairable", 1); 860 } 861 } 862 863 /*package*/ String getProperty(String name, boolean checkState) { 864 // If checkState is false, check if the event loop is running. 865 // before making the call to Bluez 866 if (checkState) { 867 if (!isEnabledInternal()) return null; 868 } else if (!mEventLoop.isEventLoopRunning()) { 869 return null; 870 } 871 872 return mAdapterProperties.getProperty(name); 873 } 874 875 BluetoothAdapterProperties getAdapterProperties() { 876 return mAdapterProperties; 877 } 878 879 BluetoothDeviceProperties getDeviceProperties() { 880 return mDeviceProperties; 881 } 882 883 boolean isRemoteDeviceInCache(String address) { 884 return mDeviceProperties.isInCache(address); 885 } 886 887 void setRemoteDeviceProperty(String address, String name, String value) { 888 mDeviceProperties.setProperty(address, name, value); 889 } 890 891 void updateRemoteDevicePropertiesCache(String address) { 892 mDeviceProperties.updateCache(address); 893 } 894 895 public synchronized String getAddress() { 896 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 897 // Don't check state since we want to provide address, even if BT is off 898 return getProperty("Address", false); 899 } 900 901 public synchronized String getName() { 902 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 903 // Don't check state since we want to provide name, even if BT is off 904 return getProperty("Name", false); 905 } 906 907 public ParcelUuid[] getUuids() { 908 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 909 String value = getProperty("UUIDs", true); 910 if (value == null) return null; 911 return convertStringToParcelUuid(value); 912 } 913 914 private ParcelUuid[] convertStringToParcelUuid(String value) { 915 String[] uuidStrings = null; 916 // The UUIDs are stored as a "," separated string. 917 uuidStrings = value.split(","); 918 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length]; 919 920 for (int i = 0; i < uuidStrings.length; i++) { 921 uuids[i] = ParcelUuid.fromString(uuidStrings[i]); 922 } 923 return uuids; 924 } 925 926 /** 927 * Returns the user-friendly name of a remote device. This value is 928 * returned from our local cache, which is updated when onPropertyChange 929 * event is received. 930 * Do not expect to retrieve the updated remote name immediately after 931 * changing the name on the remote device. 932 * 933 * @param address Bluetooth address of remote device. 934 * 935 * @return The user-friendly name of the specified remote device. 936 */ 937 public synchronized String getRemoteName(String address) { 938 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 939 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 940 return null; 941 } 942 return mDeviceProperties.getProperty(address, "Name"); 943 } 944 945 /** 946 * Returns alias of a remote device. This value is returned from our 947 * local cache, which is updated when onPropertyChange event is received. 948 * 949 * @param address Bluetooth address of remote device. 950 * 951 * @return The alias of the specified remote device. 952 */ 953 public synchronized String getRemoteAlias(String address) { 954 955 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 956 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 957 return null; 958 } 959 return mDeviceProperties.getProperty(address, "Alias"); 960 } 961 962 /** 963 * Set the alias of a remote device. 964 * 965 * @param address Bluetooth address of remote device. 966 * @param alias new alias to change to 967 * @return true on success, false on error 968 */ 969 public synchronized boolean setRemoteAlias(String address, String alias) { 970 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 971 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 972 return false; 973 } 974 975 return setDevicePropertyStringNative(getObjectPathFromAddress(address), 976 "Alias", alias); 977 } 978 979 /** 980 * Get the discoverability window for the device. A timeout of zero 981 * means that the device is permanently discoverable (if the device is 982 * in the discoverable mode). 983 * 984 * @return The discoverability window of the device, in seconds. A negative 985 * value indicates an error. 986 */ 987 public int getDiscoverableTimeout() { 988 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 989 String timeout = getProperty("DiscoverableTimeout", true); 990 if (timeout != null) 991 return Integer.valueOf(timeout); 992 else 993 return -1; 994 } 995 996 public int getScanMode() { 997 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 998 if (!isEnabledInternal()) 999 return BluetoothAdapter.SCAN_MODE_NONE; 1000 1001 boolean pairable = getProperty("Pairable", true).equals("true"); 1002 boolean discoverable = getProperty("Discoverable", true).equals("true"); 1003 return bluezStringToScanMode (pairable, discoverable); 1004 } 1005 1006 public synchronized boolean startDiscovery() { 1007 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1008 "Need BLUETOOTH_ADMIN permission"); 1009 if (!isEnabledInternal()) return false; 1010 1011 return startDiscoveryNative(); 1012 } 1013 1014 public synchronized boolean cancelDiscovery() { 1015 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1016 "Need BLUETOOTH_ADMIN permission"); 1017 if (!isEnabledInternal()) return false; 1018 1019 return stopDiscoveryNative(); 1020 } 1021 1022 public boolean isDiscovering() { 1023 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1024 1025 String discoveringProperty = getProperty("Discovering", false); 1026 if (discoveringProperty == null) { 1027 return false; 1028 } 1029 1030 return discoveringProperty.equals("true"); 1031 } 1032 1033 private boolean isBondingFeasible(String address) { 1034 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1035 "Need BLUETOOTH_ADMIN permission"); 1036 if (!isEnabledInternal()) return false; 1037 1038 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1039 return false; 1040 } 1041 address = address.toUpperCase(); 1042 1043 if (mBondState.getPendingOutgoingBonding() != null) { 1044 Log.d(TAG, "Ignoring createBond(): another device is bonding"); 1045 // a different device is currently bonding, fail 1046 return false; 1047 } 1048 1049 // Check for bond state only if we are not performing auto 1050 // pairing exponential back-off attempts. 1051 if (!mBondState.isAutoPairingAttemptsInProgress(address) && 1052 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) { 1053 Log.d(TAG, "Ignoring createBond(): this device is already bonding or bonded"); 1054 return false; 1055 } 1056 1057 if (address.equals(mDockAddress)) { 1058 if (!writeDockPin()) { 1059 Log.e(TAG, "Error while writing Pin for the dock"); 1060 return false; 1061 } 1062 } 1063 return true; 1064 } 1065 1066 public synchronized boolean createBond(String address) { 1067 if (!isBondingFeasible(address)) return false; 1068 1069 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) { 1070 return false; 1071 } 1072 1073 mBondState.setPendingOutgoingBonding(address); 1074 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); 1075 1076 return true; 1077 } 1078 1079 public synchronized boolean createBondOutOfBand(String address, byte[] hash, 1080 byte[] randomizer) { 1081 if (!isBondingFeasible(address)) return false; 1082 1083 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) { 1084 return false; 1085 } 1086 1087 setDeviceOutOfBandData(address, hash, randomizer); 1088 mBondState.setPendingOutgoingBonding(address); 1089 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); 1090 1091 return true; 1092 } 1093 1094 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash, 1095 byte[] randomizer) { 1096 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1097 "Need BLUETOOTH_ADMIN permission"); 1098 if (!isEnabledInternal()) return false; 1099 1100 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer); 1101 1102 if (DBG) { 1103 Log.d(TAG, "Setting out of band data for: " + address + ":" + 1104 Arrays.toString(hash) + ":" + Arrays.toString(randomizer)); 1105 } 1106 1107 mDeviceOobData.put(address, value); 1108 return true; 1109 } 1110 1111 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) { 1112 return mDeviceOobData.get(device.getAddress()); 1113 } 1114 1115 1116 public synchronized byte[] readOutOfBandData() { 1117 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 1118 "Need BLUETOOTH permission"); 1119 if (!isEnabledInternal()) return null; 1120 1121 return readAdapterOutOfBandDataNative(); 1122 } 1123 1124 public synchronized boolean cancelBondProcess(String address) { 1125 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1126 "Need BLUETOOTH_ADMIN permission"); 1127 if (!isEnabledInternal()) return false; 1128 1129 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1130 return false; 1131 } 1132 address = address.toUpperCase(); 1133 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) { 1134 return false; 1135 } 1136 1137 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 1138 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 1139 cancelDeviceCreationNative(address); 1140 return true; 1141 } 1142 1143 public synchronized boolean removeBond(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 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 1152 if (state != null) { 1153 state.sendMessage(BluetoothDeviceProfileState.UNPAIR); 1154 return true; 1155 } else { 1156 return false; 1157 } 1158 } 1159 1160 public synchronized boolean removeBondInternal(String address) { 1161 // Unset the trusted device state and then unpair 1162 setTrust(address, false); 1163 return removeDeviceNative(getObjectPathFromAddress(address)); 1164 } 1165 1166 public synchronized String[] listBonds() { 1167 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1168 return mBondState.listInState(BluetoothDevice.BOND_BONDED); 1169 } 1170 1171 /*package*/ synchronized String[] listInState(int state) { 1172 return mBondState.listInState(state); 1173 } 1174 1175 public synchronized int getBondState(String address) { 1176 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1177 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1178 return BluetoothDevice.ERROR; 1179 } 1180 return mBondState.getBondState(address.toUpperCase()); 1181 } 1182 1183 /*package*/ synchronized boolean setBondState(String address, int state) { 1184 return setBondState(address, state, 0); 1185 } 1186 1187 /*package*/ synchronized boolean setBondState(String address, int state, int reason) { 1188 mBondState.setBondState(address.toUpperCase(), state, reason); 1189 return true; 1190 } 1191 1192 public synchronized boolean isBluetoothDock(String address) { 1193 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, 1194 Context.MODE_PRIVATE); 1195 1196 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address); 1197 } 1198 1199 /*package*/ String[] getRemoteDeviceProperties(String address) { 1200 if (!isEnabledInternal()) return null; 1201 1202 String objectPath = getObjectPathFromAddress(address); 1203 return (String [])getDevicePropertiesNative(objectPath); 1204 } 1205 1206 /** 1207 * Sets the remote device trust state. 1208 * 1209 * @return boolean to indicate operation success or fail 1210 */ 1211 public synchronized boolean setTrust(String address, boolean value) { 1212 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1213 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1214 "Need BLUETOOTH_ADMIN permission"); 1215 return false; 1216 } 1217 1218 if (!isEnabledInternal()) return false; 1219 1220 return setDevicePropertyBooleanNative( 1221 getObjectPathFromAddress(address), "Trusted", value ? 1 : 0); 1222 } 1223 1224 /** 1225 * Gets the remote device trust state as boolean. 1226 * Note: this value may be 1227 * retrieved from cache if we retrieved the data before * 1228 * 1229 * @return boolean to indicate trusted or untrusted state 1230 */ 1231 public synchronized boolean getTrustState(String address) { 1232 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1233 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1234 return false; 1235 } 1236 1237 String val = mDeviceProperties.getProperty(address, "Trusted"); 1238 if (val == null) { 1239 return false; 1240 } else { 1241 return val.equals("true"); 1242 } 1243 } 1244 1245 /** 1246 * Gets the remote major, minor classes encoded as a 32-bit 1247 * integer. 1248 * 1249 * Note: this value is retrieved from cache, because we get it during 1250 * remote-device discovery. 1251 * 1252 * @return 32-bit integer encoding the remote major, minor, and service 1253 * classes. 1254 */ 1255 public synchronized int getRemoteClass(String address) { 1256 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1257 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1258 return BluetoothClass.ERROR; 1259 } 1260 String val = mDeviceProperties.getProperty(address, "Class"); 1261 if (val == null) 1262 return BluetoothClass.ERROR; 1263 else { 1264 return Integer.valueOf(val); 1265 } 1266 } 1267 1268 1269 /** 1270 * Gets the UUIDs supported by the remote device 1271 * 1272 * @return array of 128bit ParcelUuids 1273 */ 1274 public synchronized ParcelUuid[] getRemoteUuids(String address) { 1275 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1276 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1277 return null; 1278 } 1279 return getUuidFromCache(address); 1280 } 1281 1282 ParcelUuid[] getUuidFromCache(String address) { 1283 String value = mDeviceProperties.getProperty(address, "UUIDs"); 1284 if (value == null) return null; 1285 1286 String[] uuidStrings = null; 1287 // The UUIDs are stored as a "," separated string. 1288 uuidStrings = value.split(","); 1289 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length]; 1290 1291 for (int i = 0; i < uuidStrings.length; i++) { 1292 uuids[i] = ParcelUuid.fromString(uuidStrings[i]); 1293 } 1294 return uuids; 1295 } 1296 1297 /** 1298 * Connect and fetch new UUID's using SDP. 1299 * The UUID's found are broadcast as intents. 1300 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the 1301 * a given uuid. 1302 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success 1303 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for 1304 * callback and broadcast intents. 1305 */ 1306 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid, 1307 IBluetoothCallback callback) { 1308 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1309 if (!isEnabledInternal()) return false; 1310 1311 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1312 return false; 1313 } 1314 1315 RemoteService service = new RemoteService(address, uuid); 1316 if (uuid != null && mUuidCallbackTracker.get(service) != null) { 1317 // An SDP query for this address & uuid is already in progress 1318 // Do not add this callback for the uuid 1319 return false; 1320 } 1321 1322 if (mUuidIntentTracker.contains(address)) { 1323 // An SDP query for this address is already in progress 1324 // Add this uuid onto the in-progress SDP query 1325 if (uuid != null) { 1326 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback); 1327 } 1328 return true; 1329 } 1330 1331 // If the device is already created, we will 1332 // do the SDP on the callback of createDeviceNative. 1333 boolean ret= createDeviceNative(address); 1334 1335 mUuidIntentTracker.add(address); 1336 if (uuid != null) { 1337 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback); 1338 } 1339 1340 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 1341 message.obj = address; 1342 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY); 1343 return ret; 1344 } 1345 1346 /** 1347 * Gets the rfcomm channel associated with the UUID. 1348 * Pulls records from the cache only. 1349 * 1350 * @param address Address of the remote device 1351 * @param uuid ParcelUuid of the service attribute 1352 * 1353 * @return rfcomm channel associated with the service attribute 1354 * -1 on error 1355 */ 1356 public int getRemoteServiceChannel(String address, ParcelUuid uuid) { 1357 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1358 if (!isEnabledInternal()) return -1; 1359 1360 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1361 return BluetoothDevice.ERROR; 1362 } 1363 // Check if we are recovering from a crash. 1364 if (mDeviceProperties.isEmpty()) { 1365 if (mDeviceProperties.updateCache(address) == null) 1366 return -1; 1367 } 1368 1369 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address); 1370 if (value != null && value.containsKey(uuid)) 1371 return value.get(uuid); 1372 return -1; 1373 } 1374 1375 public synchronized boolean setPin(String address, byte[] pin) { 1376 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1377 "Need BLUETOOTH_ADMIN permission"); 1378 if (!isEnabledInternal()) return false; 1379 1380 if (pin == null || pin.length <= 0 || pin.length > 16 || 1381 !BluetoothAdapter.checkBluetoothAddress(address)) { 1382 return false; 1383 } 1384 address = address.toUpperCase(); 1385 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1386 if (data == null) { 1387 Log.w(TAG, "setPin(" + address + ") called but no native data available, " + 1388 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1389 " or by bluez.\n"); 1390 return false; 1391 } 1392 // bluez API wants pin as a string 1393 String pinString; 1394 try { 1395 pinString = new String(pin, "UTF8"); 1396 } catch (UnsupportedEncodingException uee) { 1397 Log.e(TAG, "UTF8 not supported?!?"); 1398 return false; 1399 } 1400 return setPinNative(address, pinString, data.intValue()); 1401 } 1402 1403 public synchronized boolean setPasskey(String address, int passkey) { 1404 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1405 "Need BLUETOOTH_ADMIN permission"); 1406 if (!isEnabledInternal()) return false; 1407 1408 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) { 1409 return false; 1410 } 1411 address = address.toUpperCase(); 1412 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1413 if (data == null) { 1414 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + 1415 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1416 " or by bluez.\n"); 1417 return false; 1418 } 1419 return setPasskeyNative(address, passkey, data.intValue()); 1420 } 1421 1422 public synchronized boolean setPairingConfirmation(String address, boolean confirm) { 1423 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1424 "Need BLUETOOTH_ADMIN permission"); 1425 if (!isEnabledInternal()) return false; 1426 1427 address = address.toUpperCase(); 1428 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1429 if (data == null) { 1430 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " + 1431 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1432 " or by bluez.\n"); 1433 return false; 1434 } 1435 return setPairingConfirmationNative(address, confirm, data.intValue()); 1436 } 1437 1438 public synchronized boolean setRemoteOutOfBandData(String address) { 1439 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1440 "Need BLUETOOTH_ADMIN permission"); 1441 if (!isEnabledInternal()) return false; 1442 address = address.toUpperCase(); 1443 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1444 if (data == null) { 1445 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " + 1446 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + 1447 " or by bluez.\n"); 1448 return false; 1449 } 1450 1451 Pair<byte[], byte[]> val = mDeviceOobData.get(address); 1452 byte[] hash, randomizer; 1453 if (val == null) { 1454 // TODO: check what should be passed in this case. 1455 hash = new byte[16]; 1456 randomizer = new byte[16]; 1457 } else { 1458 hash = val.first; 1459 randomizer = val.second; 1460 } 1461 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue()); 1462 } 1463 1464 public synchronized boolean cancelPairingUserInput(String address) { 1465 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 1466 "Need BLUETOOTH_ADMIN permission"); 1467 if (!isEnabledInternal()) return false; 1468 1469 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 1470 return false; 1471 } 1472 mBondState.setBondState(address, BluetoothDevice.BOND_NONE, 1473 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); 1474 address = address.toUpperCase(); 1475 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); 1476 if (data == null) { 1477 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " + 1478 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " + 1479 "by the remote or by bluez.\n"); 1480 return false; 1481 } 1482 return cancelPairingUserInputNative(address, data.intValue()); 1483 } 1484 1485 /*package*/ void updateDeviceServiceChannelCache(String address) { 1486 if (DBG) Log.d(TAG, "updateDeviceServiceChannelCache(" + address + ")"); 1487 1488 // We are storing the rfcomm channel numbers only for the uuids 1489 // we are interested in. 1490 ParcelUuid[] deviceUuids = getRemoteUuids(address); 1491 1492 ArrayList<ParcelUuid> applicationUuids = new ArrayList<ParcelUuid>(); 1493 1494 synchronized (this) { 1495 for (RemoteService service : mUuidCallbackTracker.keySet()) { 1496 if (service.address.equals(address)) { 1497 applicationUuids.add(service.uuid); 1498 } 1499 } 1500 } 1501 1502 Map <ParcelUuid, Integer> uuidToChannelMap = new HashMap<ParcelUuid, Integer>(); 1503 1504 // Retrieve RFCOMM channel for default uuids 1505 for (ParcelUuid uuid : RFCOMM_UUIDS) { 1506 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) { 1507 int channel = getDeviceServiceChannelForUuid(address, uuid); 1508 uuidToChannelMap.put(uuid, channel); 1509 if (DBG) Log.d(TAG, "\tuuid(system): " + uuid + " " + channel); 1510 } 1511 } 1512 // Retrieve RFCOMM channel for application requested uuids 1513 for (ParcelUuid uuid : applicationUuids) { 1514 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) { 1515 int channel = getDeviceServiceChannelForUuid(address, uuid); 1516 uuidToChannelMap.put(uuid, channel); 1517 if (DBG) Log.d(TAG, "\tuuid(application): " + uuid + " " + channel); 1518 } 1519 } 1520 1521 synchronized (this) { 1522 // Make application callbacks 1523 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator(); 1524 iter.hasNext();) { 1525 RemoteService service = iter.next(); 1526 if (service.address.equals(address)) { 1527 if (uuidToChannelMap.containsKey(service.uuid)) { 1528 int channel = uuidToChannelMap.get(service.uuid); 1529 1530 if (DBG) Log.d(TAG, "Making callback for " + service.uuid + 1531 " with result " + channel); 1532 IBluetoothCallback callback = mUuidCallbackTracker.get(service); 1533 if (callback != null) { 1534 try { 1535 callback.onRfcommChannelFound(channel); 1536 } catch (RemoteException e) {Log.e(TAG, "", e);} 1537 } 1538 1539 iter.remove(); 1540 } 1541 } 1542 } 1543 1544 // Update cache 1545 mDeviceServiceChannelCache.put(address, uuidToChannelMap); 1546 } 1547 } 1548 1549 private int getDeviceServiceChannelForUuid(String address, 1550 ParcelUuid uuid) { 1551 return getDeviceServiceChannelNative(getObjectPathFromAddress(address), 1552 uuid.toString(), 0x0004); 1553 } 1554 1555 /** 1556 * b is a handle to a Binder instance, so that this service can be notified 1557 * for Applications that terminate unexpectedly, to clean there service 1558 * records 1559 */ 1560 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid, 1561 int channel, IBinder b) { 1562 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1563 if (!isEnabledInternal()) return -1; 1564 1565 if (serviceName == null || uuid == null || channel < 1 || 1566 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) { 1567 return -1; 1568 } 1569 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) { 1570 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid); 1571 return -1; 1572 } 1573 int handle = addRfcommServiceRecordNative(serviceName, 1574 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(), 1575 (short)channel); 1576 if (DBG) Log.d(TAG, "new handle " + Integer.toHexString(handle)); 1577 if (handle == -1) { 1578 return -1; 1579 } 1580 1581 ServiceRecordClient client = new ServiceRecordClient(); 1582 client.pid = Binder.getCallingPid(); 1583 client.binder = b; 1584 client.death = new Reaper(handle, client.pid, RFCOMM_RECORD_REAPER); 1585 mServiceRecordToPid.put(new Integer(handle), client); 1586 try { 1587 b.linkToDeath(client.death, 0); 1588 } catch (RemoteException e) { 1589 Log.e(TAG, "", e); 1590 client.death = null; 1591 } 1592 return handle; 1593 } 1594 1595 public void removeServiceRecord(int handle) { 1596 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 1597 "Need BLUETOOTH permission"); 1598 // Since this is a binder call check if Bluetooth is off 1599 if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return; 1600 Message message = mHandler.obtainMessage(MESSAGE_REMOVE_SERVICE_RECORD); 1601 message.obj = new Pair<Integer, Integer>(handle, Binder.getCallingPid()); 1602 mHandler.sendMessage(message); 1603 } 1604 1605 private synchronized void checkAndRemoveRecord(int handle, int pid) { 1606 ServiceRecordClient client = mServiceRecordToPid.get(handle); 1607 if (client != null && pid == client.pid) { 1608 if (DBG) Log.d(TAG, "Removing service record " + 1609 Integer.toHexString(handle) + " for pid " + pid); 1610 1611 if (client.death != null) { 1612 client.binder.unlinkToDeath(client.death, 0); 1613 } 1614 1615 mServiceRecordToPid.remove(handle); 1616 removeServiceRecordNative(handle); 1617 } 1618 } 1619 1620 private class Reaper implements IBinder.DeathRecipient { 1621 int mPid; 1622 int mHandle; 1623 int mType; 1624 1625 Reaper(int handle, int pid, int type) { 1626 mPid = pid; 1627 mHandle = handle; 1628 mType = type; 1629 } 1630 1631 Reaper(int pid, int type) { 1632 mPid = pid; 1633 mType = type; 1634 } 1635 1636 @Override 1637 public void binderDied() { 1638 synchronized (BluetoothService.this) { 1639 if (DBG) Log.d(TAG, "Tracked app " + mPid + " died" + "Type:" + mType); 1640 if (mType == RFCOMM_RECORD_REAPER) { 1641 checkAndRemoveRecord(mHandle, mPid); 1642 } else if (mType == STATE_CHANGE_REAPER) { 1643 mStateChangeTracker.remove(mPid); 1644 } 1645 } 1646 } 1647 } 1648 1649 1650 @Override 1651 public boolean changeApplicationBluetoothState(boolean on, 1652 IBluetoothStateChangeCallback callback, IBinder binder) { 1653 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1654 1655 int pid = Binder.getCallingPid(); 1656 //mStateChangeTracker is a synchronized map 1657 if (!mStateChangeTracker.containsKey(pid)) { 1658 if (on) { 1659 mStateChangeTracker.put(pid, callback); 1660 } else { 1661 return false; 1662 } 1663 } else if (!on) { 1664 mStateChangeTracker.remove(pid); 1665 } 1666 1667 if (binder != null) { 1668 try { 1669 binder.linkToDeath(new Reaper(pid, STATE_CHANGE_REAPER), 0); 1670 } catch (RemoteException e) { 1671 Log.e(TAG, "", e); 1672 return false; 1673 } 1674 } 1675 1676 int type; 1677 if (on) { 1678 type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_ON; 1679 } else { 1680 type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_OFF; 1681 } 1682 1683 mBluetoothState.sendMessage(type, callback); 1684 return true; 1685 } 1686 1687 boolean isApplicationStateChangeTrackerEmpty() { 1688 return mStateChangeTracker.isEmpty(); 1689 } 1690 1691 void clearApplicationStateChangeTracker() { 1692 mStateChangeTracker.clear(); 1693 } 1694 1695 Collection<IBluetoothStateChangeCallback> getApplicationStateChangeCallbacks() { 1696 return mStateChangeTracker.values(); 1697 } 1698 1699 int getNumberOfApplicationStateChangeTrackers() { 1700 return mStateChangeTracker.size(); 1701 } 1702 1703 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 1704 @Override 1705 public void onReceive(Context context, Intent intent) { 1706 if (intent == null) return; 1707 1708 String action = intent.getAction(); 1709 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { 1710 ContentResolver resolver = context.getContentResolver(); 1711 // Query the airplane mode from Settings.System just to make sure that 1712 // some random app is not sending this intent and disabling bluetooth 1713 if (isAirplaneModeOn()) { 1714 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_ON); 1715 } else { 1716 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_OFF); 1717 } 1718 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) { 1719 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 1720 Intent.EXTRA_DOCK_STATE_UNDOCKED); 1721 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state); 1722 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) { 1723 mDockAddress = null; 1724 mDockPin = null; 1725 } else { 1726 SharedPreferences.Editor editor = 1727 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, 1728 mContext.MODE_PRIVATE).edit(); 1729 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true); 1730 editor.apply(); 1731 } 1732 } 1733 } 1734 }; 1735 1736 private void registerForAirplaneMode(IntentFilter filter) { 1737 final ContentResolver resolver = mContext.getContentResolver(); 1738 final String airplaneModeRadios = Settings.System.getString(resolver, 1739 Settings.System.AIRPLANE_MODE_RADIOS); 1740 final String toggleableRadios = Settings.System.getString(resolver, 1741 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 1742 1743 mIsAirplaneSensitive = airplaneModeRadios == null ? true : 1744 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); 1745 mIsAirplaneToggleable = toggleableRadios == null ? false : 1746 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH); 1747 1748 if (mIsAirplaneSensitive) { 1749 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 1750 } 1751 } 1752 1753 /* Returns true if airplane mode is currently on */ 1754 /*package*/ final boolean isAirplaneModeOn() { 1755 return Settings.System.getInt(mContext.getContentResolver(), 1756 Settings.System.AIRPLANE_MODE_ON, 0) == 1; 1757 } 1758 1759 /* Broadcast the Uuid intent */ 1760 /*package*/ synchronized void sendUuidIntent(String address) { 1761 ParcelUuid[] uuid = getUuidFromCache(address); 1762 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 1763 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); 1764 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid); 1765 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 1766 mUuidIntentTracker.remove(address); 1767 } 1768 1769 /*package*/ synchronized void makeServiceChannelCallbacks(String address) { 1770 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator(); 1771 iter.hasNext();) { 1772 RemoteService service = iter.next(); 1773 if (service.address.equals(address)) { 1774 if (DBG) Log.d(TAG, "Cleaning up failed UUID channel lookup: " 1775 + service.address + " " + service.uuid); 1776 IBluetoothCallback callback = mUuidCallbackTracker.get(service); 1777 if (callback != null) { 1778 try { 1779 callback.onRfcommChannelFound(-1); 1780 } catch (RemoteException e) {Log.e(TAG, "", e);} 1781 } 1782 1783 iter.remove(); 1784 } 1785 } 1786 } 1787 1788 @Override 1789 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1790 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1791 1792 if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) { 1793 return; 1794 } 1795 1796 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive); 1797 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable); 1798 1799 pw.println("Local address = " + getAddress()); 1800 pw.println("Local name = " + getName()); 1801 pw.println("isDiscovering() = " + isDiscovering()); 1802 1803 mAdapter.getProfileProxy(mContext, 1804 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); 1805 mAdapter.getProfileProxy(mContext, 1806 mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE); 1807 mAdapter.getProfileProxy(mContext, 1808 mBluetoothProfileServiceListener, BluetoothProfile.PAN); 1809 1810 dumpKnownDevices(pw); 1811 dumpAclConnectedDevices(pw); 1812 dumpHeadsetService(pw); 1813 dumpInputDeviceProfile(pw); 1814 dumpPanProfile(pw); 1815 dumpApplicationServiceRecords(pw); 1816 dumpProfileState(pw); 1817 } 1818 1819 private void dumpProfileState(PrintWriter pw) { 1820 pw.println("\n--Profile State dump--"); 1821 pw.println("\n Headset profile state:" + 1822 mAdapter.getProfileConnectionState(BluetoothProfile.HEADSET)); 1823 pw.println("\n A2dp profile state:" + 1824 mAdapter.getProfileConnectionState(BluetoothProfile.A2DP)); 1825 pw.println("\n HID profile state:" + 1826 mAdapter.getProfileConnectionState(BluetoothProfile.INPUT_DEVICE)); 1827 pw.println("\n PAN profile state:" + 1828 mAdapter.getProfileConnectionState(BluetoothProfile.PAN)); 1829 } 1830 1831 private void dumpHeadsetService(PrintWriter pw) { 1832 pw.println("\n--Headset Service--"); 1833 if (mHeadsetProxy != null) { 1834 List<BluetoothDevice> deviceList = mHeadsetProxy.getConnectedDevices(); 1835 if (deviceList.size() == 0) { 1836 pw.println("No headsets connected"); 1837 } else { 1838 BluetoothDevice device = deviceList.get(0); 1839 pw.println("\ngetConnectedDevices[0] = " + device); 1840 dumpHeadsetConnectionState(pw, device); 1841 pw.println("getBatteryUsageHint() = " + 1842 mHeadsetProxy.getBatteryUsageHint(device)); 1843 } 1844 1845 deviceList.clear(); 1846 deviceList = mHeadsetProxy.getDevicesMatchingConnectionStates(new int[] { 1847 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED}); 1848 pw.println("--Connected and Disconnected Headsets"); 1849 for (BluetoothDevice device: deviceList) { 1850 pw.println(device); 1851 if (mHeadsetProxy.isAudioConnected(device)) { 1852 pw.println("SCO audio connected to device:" + device); 1853 } 1854 } 1855 } 1856 } 1857 1858 private void dumpInputDeviceProfile(PrintWriter pw) { 1859 pw.println("\n--Bluetooth Service- Input Device Profile"); 1860 if (mInputDevice != null) { 1861 List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices(); 1862 if (deviceList.size() == 0) { 1863 pw.println("No input devices connected"); 1864 } else { 1865 pw.println("Number of connected devices:" + deviceList.size()); 1866 BluetoothDevice device = deviceList.get(0); 1867 pw.println("getConnectedDevices[0] = " + device); 1868 pw.println("Priority of Connected device = " + mInputDevice.getPriority(device)); 1869 1870 switch (mInputDevice.getConnectionState(device)) { 1871 case BluetoothInputDevice.STATE_CONNECTING: 1872 pw.println("getConnectionState() = STATE_CONNECTING"); 1873 break; 1874 case BluetoothInputDevice.STATE_CONNECTED: 1875 pw.println("getConnectionState() = STATE_CONNECTED"); 1876 break; 1877 case BluetoothInputDevice.STATE_DISCONNECTING: 1878 pw.println("getConnectionState() = STATE_DISCONNECTING"); 1879 break; 1880 } 1881 } 1882 deviceList.clear(); 1883 deviceList = mInputDevice.getDevicesMatchingConnectionStates(new int[] { 1884 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED}); 1885 pw.println("--Connected and Disconnected input devices"); 1886 for (BluetoothDevice device: deviceList) { 1887 pw.println(device); 1888 } 1889 } 1890 } 1891 1892 private void dumpPanProfile(PrintWriter pw) { 1893 pw.println("\n--Bluetooth Service- Pan Profile"); 1894 if (mPan != null) { 1895 List<BluetoothDevice> deviceList = mPan.getConnectedDevices(); 1896 if (deviceList.size() == 0) { 1897 pw.println("No Pan devices connected"); 1898 } else { 1899 pw.println("Number of connected devices:" + deviceList.size()); 1900 BluetoothDevice device = deviceList.get(0); 1901 pw.println("getConnectedDevices[0] = " + device); 1902 1903 switch (mPan.getConnectionState(device)) { 1904 case BluetoothInputDevice.STATE_CONNECTING: 1905 pw.println("getConnectionState() = STATE_CONNECTING"); 1906 break; 1907 case BluetoothInputDevice.STATE_CONNECTED: 1908 pw.println("getConnectionState() = STATE_CONNECTED"); 1909 break; 1910 case BluetoothInputDevice.STATE_DISCONNECTING: 1911 pw.println("getConnectionState() = STATE_DISCONNECTING"); 1912 break; 1913 } 1914 } 1915 deviceList.clear(); 1916 deviceList = mPan.getDevicesMatchingConnectionStates(new int[] { 1917 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED}); 1918 pw.println("--Connected and Disconnected Pan devices"); 1919 for (BluetoothDevice device: deviceList) { 1920 pw.println(device); 1921 } 1922 } 1923 } 1924 1925 private void dumpHeadsetConnectionState(PrintWriter pw, 1926 BluetoothDevice device) { 1927 switch (mHeadsetProxy.getConnectionState(device)) { 1928 case BluetoothHeadset.STATE_CONNECTING: 1929 pw.println("getConnectionState() = STATE_CONNECTING"); 1930 break; 1931 case BluetoothHeadset.STATE_CONNECTED: 1932 pw.println("getConnectionState() = STATE_CONNECTED"); 1933 break; 1934 case BluetoothHeadset.STATE_DISCONNECTING: 1935 pw.println("getConnectionState() = STATE_DISCONNECTING"); 1936 break; 1937 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 1938 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED"); 1939 break; 1940 } 1941 } 1942 1943 private void dumpApplicationServiceRecords(PrintWriter pw) { 1944 pw.println("\n--Application Service Records--"); 1945 for (Integer handle : mServiceRecordToPid.keySet()) { 1946 Integer pid = mServiceRecordToPid.get(handle).pid; 1947 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); 1948 } 1949 } 1950 1951 private void dumpAclConnectedDevices(PrintWriter pw) { 1952 String[] devicesObjectPath = getKnownDevices(); 1953 pw.println("\n--ACL connected devices--"); 1954 if (devicesObjectPath != null) { 1955 for (String device : devicesObjectPath) { 1956 pw.println(getAddressFromObjectPath(device)); 1957 } 1958 } 1959 } 1960 1961 private void dumpKnownDevices(PrintWriter pw) { 1962 pw.println("\n--Known devices--"); 1963 for (String address : mDeviceProperties.keySet()) { 1964 int bondState = mBondState.getBondState(address); 1965 pw.printf("%s %10s (%d) %s\n", address, 1966 toBondStateString(bondState), 1967 mBondState.getAttempt(address), 1968 getRemoteName(address)); 1969 1970 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address); 1971 if (uuidChannels == null) { 1972 pw.println("\tuuids = null"); 1973 } else { 1974 for (ParcelUuid uuid : uuidChannels.keySet()) { 1975 Integer channel = uuidChannels.get(uuid); 1976 if (channel == null) { 1977 pw.println("\t" + uuid); 1978 } else { 1979 pw.println("\t" + uuid + " RFCOMM channel = " + channel); 1980 } 1981 } 1982 } 1983 for (RemoteService service : mUuidCallbackTracker.keySet()) { 1984 if (service.address.equals(address)) { 1985 pw.println("\tPENDING CALLBACK: " + service.uuid); 1986 } 1987 } 1988 } 1989 } 1990 1991 private void getProfileProxy() { 1992 mAdapter.getProfileProxy(mContext, 1993 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); 1994 } 1995 1996 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 1997 new BluetoothProfile.ServiceListener() { 1998 public void onServiceConnected(int profile, BluetoothProfile proxy) { 1999 if (profile == BluetoothProfile.HEADSET) { 2000 mHeadsetProxy = (BluetoothHeadset) proxy; 2001 } else if (profile == BluetoothProfile.INPUT_DEVICE) { 2002 mInputDevice = (BluetoothInputDevice) proxy; 2003 } else if (profile == BluetoothProfile.PAN) { 2004 mPan = (BluetoothPan) proxy; 2005 } 2006 } 2007 public void onServiceDisconnected(int profile) { 2008 if (profile == BluetoothProfile.HEADSET) { 2009 mHeadsetProxy = null; 2010 } else if (profile == BluetoothProfile.INPUT_DEVICE) { 2011 mInputDevice = null; 2012 } else if (profile == BluetoothProfile.PAN) { 2013 mPan = null; 2014 } 2015 } 2016 }; 2017 2018 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) { 2019 if (pairable && discoverable) 2020 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; 2021 else if (pairable && !discoverable) 2022 return BluetoothAdapter.SCAN_MODE_CONNECTABLE; 2023 else 2024 return BluetoothAdapter.SCAN_MODE_NONE; 2025 } 2026 2027 /* package */ static String scanModeToBluezString(int mode) { 2028 switch (mode) { 2029 case BluetoothAdapter.SCAN_MODE_NONE: 2030 return "off"; 2031 case BluetoothAdapter.SCAN_MODE_CONNECTABLE: 2032 return "connectable"; 2033 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: 2034 return "discoverable"; 2035 } 2036 return null; 2037 } 2038 2039 /*package*/ String getAddressFromObjectPath(String objectPath) { 2040 String adapterObjectPath = mAdapterProperties.getObjectPath(); 2041 if (adapterObjectPath == null || objectPath == null) { 2042 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath + 2043 " or deviceObjectPath:" + objectPath + " is null"); 2044 return null; 2045 } 2046 if (!objectPath.startsWith(adapterObjectPath)) { 2047 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath + 2048 " is not a prefix of deviceObjectPath:" + objectPath + 2049 "bluetoothd crashed ?"); 2050 return null; 2051 } 2052 String address = objectPath.substring(adapterObjectPath.length()); 2053 if (address != null) return address.replace('_', ':'); 2054 2055 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null"); 2056 return null; 2057 } 2058 2059 /*package*/ String getObjectPathFromAddress(String address) { 2060 String path = mAdapterProperties.getObjectPath(); 2061 if (path == null) { 2062 Log.e(TAG, "Error: Object Path is null"); 2063 return null; 2064 } 2065 path = path + address.replace(":", "_"); 2066 return path; 2067 } 2068 2069 /*package */ void setLinkTimeout(String address, int num_slots) { 2070 String path = getObjectPathFromAddress(address); 2071 boolean result = setLinkTimeoutNative(path, num_slots); 2072 2073 if (!result) Log.d(TAG, "Set Link Timeout to " + num_slots + " slots failed"); 2074 } 2075 2076 /**** Handlers for PAN Profile ****/ 2077 // TODO: This needs to be converted to a state machine. 2078 2079 public boolean isTetheringOn() { 2080 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2081 synchronized (mBluetoothPanProfileHandler) { 2082 return mBluetoothPanProfileHandler.isTetheringOn(); 2083 } 2084 } 2085 2086 /*package*/boolean allowIncomingTethering() { 2087 synchronized (mBluetoothPanProfileHandler) { 2088 return mBluetoothPanProfileHandler.allowIncomingTethering(); 2089 } 2090 } 2091 2092 public void setBluetoothTethering(boolean value) { 2093 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2094 synchronized (mBluetoothPanProfileHandler) { 2095 mBluetoothPanProfileHandler.setBluetoothTethering(value); 2096 } 2097 } 2098 2099 public int getPanDeviceConnectionState(BluetoothDevice device) { 2100 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2101 synchronized (mBluetoothPanProfileHandler) { 2102 return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device); 2103 } 2104 } 2105 2106 public boolean connectPanDevice(BluetoothDevice device) { 2107 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 2108 "Need BLUETOOTH_ADMIN permission"); 2109 synchronized (mBluetoothPanProfileHandler) { 2110 return mBluetoothPanProfileHandler.connectPanDevice(device); 2111 } 2112 } 2113 2114 public List<BluetoothDevice> getConnectedPanDevices() { 2115 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2116 synchronized (mBluetoothPanProfileHandler) { 2117 return mBluetoothPanProfileHandler.getConnectedPanDevices(); 2118 } 2119 } 2120 2121 public List<BluetoothDevice> getPanDevicesMatchingConnectionStates( 2122 int[] states) { 2123 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2124 synchronized (mBluetoothPanProfileHandler) { 2125 return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states); 2126 } 2127 } 2128 2129 public boolean disconnectPanDevice(BluetoothDevice device) { 2130 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 2131 "Need BLUETOOTH_ADMIN permission"); 2132 synchronized (mBluetoothPanProfileHandler) { 2133 return mBluetoothPanProfileHandler.disconnectPanDevice(device); 2134 } 2135 } 2136 2137 /*package*/void handlePanDeviceStateChange(BluetoothDevice device, 2138 String iface, 2139 int state, 2140 int role) { 2141 synchronized (mBluetoothPanProfileHandler) { 2142 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role); 2143 } 2144 } 2145 2146 /*package*/void handlePanDeviceStateChange(BluetoothDevice device, 2147 int state, int role) { 2148 synchronized (mBluetoothPanProfileHandler) { 2149 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role); 2150 } 2151 } 2152 2153 /**** Handlers for Input Device Profile ****/ 2154 // This needs to be converted to state machine 2155 2156 public boolean connectInputDevice(BluetoothDevice device) { 2157 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 2158 "Need BLUETOOTH_ADMIN permission"); 2159 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); 2160 synchronized (mBluetoothInputProfileHandler) { 2161 return mBluetoothInputProfileHandler.connectInputDevice(device, state); 2162 } 2163 } 2164 2165 public boolean connectInputDeviceInternal(BluetoothDevice device) { 2166 synchronized (mBluetoothInputProfileHandler) { 2167 return mBluetoothInputProfileHandler.connectInputDeviceInternal(device); 2168 } 2169 } 2170 2171 public boolean disconnectInputDevice(BluetoothDevice device) { 2172 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 2173 "Need BLUETOOTH_ADMIN permission"); 2174 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); 2175 synchronized (mBluetoothInputProfileHandler) { 2176 return mBluetoothInputProfileHandler.disconnectInputDevice(device, state); 2177 } 2178 } 2179 2180 public boolean disconnectInputDeviceInternal(BluetoothDevice device) { 2181 synchronized (mBluetoothInputProfileHandler) { 2182 return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device); 2183 } 2184 } 2185 2186 public int getInputDeviceConnectionState(BluetoothDevice device) { 2187 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2188 synchronized (mBluetoothInputProfileHandler) { 2189 return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device); 2190 } 2191 } 2192 2193 public List<BluetoothDevice> getConnectedInputDevices() { 2194 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2195 synchronized (mBluetoothInputProfileHandler) { 2196 return mBluetoothInputProfileHandler.getConnectedInputDevices(); 2197 } 2198 } 2199 2200 public List<BluetoothDevice> getInputDevicesMatchingConnectionStates( 2201 int[] states) { 2202 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2203 synchronized (mBluetoothInputProfileHandler) { 2204 return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states); 2205 } 2206 } 2207 2208 2209 public int getInputDevicePriority(BluetoothDevice device) { 2210 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2211 synchronized (mBluetoothInputProfileHandler) { 2212 return mBluetoothInputProfileHandler.getInputDevicePriority(device); 2213 } 2214 } 2215 2216 public boolean setInputDevicePriority(BluetoothDevice device, int priority) { 2217 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 2218 "Need BLUETOOTH_ADMIN permission"); 2219 synchronized (mBluetoothInputProfileHandler) { 2220 return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority); 2221 } 2222 } 2223 2224 /** 2225 * Handle incoming profile acceptance for profiles handled by Bluetooth Service, 2226 * currently PAN and HID. This also is the catch all for all rejections for profiles 2227 * that is not supported. 2228 * 2229 * @param device - Bluetooth Device 2230 * @param allow - true / false 2231 * @return 2232 */ 2233 public boolean allowIncomingProfileConnect(BluetoothDevice device, boolean allow) { 2234 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 2235 "Need BLUETOOTH_ADMIN permission"); 2236 String address = device.getAddress(); 2237 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 2238 return false; 2239 } 2240 2241 Integer data = getAuthorizationAgentRequestData(address); 2242 if (data == null) { 2243 Log.w(TAG, "allowIncomingProfileConnect(" + device + 2244 ") called but no native data available"); 2245 return false; 2246 } 2247 if (DBG) log("allowIncomingProfileConnect: " + device + " : " + allow + " : " + data); 2248 return setAuthorizationNative(address, allow, data.intValue()); 2249 } 2250 2251 /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { 2252 synchronized (mBluetoothInputProfileHandler) { 2253 return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states); 2254 } 2255 } 2256 2257 /*package*/void handleInputDevicePropertyChange(String address, boolean connected) { 2258 synchronized (mBluetoothInputProfileHandler) { 2259 mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected); 2260 } 2261 } 2262 2263 /**** Handlers for Health Device Profile ****/ 2264 // TODO: All these need to be converted to a state machine. 2265 2266 public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, 2267 IBluetoothHealthCallback callback) { 2268 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2269 "Need BLUETOOTH permission"); 2270 synchronized (mBluetoothHealthProfileHandler) { 2271 return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback); 2272 } 2273 } 2274 2275 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { 2276 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2277 "Need BLUETOOTH permission"); 2278 synchronized (mBluetoothHealthProfileHandler) { 2279 return mBluetoothHealthProfileHandler.unregisterAppConfiguration(config); 2280 } 2281 } 2282 2283 2284 public boolean connectChannelToSource(BluetoothDevice device, 2285 BluetoothHealthAppConfiguration config) { 2286 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2287 "Need BLUETOOTH permission"); 2288 synchronized (mBluetoothHealthProfileHandler) { 2289 return mBluetoothHealthProfileHandler.connectChannelToSource(device, 2290 config); 2291 } 2292 } 2293 2294 public boolean connectChannelToSink(BluetoothDevice device, 2295 BluetoothHealthAppConfiguration config, int channelType) { 2296 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2297 "Need BLUETOOTH permission"); 2298 synchronized (mBluetoothHealthProfileHandler) { 2299 return mBluetoothHealthProfileHandler.connectChannel(device, config, 2300 channelType); 2301 } 2302 } 2303 2304 public boolean disconnectChannel(BluetoothDevice device, 2305 BluetoothHealthAppConfiguration config, int id) { 2306 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2307 "Need BLUETOOTH permission"); 2308 synchronized (mBluetoothHealthProfileHandler) { 2309 return mBluetoothHealthProfileHandler.disconnectChannel(device, config, id); 2310 } 2311 } 2312 2313 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, 2314 BluetoothHealthAppConfiguration config) { 2315 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2316 "Need BLUETOOTH permission"); 2317 synchronized (mBluetoothHealthProfileHandler) { 2318 return mBluetoothHealthProfileHandler.getMainChannelFd(device, config); 2319 } 2320 } 2321 2322 /*package*/ void onHealthDevicePropertyChanged(String devicePath, 2323 String channelPath) { 2324 synchronized (mBluetoothHealthProfileHandler) { 2325 mBluetoothHealthProfileHandler.onHealthDevicePropertyChanged(devicePath, 2326 channelPath); 2327 } 2328 } 2329 2330 /*package*/ void onHealthDeviceChannelChanged(String devicePath, 2331 String channelPath, boolean exists) { 2332 synchronized(mBluetoothHealthProfileHandler) { 2333 mBluetoothHealthProfileHandler.onHealthDeviceChannelChanged(devicePath, 2334 channelPath, exists); 2335 } 2336 } 2337 2338 /*package*/ void onHealthDeviceChannelConnectionError(int channelCode, 2339 int newState) { 2340 synchronized(mBluetoothHealthProfileHandler) { 2341 mBluetoothHealthProfileHandler.onHealthDeviceChannelConnectionError(channelCode, 2342 newState); 2343 } 2344 } 2345 2346 public int getHealthDeviceConnectionState(BluetoothDevice device) { 2347 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2348 "Need BLUETOOTH permission"); 2349 synchronized (mBluetoothHealthProfileHandler) { 2350 return mBluetoothHealthProfileHandler.getHealthDeviceConnectionState(device); 2351 } 2352 } 2353 2354 public List<BluetoothDevice> getConnectedHealthDevices() { 2355 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2356 "Need BLUETOOTH permission"); 2357 synchronized (mBluetoothHealthProfileHandler) { 2358 return mBluetoothHealthProfileHandler.getConnectedHealthDevices(); 2359 } 2360 } 2361 2362 public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates( 2363 int[] states) { 2364 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 2365 "Need BLUETOOTH permission"); 2366 synchronized (mBluetoothHealthProfileHandler) { 2367 return mBluetoothHealthProfileHandler. 2368 getHealthDevicesMatchingConnectionStates(states); 2369 } 2370 } 2371 2372 /*package*/boolean notifyIncomingHidConnection(String address) { 2373 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2374 if (state == null) { 2375 return false; 2376 } 2377 Message msg = new Message(); 2378 msg.what = BluetoothDeviceProfileState.CONNECT_HID_INCOMING; 2379 state.sendMessage(msg); 2380 return true; 2381 } 2382 2383 public boolean connectHeadset(String address) { 2384 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false; 2385 2386 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2387 if (state != null) { 2388 Message msg = new Message(); 2389 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING; 2390 msg.obj = state; 2391 mHfpProfileState.sendMessage(msg); 2392 return true; 2393 } 2394 return false; 2395 } 2396 2397 public boolean disconnectHeadset(String address) { 2398 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false; 2399 2400 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2401 if (state != null) { 2402 Message msg = new Message(); 2403 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING; 2404 msg.obj = state; 2405 mHfpProfileState.sendMessage(msg); 2406 return true; 2407 } 2408 return false; 2409 } 2410 2411 public boolean connectSink(String address) { 2412 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false; 2413 2414 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2415 if (state != null) { 2416 Message msg = new Message(); 2417 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING; 2418 msg.obj = state; 2419 mA2dpProfileState.sendMessage(msg); 2420 return true; 2421 } 2422 return false; 2423 } 2424 2425 public boolean disconnectSink(String address) { 2426 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false; 2427 2428 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2429 if (state != null) { 2430 Message msg = new Message(); 2431 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING; 2432 msg.obj = state; 2433 mA2dpProfileState.sendMessage(msg); 2434 return true; 2435 } 2436 return false; 2437 } 2438 2439 BluetoothDeviceProfileState addProfileState(String address, boolean setTrust) { 2440 BluetoothDeviceProfileState state = 2441 new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust); 2442 mDeviceProfileState.put(address, state); 2443 state.start(); 2444 return state; 2445 } 2446 2447 void removeProfileState(String address) { 2448 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2449 if (state == null) return; 2450 2451 state.quit(); 2452 mDeviceProfileState.remove(address); 2453 } 2454 2455 String[] getKnownDevices() { 2456 String[] bonds = null; 2457 String val = getProperty("Devices", true); 2458 if (val != null) { 2459 bonds = val.split(","); 2460 } 2461 return bonds; 2462 } 2463 2464 private void initProfileState() { 2465 String[] bonds = null; 2466 String val = getProperty("Devices", false); 2467 if (val != null) { 2468 bonds = val.split(","); 2469 } 2470 if (bonds == null) { 2471 return; 2472 } 2473 for (String path : bonds) { 2474 String address = getAddressFromObjectPath(path); 2475 BluetoothDeviceProfileState state = addProfileState(address, false); 2476 } 2477 } 2478 2479 private void autoConnect() { 2480 synchronized (this) { 2481 if (!mAllowConnect) { 2482 Log.d(TAG, "Not auto-connecting devices because of temporary BT on state."); 2483 return; 2484 } 2485 } 2486 2487 String[] bonds = getKnownDevices(); 2488 if (bonds == null) { 2489 return; 2490 } 2491 for (String path : bonds) { 2492 String address = getAddressFromObjectPath(path); 2493 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2494 if (state != null) { 2495 Message msg = new Message(); 2496 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES; 2497 state.sendMessage(msg); 2498 } 2499 } 2500 } 2501 2502 public boolean notifyIncomingConnection(String address, boolean rejected) { 2503 synchronized (this) { 2504 if (!mAllowConnect) { 2505 Log.d(TAG, "Not allowing incoming connection because of temporary BT on state."); 2506 return false; 2507 } 2508 } 2509 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2510 if (state != null) { 2511 Message msg = new Message(); 2512 if (rejected) { 2513 if (mA2dpService.getPriority(getRemoteDevice(address)) >= 2514 BluetoothProfile.PRIORITY_ON) { 2515 msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES; 2516 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING; 2517 state.sendMessageDelayed(msg, 2518 BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY); 2519 } 2520 } else { 2521 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING; 2522 state.sendMessage(msg); 2523 } 2524 return true; 2525 } 2526 return false; 2527 } 2528 2529 /*package*/ boolean notifyIncomingA2dpConnection(String address, boolean rejected) { 2530 synchronized (this) { 2531 if (!mAllowConnect) { 2532 Log.d(TAG, "Not allowing a2dp connection because of temporary BT on state."); 2533 return false; 2534 } 2535 } 2536 2537 BluetoothDeviceProfileState state = mDeviceProfileState.get(address); 2538 if (state != null) { 2539 Message msg = new Message(); 2540 if (rejected) { 2541 if (mHeadsetProxy.getPriority(getRemoteDevice(address)) >= 2542 BluetoothProfile.PRIORITY_ON) { 2543 msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES; 2544 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING; 2545 state.sendMessageDelayed(msg, 2546 BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY); 2547 } 2548 } else { 2549 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING; 2550 state.sendMessage(msg); 2551 } 2552 return true; 2553 } 2554 return false; 2555 } 2556 2557 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) { 2558 mA2dpService = a2dpService; 2559 } 2560 2561 /*package*/ Integer getAuthorizationAgentRequestData(String address) { 2562 Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address); 2563 return data; 2564 } 2565 2566 public void sendProfileStateMessage(int profile, int cmd) { 2567 Message msg = new Message(); 2568 msg.what = cmd; 2569 if (profile == BluetoothProfileState.HFP) { 2570 mHfpProfileState.sendMessage(msg); 2571 } else if (profile == BluetoothProfileState.A2DP) { 2572 mA2dpProfileState.sendMessage(msg); 2573 } 2574 } 2575 2576 public int getAdapterConnectionState() { 2577 return mAdapterConnectionState; 2578 } 2579 2580 public int getProfileConnectionState(int profile) { 2581 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 2582 2583 Pair<Integer, Integer> state = mProfileConnectionState.get(profile); 2584 if (state == null) return BluetoothProfile.STATE_DISCONNECTED; 2585 2586 return state.first; 2587 } 2588 2589 private void updateProfileConnectionState(int profile, int newState, int oldState) { 2590 // mProfileConnectionState is a hashmap - 2591 // <Integer, Pair<Integer, Integer>> 2592 // The key is the profile, the value is a pair. first element 2593 // is the state and the second element is the number of devices 2594 // in that state. 2595 int numDev = 1; 2596 int newHashState = newState; 2597 boolean update = true; 2598 2599 // The following conditions are considered in this function: 2600 // 1. If there is no record of profile and state - update 2601 // 2. If a new device's state is current hash state - increment 2602 // number of devices in the state. 2603 // 3. If a state change has happened to Connected or Connecting 2604 // (if current state is not connected), update. 2605 // 4. If numDevices is 1 and that device state is being updated, update 2606 // 5. If numDevices is > 1 and one of the devices is changing state, 2607 // decrement numDevices but maintain oldState if it is Connected or 2608 // Connecting 2609 Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile); 2610 if (stateNumDev != null) { 2611 int currHashState = stateNumDev.first; 2612 numDev = stateNumDev.second; 2613 2614 if (newState == currHashState) { 2615 numDev ++; 2616 } else if (newState == BluetoothProfile.STATE_CONNECTED || 2617 (newState == BluetoothProfile.STATE_CONNECTING && 2618 currHashState != BluetoothProfile.STATE_CONNECTED)) { 2619 numDev = 1; 2620 } else if (numDev == 1 && oldState == currHashState) { 2621 update = true; 2622 } else if (numDev > 1 && oldState == currHashState) { 2623 numDev --; 2624 2625 if (currHashState == BluetoothProfile.STATE_CONNECTED || 2626 currHashState == BluetoothProfile.STATE_CONNECTING) { 2627 newHashState = currHashState; 2628 } 2629 } else { 2630 update = false; 2631 } 2632 } 2633 2634 if (update) { 2635 mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState, 2636 numDev)); 2637 } 2638 } 2639 2640 public synchronized void sendConnectionStateChange(BluetoothDevice 2641 device, int profile, int state, int prevState) { 2642 // Since this is a binder call check if Bluetooth is on still 2643 if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return; 2644 2645 if (!validateProfileConnectionState(state) || 2646 !validateProfileConnectionState(prevState)) { 2647 // Previously, an invalid state was broadcast anyway, 2648 // with the invalid state converted to -1 in the intent. 2649 // Better to log an error and not send an intent with 2650 // invalid contents or set mAdapterConnectionState to -1. 2651 Log.e(TAG, "Error in sendConnectionStateChange: " 2652 + "prevState " + prevState + " state " + state); 2653 return; 2654 } 2655 2656 updateProfileConnectionState(profile, state, prevState); 2657 2658 if (updateCountersAndCheckForConnectionStateChange(state, prevState)) { 2659 mAdapterConnectionState = state; 2660 2661 if (state == BluetoothProfile.STATE_DISCONNECTED) { 2662 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.ALL_DEVICES_DISCONNECTED); 2663 } 2664 2665 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); 2666 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2667 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 2668 convertToAdapterState(state)); 2669 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, 2670 convertToAdapterState(prevState)); 2671 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2672 mContext.sendBroadcast(intent, BLUETOOTH_PERM); 2673 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": " 2674 + prevState + " -> " + state); 2675 } 2676 } 2677 2678 private boolean validateProfileConnectionState(int state) { 2679 return (state == BluetoothProfile.STATE_DISCONNECTED || 2680 state == BluetoothProfile.STATE_CONNECTING || 2681 state == BluetoothProfile.STATE_CONNECTED || 2682 state == BluetoothProfile.STATE_DISCONNECTING); 2683 } 2684 2685 private int convertToAdapterState(int state) { 2686 switch (state) { 2687 case BluetoothProfile.STATE_DISCONNECTED: 2688 return BluetoothAdapter.STATE_DISCONNECTED; 2689 case BluetoothProfile.STATE_DISCONNECTING: 2690 return BluetoothAdapter.STATE_DISCONNECTING; 2691 case BluetoothProfile.STATE_CONNECTED: 2692 return BluetoothAdapter.STATE_CONNECTED; 2693 case BluetoothProfile.STATE_CONNECTING: 2694 return BluetoothAdapter.STATE_CONNECTING; 2695 } 2696 Log.e(TAG, "Error in convertToAdapterState"); 2697 return -1; 2698 } 2699 2700 private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) { 2701 switch (prevState) { 2702 case BluetoothProfile.STATE_CONNECTING: 2703 mProfilesConnecting--; 2704 break; 2705 2706 case BluetoothProfile.STATE_CONNECTED: 2707 mProfilesConnected--; 2708 break; 2709 2710 case BluetoothProfile.STATE_DISCONNECTING: 2711 mProfilesDisconnecting--; 2712 break; 2713 } 2714 2715 switch (state) { 2716 case BluetoothProfile.STATE_CONNECTING: 2717 mProfilesConnecting++; 2718 return (mProfilesConnected == 0 && mProfilesConnecting == 1); 2719 2720 case BluetoothProfile.STATE_CONNECTED: 2721 mProfilesConnected++; 2722 return (mProfilesConnected == 1); 2723 2724 case BluetoothProfile.STATE_DISCONNECTING: 2725 mProfilesDisconnecting++; 2726 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1); 2727 2728 case BluetoothProfile.STATE_DISCONNECTED: 2729 return (mProfilesConnected == 0 && mProfilesConnecting == 0); 2730 2731 default: 2732 return true; 2733 } 2734 } 2735 2736 private void createIncomingConnectionStateFile() { 2737 File f = new File(INCOMING_CONNECTION_FILE); 2738 if (!f.exists()) { 2739 try { 2740 f.createNewFile(); 2741 } catch (IOException e) { 2742 Log.e(TAG, "IOException: cannot create file"); 2743 } 2744 } 2745 } 2746 2747 /** @hide */ 2748 public Pair<Integer, String> getIncomingState(String address) { 2749 if (mIncomingConnections.isEmpty()) { 2750 createIncomingConnectionStateFile(); 2751 readIncomingConnectionState(); 2752 } 2753 return mIncomingConnections.get(address); 2754 } 2755 2756 private void readIncomingConnectionState() { 2757 synchronized(mIncomingConnections) { 2758 FileInputStream fstream = null; 2759 try { 2760 fstream = new FileInputStream(INCOMING_CONNECTION_FILE); 2761 DataInputStream in = new DataInputStream(fstream); 2762 BufferedReader file = new BufferedReader(new InputStreamReader(in)); 2763 String line; 2764 while((line = file.readLine()) != null) { 2765 line = line.trim(); 2766 if (line.length() == 0) continue; 2767 String[] value = line.split(","); 2768 if (value != null && value.length == 3) { 2769 Integer val1 = Integer.parseInt(value[1]); 2770 Pair<Integer, String> val = new Pair(val1, value[2]); 2771 mIncomingConnections.put(value[0], val); 2772 } 2773 } 2774 } catch (FileNotFoundException e) { 2775 log("FileNotFoundException: readIncomingConnectionState" + e.toString()); 2776 } catch (IOException e) { 2777 log("IOException: readIncomingConnectionState" + e.toString()); 2778 } finally { 2779 if (fstream != null) { 2780 try { 2781 fstream.close(); 2782 } catch (IOException e) { 2783 // Ignore 2784 } 2785 } 2786 } 2787 } 2788 } 2789 2790 private void truncateIncomingConnectionFile() { 2791 RandomAccessFile r = null; 2792 try { 2793 r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw"); 2794 r.setLength(0); 2795 } catch (FileNotFoundException e) { 2796 log("FileNotFoundException: truncateIncomingConnectionState" + e.toString()); 2797 } catch (IOException e) { 2798 log("IOException: truncateIncomingConnectionState" + e.toString()); 2799 } finally { 2800 if (r != null) { 2801 try { 2802 r.close(); 2803 } catch (IOException e) { 2804 // ignore 2805 } 2806 } 2807 } 2808 } 2809 2810 /** @hide */ 2811 public void writeIncomingConnectionState(String address, Pair<Integer, String> data) { 2812 synchronized(mIncomingConnections) { 2813 mIncomingConnections.put(address, data); 2814 2815 truncateIncomingConnectionFile(); 2816 BufferedWriter out = null; 2817 StringBuilder value = new StringBuilder(); 2818 try { 2819 out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true)); 2820 for (String devAddress: mIncomingConnections.keySet()) { 2821 Pair<Integer, String> val = mIncomingConnections.get(devAddress); 2822 value.append(devAddress); 2823 value.append(","); 2824 value.append(val.first.toString()); 2825 value.append(","); 2826 value.append(val.second); 2827 value.append("\n"); 2828 } 2829 out.write(value.toString()); 2830 } catch (FileNotFoundException e) { 2831 log("FileNotFoundException: writeIncomingConnectionState" + e.toString()); 2832 } catch (IOException e) { 2833 log("IOException: writeIncomingConnectionState" + e.toString()); 2834 } finally { 2835 if (out != null) { 2836 try { 2837 out.close(); 2838 } catch (IOException e) { 2839 // Ignore 2840 } 2841 } 2842 } 2843 } 2844 } 2845 2846 private static void log(String msg) { 2847 Log.d(TAG, msg); 2848 } 2849 2850 private native static void classInitNative(); 2851 private native void initializeNativeDataNative(); 2852 private native boolean setupNativeDataNative(); 2853 private native boolean tearDownNativeDataNative(); 2854 private native void cleanupNativeDataNative(); 2855 /*package*/ native String getAdapterPathNative(); 2856 2857 private native int isEnabledNative(); 2858 /*package*/ native int enableNative(); 2859 /*package*/ native int disableNative(); 2860 2861 /*package*/ native Object[] getAdapterPropertiesNative(); 2862 private native Object[] getDevicePropertiesNative(String objectPath); 2863 private native boolean setAdapterPropertyStringNative(String key, String value); 2864 private native boolean setAdapterPropertyIntegerNative(String key, int value); 2865 private native boolean setAdapterPropertyBooleanNative(String key, int value); 2866 2867 private native boolean startDiscoveryNative(); 2868 private native boolean stopDiscoveryNative(); 2869 2870 private native boolean createPairedDeviceNative(String address, int timeout_ms); 2871 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms); 2872 private native byte[] readAdapterOutOfBandDataNative(); 2873 2874 private native boolean cancelDeviceCreationNative(String address); 2875 private native boolean removeDeviceNative(String objectPath); 2876 private native int getDeviceServiceChannelNative(String objectPath, String uuid, 2877 int attributeId); 2878 2879 private native boolean cancelPairingUserInputNative(String address, int nativeData); 2880 private native boolean setPinNative(String address, String pin, int nativeData); 2881 private native boolean setPasskeyNative(String address, int passkey, int nativeData); 2882 private native boolean setPairingConfirmationNative(String address, boolean confirm, 2883 int nativeData); 2884 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash, 2885 byte[] randomizer, int nativeData); 2886 2887 private native boolean setDevicePropertyBooleanNative(String objectPath, String key, 2888 int value); 2889 private native boolean setDevicePropertyStringNative(String objectPath, String key, 2890 String value); 2891 private native boolean createDeviceNative(String address); 2892 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern); 2893 2894 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb, 2895 short channel); 2896 private native boolean removeServiceRecordNative(int handle); 2897 private native boolean setLinkTimeoutNative(String path, int num_slots); 2898 2899 native boolean connectInputDeviceNative(String path); 2900 native boolean disconnectInputDeviceNative(String path); 2901 2902 native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge); 2903 native boolean connectPanDeviceNative(String path, String dstRole); 2904 native boolean disconnectPanDeviceNative(String path); 2905 native boolean disconnectPanServerDeviceNative(String path, 2906 String address, String iface); 2907 2908 private native int[] addReservedServiceRecordsNative(int[] uuuids); 2909 private native boolean removeReservedServiceRecordsNative(int[] handles); 2910 2911 // Health API 2912 native String registerHealthApplicationNative(int dataType, String role, String name, 2913 String channelType); 2914 native String registerHealthApplicationNative(int dataType, String role, String name); 2915 native boolean unregisterHealthApplicationNative(String path); 2916 native boolean createChannelNative(String devicePath, String appPath, String channelType, 2917 int code); 2918 native boolean destroyChannelNative(String devicePath, String channelpath, int code); 2919 native String getMainChannelNative(String path); 2920 native String getChannelApplicationNative(String channelPath); 2921 native ParcelFileDescriptor getChannelFdNative(String channelPath); 2922 native boolean releaseChannelFdNative(String channelPath); 2923 native boolean setAuthorizationNative(String address, boolean value, int data); 2924 } 2925