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