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