2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * java/services/com/android/server/BluetoothService.java
20 * and make the contructor package private again.
25 package android.server;
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.BluetoothClass;
29 import android.bluetooth.BluetoothDevice;
30 import android.bluetooth.BluetoothHeadset;
31 import android.bluetooth.BluetoothDeviceProfileState;
32 import android.bluetooth.BluetoothProfileState;
33 import android.bluetooth.BluetoothSocket;
34 import android.bluetooth.BluetoothUuid;
35 import android.bluetooth.IBluetooth;
36 import android.bluetooth.IBluetoothCallback;
37 import android.content.BroadcastReceiver;
38 import android.content.ContentResolver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.SharedPreferences;
43 import android.os.Binder;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.os.Message;
47 import android.os.ParcelUuid;
48 import android.os.RemoteException;
49 import android.os.ServiceManager;
50 import android.os.SystemService;
51 import android.provider.Settings;
52 import android.util.Log;
53 import android.util.Pair;
55 import com.android.internal.app.IBatteryStats;
57 import java.io.BufferedInputStream;
58 import java.io.BufferedReader;
59 import java.io.BufferedWriter;
60 import java.io.DataInputStream;
62 import java.io.FileDescriptor;
63 import java.io.FileInputStream;
64 import java.io.FileNotFoundException;
65 import java.io.FileOutputStream;
66 import java.io.FileWriter;
67 import java.io.IOException;
68 import java.io.InputStreamReader;
69 import java.io.PrintWriter;
70 import java.io.UnsupportedEncodingException;
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.HashMap;
74 import java.util.Iterator;
77 public class BluetoothService extends IBluetooth.Stub {
78 private static final String TAG = "BluetoothService";
79 private static final boolean DBG = true;
81 private int mNativeData;
82 private BluetoothEventLoop mEventLoop;
83 private boolean mIsAirplaneSensitive;
84 private boolean mIsAirplaneToggleable;
85 private int mBluetoothState;
86 private boolean mRestart = false; // need to call enable() after disable()
87 private boolean mIsDiscovering;
89 private BluetoothAdapter mAdapter; // constant after init()
90 private final BondState mBondState = new BondState(); // local cache of bondings
91 private final IBatteryStats mBatteryStats;
92 private final Context mContext;
94 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
95 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
97 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
98 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
100 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
101 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
103 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
104 private static final int MESSAGE_FINISH_DISABLE = 2;
105 private static final int MESSAGE_UUID_INTENT = 3;
106 private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
107 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 5;
109 // The time (in millisecs) to delay the pairing attempt after the first
110 // auto pairing attempt fails. We use an exponential delay with
111 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
112 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
113 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
114 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
116 // The timeout used to sent the UUIDs Intent
117 // This timeout should be greater than the page timeout
118 private static final int UUID_INTENT_DELAY = 6000;
120 /** Always retrieve RFCOMM channel for these SDP UUIDs */
121 private static final ParcelUuid[] RFCOMM_UUIDS = {
122 BluetoothUuid.Handsfree,
124 BluetoothUuid.ObexObjectPush };
126 // TODO(): Optimize all these string handling
127 private final Map<String, String> mAdapterProperties;
128 private final HashMap<String, Map<String, String>> mDeviceProperties;
130 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
131 private final ArrayList<String> mUuidIntentTracker;
132 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
134 private final HashMap<Integer, Integer> mServiceRecordToPid;
136 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
137 private final BluetoothProfileState mA2dpProfileState;
138 private final BluetoothProfileState mHfpProfileState;
140 private BluetoothA2dpService mA2dpService;
141 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
143 private static String mDockAddress;
144 private String mDockPin;
146 private static class RemoteService {
147 public String address;
148 public ParcelUuid uuid;
149 public RemoteService(String address, ParcelUuid uuid) {
150 this.address = address;
154 public boolean equals(Object o) {
155 if (o instanceof RemoteService) {
156 RemoteService service = (RemoteService)o;
157 return address.equals(service.address) && uuid.equals(service.uuid);
163 public int hashCode() {
165 hash = hash * 31 + (address == null ? 0 : address.hashCode());
166 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
175 public BluetoothService(Context context) {
178 // Need to do this in place of:
179 // mBatteryStats = BatteryStatsService.getService();
180 // Since we can not import BatteryStatsService from here. This class really needs to be
181 // moved to java/services/com/android/server/
182 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
184 initializeNativeDataNative();
186 if (isEnabledNative() == 1) {
187 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
191 mBluetoothState = BluetoothAdapter.STATE_OFF;
192 mIsDiscovering = false;
193 mAdapterProperties = new HashMap<String, String>();
194 mDeviceProperties = new HashMap<String, Map<String,String>>();
196 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
197 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
198 mUuidIntentTracker = new ArrayList<String>();
199 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
200 mServiceRecordToPid = new HashMap<Integer, Integer>();
201 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
202 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
203 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
205 mHfpProfileState.start();
206 mA2dpProfileState.start();
208 IntentFilter filter = new IntentFilter();
209 registerForAirplaneMode(filter);
211 filter.addAction(Intent.ACTION_DOCK_EVENT);
212 mContext.registerReceiver(mReceiver, filter);
215 public static synchronized String readDockBluetoothAddress() {
216 if (mDockAddress != null) return mDockAddress;
218 BufferedInputStream file = null;
221 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
222 byte[] address = new byte[17];
224 dockAddress = new String(address);
225 dockAddress = dockAddress.toUpperCase();
226 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
227 mDockAddress = dockAddress;
230 log("CheckBluetoothAddress failed for car dock address:" + dockAddress);
232 } catch (FileNotFoundException e) {
233 log("FileNotFoundException while trying to read dock address");
234 } catch (IOException e) {
235 log("IOException while trying to read dock address");
240 } catch (IOException e) {
249 private synchronized boolean writeDockPin() {
250 BufferedWriter out = null;
252 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
254 // Generate a random 4 digit pin between 0000 and 9999
255 // This is not truly random but good enough for our purposes.
256 int pin = (int) Math.floor(Math.random() * 10000);
258 mDockPin = String.format("%04d", pin);
261 } catch (FileNotFoundException e) {
262 log("FileNotFoundException while trying to write dock pairing pin");
263 } catch (IOException e) {
264 log("IOException while while trying to write dock pairing pin");
269 } catch (IOException e) {
278 /*package*/ synchronized String getDockPin() {
282 public synchronized void initAfterRegistration() {
283 mAdapter = BluetoothAdapter.getDefaultAdapter();
284 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
288 protected void finalize() throws Throwable {
289 mContext.unregisterReceiver(mReceiver);
291 cleanupNativeDataNative();
297 public boolean isEnabled() {
298 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
299 return isEnabledInternal();
302 private boolean isEnabledInternal() {
303 return mBluetoothState == BluetoothAdapter.STATE_ON;
306 public int getBluetoothState() {
307 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
308 return mBluetoothState;
313 * Bring down bluetooth and disable BT in settings. Returns true on success.
315 public boolean disable() {
316 return disable(true);
320 * Bring down bluetooth. Returns true on success.
322 * @param saveSetting If true, persist the new setting
324 public synchronized boolean disable(boolean saveSetting) {
325 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
327 switch (mBluetoothState) {
328 case BluetoothAdapter.STATE_OFF:
330 case BluetoothAdapter.STATE_ON:
335 if (mEnableThread != null && mEnableThread.isAlive()) {
338 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
339 mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
341 // Allow 3 seconds for profiles to gracefully disconnect
342 // TODO: Introduce a callback mechanism so that each profile can notify
343 // BluetoothService when it is done shutting down
344 mHandler.sendMessageDelayed(
345 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
350 private synchronized void finishDisable(boolean saveSetting) {
351 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
355 tearDownNativeDataNative();
358 // mark in progress bondings as cancelled
359 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
360 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
361 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
365 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
366 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
367 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
369 mIsDiscovering = false;
370 mAdapterProperties.clear();
371 mServiceRecordToPid.clear();
374 persistBluetoothOnSetting(false);
377 setBluetoothState(BluetoothAdapter.STATE_OFF);
379 // Log bluetooth off to battery stats.
380 long ident = Binder.clearCallingIdentity();
382 mBatteryStats.noteBluetoothOff();
383 } catch (RemoteException e) {
385 Binder.restoreCallingIdentity(ident);
394 /** Bring up BT and persist BT on in settings */
395 public boolean enable() {
400 * Enable this Bluetooth device, asynchronously.
401 * This turns on/off the underlying hardware.
403 * @param saveSetting If true, persist the new state of BT in settings
404 * @return True on success (so far)
406 public synchronized boolean enable(boolean saveSetting) {
407 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
408 "Need BLUETOOTH_ADMIN permission");
410 // Airplane mode can prevent Bluetooth radio from being turned on.
411 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
414 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
417 if (mEnableThread != null && mEnableThread.isAlive()) {
420 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
421 mEnableThread = new EnableThread(saveSetting);
422 mEnableThread.start();
426 /** Forcibly restart Bluetooth if it is on */
427 /* package */ synchronized void restart() {
428 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
432 if (!disable(false)) {
437 private synchronized void setBluetoothState(int state) {
438 if (state == mBluetoothState) {
442 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
444 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
445 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
446 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
447 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
449 mBluetoothState = state;
451 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
454 private final Handler mHandler = new Handler() {
456 public void handleMessage(Message msg) {
458 case MESSAGE_REGISTER_SDP_RECORDS:
459 if (!isEnabledInternal()) {
462 // SystemService.start() forks sdptool to register service
463 // records. It can fail to register some records if it is
464 // forked multiple times in a row, probably because there is
465 // some race in sdptool or bluez when operated in parallel.
466 // As a workaround, delay 500ms between each fork of sdptool.
467 // TODO: Don't fork sdptool in order to register service
468 // records, use a DBUS call instead.
471 Log.d(TAG, "Registering hfag record");
472 SystemService.start("hfag");
473 mHandler.sendMessageDelayed(
474 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 2, -1), 500);
477 Log.d(TAG, "Registering hsag record");
478 SystemService.start("hsag");
479 mHandler.sendMessageDelayed(
480 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 3, -1), 500);
483 Log.d(TAG, "Registering opush record");
484 SystemService.start("opush");
485 mHandler.sendMessageDelayed(
486 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 4, -1), 500);
489 Log.d(TAG, "Registering pbap record");
490 SystemService.start("pbap");
494 case MESSAGE_FINISH_DISABLE:
495 finishDisable(msg.arg1 != 0);
497 case MESSAGE_UUID_INTENT:
498 String address = (String)msg.obj;
499 if (address != null) {
500 sendUuidIntent(address);
501 makeServiceChannelCallbacks(address);
504 case MESSAGE_DISCOVERABLE_TIMEOUT:
506 if (isEnabledInternal()) {
507 // TODO: Switch back to the previous scan mode
508 // This is ok for now, because we only use
509 // CONNECTABLE and CONNECTABLE_DISCOVERABLE
510 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
513 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
514 address = (String)msg.obj;
515 if (address != null) {
524 private EnableThread mEnableThread;
526 private class EnableThread extends Thread {
527 private final boolean mSaveSetting;
528 public EnableThread(boolean saveSetting) {
529 mSaveSetting = saveSetting;
532 boolean res = (enableNative() == 0);
535 boolean running = false;
536 while ((retryCount-- > 0) && !running) {
538 // it may take a momement for the other thread to do its
539 // thing. Check periodically for a while.
541 while ((pollCount-- > 0) && !running) {
542 if (mEventLoop.isEventLoopRunning()) {
548 } catch (InterruptedException e) {}
552 log("bt EnableThread giving up");
560 if (!setupNativeDataNative()) {
564 persistBluetoothOnSetting(true);
566 mIsDiscovering = false;
567 mBondState.readAutoPairingData();
568 mBondState.loadBondState();
570 mHandler.sendMessageDelayed(
571 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
573 // Log bluetooth on to battery stats.
574 long ident = Binder.clearCallingIdentity();
576 mBatteryStats.noteBluetoothOn();
577 } catch (RemoteException e) {
579 Binder.restoreCallingIdentity(ident);
583 mEnableThread = null;
585 setBluetoothState(res ?
586 BluetoothAdapter.STATE_ON :
587 BluetoothAdapter.STATE_OFF);
591 String[] propVal = {"Pairable", getProperty("Pairable")};
592 mEventLoop.onPropertyChanged(propVal);
595 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
602 private void persistBluetoothOnSetting(boolean bluetoothOn) {
603 long origCallerIdentityToken = Binder.clearCallingIdentity();
604 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
605 bluetoothOn ? 1 : 0);
606 Binder.restoreCallingIdentity(origCallerIdentityToken);
609 /*package*/ synchronized boolean attemptAutoPair(String address) {
610 if (!mBondState.hasAutoPairingFailed(address) &&
611 !mBondState.isAutoPairingBlacklisted(address)) {
612 mBondState.attempt(address);
613 setPin(address, BluetoothDevice.convertPinToBytes("0000"));
619 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
620 if (result == BluetoothDevice.BOND_SUCCESS) {
621 setBondState(address, BluetoothDevice.BOND_BONDED);
622 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
623 mBondState.clearPinAttempts(address);
625 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
626 mBondState.getAttempt(address) == 1) {
627 mBondState.addAutoPairingFailure(address);
628 pairingAttempt(address, result);
629 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
630 mBondState.isAutoPairingAttemptsInProgress(address)) {
631 pairingAttempt(address, result);
633 setBondState(address, BluetoothDevice.BOND_NONE, result);
634 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
635 mBondState.clearPinAttempts(address);
640 /*package*/ synchronized String getPendingOutgoingBonding() {
641 return mBondState.getPendingOutgoingBonding();
644 private void pairingAttempt(String address, int result) {
645 // This happens when our initial guess of "0000" as the pass key
646 // fails. Try to create the bond again and display the pin dialog
647 // to the user. Use back-off while posting the delayed
648 // message. The initial value is
649 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
650 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
651 // reached, display an error to the user.
652 int attempt = mBondState.getAttempt(address);
653 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
654 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
655 mBondState.clearPinAttempts(address);
656 setBondState(address, BluetoothDevice.BOND_NONE, result);
660 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
661 message.obj = address;
662 boolean postResult = mHandler.sendMessageDelayed(message,
663 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
665 mBondState.clearPinAttempts(address);
666 setBondState(address,
667 BluetoothDevice.BOND_NONE, result);
670 mBondState.attempt(address);
673 /** local cache of bonding state.
674 /* we keep our own state to track the intermediate state BONDING, which
675 /* bluez does not track.
676 * All addresses must be passed in upper case.
678 public class BondState {
679 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
680 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
682 private static final String AUTO_PAIRING_BLACKLIST =
683 "/etc/bluetooth/auto_pairing.conf";
684 private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST =
685 "/data/misc/bluetooth/dynamic_auto_pairing.conf";
686 private ArrayList<String> mAutoPairingAddressBlacklist;
687 private ArrayList<String> mAutoPairingExactNameBlacklist;
688 private ArrayList<String> mAutoPairingPartialNameBlacklist;
689 // Addresses added to blacklist dynamically based on usage.
690 private ArrayList<String> mAutoPairingDynamicAddressBlacklist;
693 // If this is an outgoing connection, store the address.
694 // There can be only 1 pending outgoing connection at a time,
695 private String mPendingOutgoingBonding;
697 private synchronized void setPendingOutgoingBonding(String address) {
698 mPendingOutgoingBonding = address;
701 public synchronized String getPendingOutgoingBonding() {
702 return mPendingOutgoingBonding;
705 public synchronized void loadBondState() {
706 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
709 String []bonds = null;
710 String val = getPropertyInternal("Devices");
712 bonds = val.split(",");
718 if (DBG) log("found " + bonds.length + " bonded devices");
719 for (String device : bonds) {
720 mState.put(getAddressFromObjectPath(device).toUpperCase(),
721 BluetoothDevice.BOND_BONDED);
725 public synchronized void setBondState(String address, int state) {
726 setBondState(address, state, 0);
729 /** reason is ignored unless state == BOND_NOT_BONDED */
730 public synchronized void setBondState(String address, int state, int reason) {
731 int oldState = getBondState(address);
732 if (oldState == state) {
736 // Check if this was an pending outgoing bonding.
737 // If yes, reset the state.
738 if (oldState == BluetoothDevice.BOND_BONDING) {
739 if (address.equals(mPendingOutgoingBonding)) {
740 mPendingOutgoingBonding = null;
744 if (state == BluetoothDevice.BOND_BONDED) {
745 addProfileState(address);
746 } else if (state == BluetoothDevice.BOND_NONE) {
747 removeProfileState(address);
750 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
752 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
753 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
754 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
755 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
756 if (state == BluetoothDevice.BOND_NONE) {
758 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
759 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
760 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
762 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
763 mState.remove(address);
765 mState.put(address, state);
768 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
771 public boolean isAutoPairingBlacklisted(String address) {
772 if (mAutoPairingAddressBlacklist != null) {
773 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
774 if (address.startsWith(blacklistAddress)) return true;
778 if (mAutoPairingDynamicAddressBlacklist != null) {
779 for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) {
780 if (address.equals(blacklistAddress)) return true;
783 String name = getRemoteName(address);
785 if (mAutoPairingExactNameBlacklist != null) {
786 for (String blacklistName : mAutoPairingExactNameBlacklist) {
787 if (name.equals(blacklistName)) return true;
791 if (mAutoPairingPartialNameBlacklist != null) {
792 for (String blacklistName : mAutoPairingPartialNameBlacklist) {
793 if (name.startsWith(blacklistName)) return true;
800 public synchronized int getBondState(String address) {
801 Integer state = mState.get(address);
803 return BluetoothDevice.BOND_NONE;
805 return state.intValue();
808 /*package*/ synchronized String[] listInState(int state) {
809 ArrayList<String> result = new ArrayList<String>(mState.size());
810 for (Map.Entry<String, Integer> e : mState.entrySet()) {
811 if (e.getValue().intValue() == state) {
812 result.add(e.getKey());
815 return result.toArray(new String[result.size()]);
818 public synchronized void addAutoPairingFailure(String address) {
819 if (mAutoPairingDynamicAddressBlacklist == null) {
820 mAutoPairingDynamicAddressBlacklist = new ArrayList<String>();
823 updateAutoPairingData(address);
824 mAutoPairingDynamicAddressBlacklist.add(address);
827 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
828 return getAttempt(address) != 0;
831 public synchronized void clearPinAttempts(String address) {
832 mPinAttempt.remove(address);
835 public synchronized boolean hasAutoPairingFailed(String address) {
836 if (mAutoPairingDynamicAddressBlacklist == null) return false;
838 return mAutoPairingDynamicAddressBlacklist.contains(address);
841 public synchronized int getAttempt(String address) {
842 Integer attempt = mPinAttempt.get(address);
843 if (attempt == null) {
846 return attempt.intValue();
849 public synchronized void attempt(String address) {
850 Integer attempt = mPinAttempt.get(address);
852 if (attempt == null) {
855 newAttempt = attempt.intValue() + 1;
857 mPinAttempt.put(address, new Integer(newAttempt));
860 private void copyAutoPairingData() {
862 FileInputStream in = null;
863 FileOutputStream out = null;
865 file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST);
866 if (file.exists()) return;
868 in = new FileInputStream(AUTO_PAIRING_BLACKLIST);
869 out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
871 byte[] buf = new byte[1024];
873 while ((len = in.read(buf)) > 0) {
874 out.write(buf, 0, len);
876 } catch (FileNotFoundException e) {
877 log("FileNotFoundException: in copyAutoPairingData");
878 } catch (IOException e) {
879 log("IOException: in copyAutoPairingData");
882 if (in != null) in.close();
883 if (out != null) out.close();
884 } catch (IOException e) {}
888 public void readAutoPairingData() {
889 if (mAutoPairingAddressBlacklist != null) return;
890 copyAutoPairingData();
891 FileInputStream fstream = null;
893 fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
894 DataInputStream in = new DataInputStream(fstream);
895 BufferedReader file = new BufferedReader(new InputStreamReader(in));
897 while((line = file.readLine()) != null) {
899 if (line.length() == 0 || line.startsWith("//")) continue;
900 String[] value = line.split("=");
901 if (value != null && value.length == 2) {
902 String[] val = value[1].split(",");
903 if (value[0].equalsIgnoreCase("AddressBlacklist")) {
904 mAutoPairingAddressBlacklist =
905 new ArrayList<String>(Arrays.asList(val));
906 } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) {
907 mAutoPairingExactNameBlacklist =
908 new ArrayList<String>(Arrays.asList(val));
909 } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) {
910 mAutoPairingPartialNameBlacklist =
911 new ArrayList<String>(Arrays.asList(val));
912 } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) {
913 mAutoPairingDynamicAddressBlacklist =
914 new ArrayList<String>(Arrays.asList(val));
916 Log.e(TAG, "Error parsing Auto pairing blacklist file");
920 } catch (FileNotFoundException e) {
921 log("FileNotFoundException: readAutoPairingData" + e.toString());
922 } catch (IOException e) {
923 log("IOException: readAutoPairingData" + e.toString());
925 if (fstream != null) {
928 } catch (IOException e) {
935 // This function adds a bluetooth address to the auto pairing blacklist
936 // file. These addresses are added to DynamicAddressBlacklistSection
937 private void updateAutoPairingData(String address) {
938 BufferedWriter out = null;
940 out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true));
941 StringBuilder str = new StringBuilder();
942 if (mAutoPairingDynamicAddressBlacklist.size() == 0) {
943 str.append("DynamicAddressBlacklist=");
947 out.write(str.toString());
948 } catch (FileNotFoundException e) {
949 log("FileNotFoundException: updateAutoPairingData" + e.toString());
950 } catch (IOException e) {
951 log("IOException: updateAutoPairingData" + e.toString());
956 } catch (IOException e) {
964 private static String toBondStateString(int bondState) {
966 case BluetoothDevice.BOND_NONE:
968 case BluetoothDevice.BOND_BONDING:
970 case BluetoothDevice.BOND_BONDED:
977 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
978 return mAdapterProperties.isEmpty();
981 /*package*/synchronized void getAllProperties() {
983 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
984 mAdapterProperties.clear();
986 String properties[] = (String [])getAdapterPropertiesNative();
987 // The String Array consists of key-value pairs.
988 if (properties == null) {
989 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
993 for (int i = 0; i < properties.length; i++) {
994 String name = properties[i];
995 String newValue = null;
998 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
1001 if (name.equals("Devices") || name.equals("UUIDs")) {
1002 StringBuilder str = new StringBuilder();
1003 len = Integer.valueOf(properties[++i]);
1004 for (int j = 0; j < len; j++) {
1005 str.append(properties[++i]);
1009 newValue = str.toString();
1012 newValue = properties[++i];
1014 mAdapterProperties.put(name, newValue);
1017 // Add adapter object path property.
1018 String adapterPath = getAdapterPathNative();
1019 if (adapterPath != null)
1020 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
1023 /* package */ synchronized void setProperty(String name, String value) {
1024 mAdapterProperties.put(name, value);
1027 public synchronized boolean setName(String name) {
1028 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1029 "Need BLUETOOTH_ADMIN permission");
1033 return setPropertyString("Name", name);
1036 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
1037 // Either have a single property function with Object as the parameter
1038 // or have a function for each property and then obfuscate in the JNI layer.
1039 // The following looks dirty.
1040 private boolean setPropertyString(String key, String value) {
1041 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1042 if (!isEnabledInternal()) return false;
1043 return setAdapterPropertyStringNative(key, value);
1046 private boolean setPropertyInteger(String key, int value) {
1047 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1048 if (!isEnabledInternal()) return false;
1049 return setAdapterPropertyIntegerNative(key, value);
1052 private boolean setPropertyBoolean(String key, boolean value) {
1053 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1054 if (!isEnabledInternal()) return false;
1055 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
1059 * Set the discoverability window for the device. A timeout of zero
1060 * makes the device permanently discoverable (if the device is
1061 * discoverable). Setting the timeout to a nonzero value does not make
1062 * a device discoverable; you need to call setMode() to make the device
1063 * explicitly discoverable.
1065 * @param timeout The discoverable timeout in seconds.
1067 public synchronized boolean setDiscoverableTimeout(int timeout) {
1068 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1069 "Need BLUETOOTH_ADMIN permission");
1070 return setPropertyInteger("DiscoverableTimeout", timeout);
1073 public synchronized boolean setScanMode(int mode, int duration) {
1074 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
1075 "Need WRITE_SECURE_SETTINGS permission");
1076 boolean pairable = false;
1077 boolean discoverable = false;
1080 case BluetoothAdapter.SCAN_MODE_NONE:
1081 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
1083 discoverable = false;
1085 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
1086 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
1088 discoverable = false;
1090 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
1091 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
1093 discoverable = true;
1094 Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT);
1095 mHandler.sendMessageDelayed(msg, duration * 1000);
1096 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
1099 Log.w(TAG, "Requested invalid scan mode " + mode);
1102 setPropertyBoolean("Pairable", pairable);
1103 setPropertyBoolean("Discoverable", discoverable);
1108 /*package*/ synchronized String getProperty(String name) {
1109 if (!isEnabledInternal()) return null;
1110 return getPropertyInternal(name);
1113 /*package*/ synchronized String getPropertyInternal(String name) {
1114 if (!mAdapterProperties.isEmpty())
1115 return mAdapterProperties.get(name);
1117 return mAdapterProperties.get(name);
1120 public synchronized String getAddress() {
1121 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1122 return getProperty("Address");
1125 public synchronized String getName() {
1126 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1127 return getProperty("Name");
1131 * Returns the user-friendly name of a remote device. This value is
1132 * returned from our local cache, which is updated when onPropertyChange
1133 * event is received.
1134 * Do not expect to retrieve the updated remote name immediately after
1135 * changing the name on the remote device.
1137 * @param address Bluetooth address of remote device.
1139 * @return The user-friendly name of the specified remote device.
1141 public synchronized String getRemoteName(String address) {
1142 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1143 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1146 return getRemoteDeviceProperty(address, "Name");
1150 * Get the discoverability window for the device. A timeout of zero
1151 * means that the device is permanently discoverable (if the device is
1152 * in the discoverable mode).
1154 * @return The discoverability window of the device, in seconds. A negative
1155 * value indicates an error.
1157 public synchronized int getDiscoverableTimeout() {
1158 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1159 String timeout = getProperty("DiscoverableTimeout");
1160 if (timeout != null)
1161 return Integer.valueOf(timeout);
1166 public synchronized int getScanMode() {
1167 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1168 if (!isEnabledInternal())
1169 return BluetoothAdapter.SCAN_MODE_NONE;
1171 boolean pairable = getProperty("Pairable").equals("true");
1172 boolean discoverable = getProperty("Discoverable").equals("true");
1173 return bluezStringToScanMode (pairable, discoverable);
1176 public synchronized boolean startDiscovery() {
1177 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1178 "Need BLUETOOTH_ADMIN permission");
1179 if (!isEnabledInternal()) return false;
1181 return startDiscoveryNative();
1184 public synchronized boolean cancelDiscovery() {
1185 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1186 "Need BLUETOOTH_ADMIN permission");
1187 if (!isEnabledInternal()) return false;
1189 return stopDiscoveryNative();
1192 public synchronized boolean isDiscovering() {
1193 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1194 return mIsDiscovering;
1197 /* package */ void setIsDiscovering(boolean isDiscovering) {
1198 mIsDiscovering = isDiscovering;
1201 private boolean isBondingFeasible(String address) {
1202 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1203 "Need BLUETOOTH_ADMIN permission");
1204 if (!isEnabledInternal()) return false;
1206 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1209 address = address.toUpperCase();
1211 if (mBondState.getPendingOutgoingBonding() != null) {
1212 log("Ignoring createBond(): another device is bonding");
1213 // a different device is currently bonding, fail
1217 // Check for bond state only if we are not performing auto
1218 // pairing exponential back-off attempts.
1219 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
1220 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
1221 log("Ignoring createBond(): this device is already bonding or bonded");
1225 if (address.equals(mDockAddress)) {
1226 if (!writeDockPin()) {
1227 log("Error while writing Pin for the dock");
1234 public synchronized boolean createBond(String address) {
1235 if (!isBondingFeasible(address)) return false;
1237 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
1241 mBondState.setPendingOutgoingBonding(address);
1242 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1247 public synchronized boolean createBondOutOfBand(String address, byte[] hash,
1248 byte[] randomizer) {
1249 if (!isBondingFeasible(address)) return false;
1251 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
1255 setDeviceOutOfBandData(address, hash, randomizer);
1256 mBondState.setPendingOutgoingBonding(address);
1257 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1262 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
1263 byte[] randomizer) {
1264 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1265 "Need BLUETOOTH_ADMIN permission");
1266 if (!isEnabledInternal()) return false;
1268 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
1271 log("Setting out of band data for:" + address + ":" +
1272 Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
1275 mDeviceOobData.put(address, value);
1279 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1280 return mDeviceOobData.get(device.getAddress());
1284 public synchronized byte[] readOutOfBandData() {
1285 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1286 "Need BLUETOOTH permission");
1287 if (!isEnabledInternal()) return null;
1289 return readAdapterOutOfBandDataNative();
1292 public synchronized boolean cancelBondProcess(String address) {
1293 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1294 "Need BLUETOOTH_ADMIN permission");
1295 if (!isEnabledInternal()) return false;
1297 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1300 address = address.toUpperCase();
1301 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1305 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
1306 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
1307 cancelDeviceCreationNative(address);
1311 public synchronized boolean removeBond(String address) {
1312 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1313 "Need BLUETOOTH_ADMIN permission");
1314 if (!isEnabledInternal()) return false;
1316 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1319 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
1320 if (state != null) {
1321 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
1328 public synchronized boolean removeBondInternal(String address) {
1329 return removeDeviceNative(getObjectPathFromAddress(address));
1332 public synchronized String[] listBonds() {
1333 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1334 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1337 /*package*/ synchronized String[] listInState(int state) {
1338 return mBondState.listInState(state);
1341 public synchronized int getBondState(String address) {
1342 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1343 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1344 return BluetoothDevice.ERROR;
1346 return mBondState.getBondState(address.toUpperCase());
1349 /*package*/ synchronized boolean setBondState(String address, int state) {
1350 return setBondState(address, state, 0);
1353 /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
1354 mBondState.setBondState(address.toUpperCase(), state);
1358 public synchronized boolean isBluetoothDock(String address) {
1359 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1360 mContext.MODE_PRIVATE);
1362 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
1365 /*package*/ boolean isRemoteDeviceInCache(String address) {
1366 return (mDeviceProperties.get(address) != null);
1369 /*package*/ String[] getRemoteDeviceProperties(String address) {
1370 if (!isEnabledInternal()) return null;
1372 String objectPath = getObjectPathFromAddress(address);
1373 return (String [])getDevicePropertiesNative(objectPath);
1376 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
1377 Map<String, String> properties = mDeviceProperties.get(address);
1378 if (properties != null) {
1379 return properties.get(property);
1381 // Query for remote device properties, again.
1382 // We will need to reload the cache when we switch Bluetooth on / off
1384 if (updateRemoteDevicePropertiesCache(address))
1385 return getRemoteDeviceProperty(address, property);
1387 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
1391 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
1392 String[] propValues = getRemoteDeviceProperties(address);
1393 if (propValues != null) {
1394 addRemoteDeviceProperties(address, propValues);
1400 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
1402 * We get a DeviceFound signal every time RSSI changes or name changes.
1403 * Don't create a new Map object every time */
1404 Map<String, String> propertyValues = mDeviceProperties.get(address);
1405 if (propertyValues == null) {
1406 propertyValues = new HashMap<String, String>();
1409 for (int i = 0; i < properties.length; i++) {
1410 String name = properties[i];
1411 String newValue = null;
1414 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
1417 if (name.equals("UUIDs") || name.equals("Nodes")) {
1418 StringBuilder str = new StringBuilder();
1419 len = Integer.valueOf(properties[++i]);
1420 for (int j = 0; j < len; j++) {
1421 str.append(properties[++i]);
1425 newValue = str.toString();
1428 newValue = properties[++i];
1431 propertyValues.put(name, newValue);
1433 mDeviceProperties.put(address, propertyValues);
1435 // We have added a new remote device or updated its properties.
1436 // Also update the serviceChannel cache.
1437 updateDeviceServiceChannelCache(address);
1440 /* package */ void removeRemoteDeviceProperties(String address) {
1441 mDeviceProperties.remove(address);
1444 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
1446 Map <String, String> propVal = mDeviceProperties.get(address);
1447 if (propVal != null) {
1448 propVal.put(name, value);
1449 mDeviceProperties.put(address, propVal);
1451 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
1456 * Sets the remote device trust state.
1458 * @return boolean to indicate operation success or fail
1460 public synchronized boolean setTrust(String address, boolean value) {
1461 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1462 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1463 "Need BLUETOOTH_ADMIN permission");
1467 if (!isEnabledInternal()) return false;
1469 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
1474 * Gets the remote device trust state as boolean.
1475 * Note: this value may be
1476 * retrieved from cache if we retrieved the data before *
1478 * @return boolean to indicate trust or untrust state
1480 public synchronized boolean getTrustState(String address) {
1481 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1482 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1486 String val = getRemoteDeviceProperty(address, "Trusted");
1490 return val.equals("true") ? true : false;
1495 * Gets the remote major, minor classes encoded as a 32-bit
1498 * Note: this value is retrieved from cache, because we get it during
1499 * remote-device discovery.
1501 * @return 32-bit integer encoding the remote major, minor, and service
1504 public synchronized int getRemoteClass(String address) {
1505 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1506 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1507 return BluetoothClass.ERROR;
1509 String val = getRemoteDeviceProperty(address, "Class");
1511 return BluetoothClass.ERROR;
1513 return Integer.valueOf(val);
1519 * Gets the UUIDs supported by the remote device
1521 * @return array of 128bit ParcelUuids
1523 public synchronized ParcelUuid[] getRemoteUuids(String address) {
1524 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1525 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1528 return getUuidFromCache(address);
1531 private ParcelUuid[] getUuidFromCache(String address) {
1532 String value = getRemoteDeviceProperty(address, "UUIDs");
1533 if (value == null) return null;
1535 String[] uuidStrings = null;
1536 // The UUIDs are stored as a "," separated string.
1537 uuidStrings = value.split(",");
1538 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1540 for (int i = 0; i < uuidStrings.length; i++) {
1541 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1547 * Connect and fetch new UUID's using SDP.
1548 * The UUID's found are broadcast as intents.
1549 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1551 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1552 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1553 * callback and broadcast intents.
1555 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1556 IBluetoothCallback callback) {
1557 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1558 if (!isEnabledInternal()) return false;
1560 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1564 RemoteService service = new RemoteService(address, uuid);
1565 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1566 // An SDP query for this address & uuid is already in progress
1567 // Do not add this callback for the uuid
1571 if (mUuidIntentTracker.contains(address)) {
1572 // An SDP query for this address is already in progress
1573 // Add this uuid onto the in-progress SDP query
1575 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1581 // Just do the SDP if the device is already created and UUIDs are not
1582 // NULL, else create the device and then do SDP.
1583 if (isRemoteDeviceInCache(address) && getRemoteUuids(address) != null) {
1584 String path = getObjectPathFromAddress(address);
1585 if (path == null) return false;
1587 // Use an empty string for the UUID pattern
1588 ret = discoverServicesNative(path, "");
1590 ret = createDeviceNative(address);
1593 mUuidIntentTracker.add(address);
1595 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1598 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1599 message.obj = address;
1600 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1605 * Gets the rfcomm channel associated with the UUID.
1606 * Pulls records from the cache only.
1608 * @param address Address of the remote device
1609 * @param uuid ParcelUuid of the service attribute
1611 * @return rfcomm channel associated with the service attribute
1614 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
1615 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1616 if (!isEnabledInternal()) return -1;
1618 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1619 return BluetoothDevice.ERROR;
1621 // Check if we are recovering from a crash.
1622 if (mDeviceProperties.isEmpty()) {
1623 if (!updateRemoteDevicePropertiesCache(address))
1627 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1628 if (value != null && value.containsKey(uuid))
1629 return value.get(uuid);
1633 public synchronized boolean setPin(String address, byte[] pin) {
1634 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1635 "Need BLUETOOTH_ADMIN permission");
1636 if (!isEnabledInternal()) return false;
1638 if (pin == null || pin.length <= 0 || pin.length > 16 ||
1639 !BluetoothAdapter.checkBluetoothAddress(address)) {
1642 address = address.toUpperCase();
1643 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1645 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1646 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1650 // bluez API wants pin as a string
1653 pinString = new String(pin, "UTF8");
1654 } catch (UnsupportedEncodingException uee) {
1655 Log.e(TAG, "UTF8 not supported?!?");
1658 return setPinNative(address, pinString, data.intValue());
1661 public synchronized boolean setPasskey(String address, int passkey) {
1662 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1663 "Need BLUETOOTH_ADMIN permission");
1664 if (!isEnabledInternal()) return false;
1666 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
1669 address = address.toUpperCase();
1670 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1672 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1673 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1677 return setPasskeyNative(address, passkey, data.intValue());
1680 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1681 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1682 "Need BLUETOOTH_ADMIN permission");
1683 if (!isEnabledInternal()) return false;
1685 address = address.toUpperCase();
1686 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1688 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1689 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1693 return setPairingConfirmationNative(address, confirm, data.intValue());
1696 public synchronized boolean setRemoteOutOfBandData(String address) {
1697 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1698 "Need BLUETOOTH_ADMIN permission");
1699 if (!isEnabledInternal()) return false;
1700 address = address.toUpperCase();
1701 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1703 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
1704 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1709 Pair<byte[], byte[]> val = mDeviceOobData.get(address);
1710 byte[] hash, randomizer;
1712 // TODO: check what should be passed in this case.
1713 hash = new byte[16];
1714 randomizer = new byte[16];
1717 randomizer = val.second;
1719 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
1722 public synchronized boolean cancelPairingUserInput(String address) {
1723 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1724 "Need BLUETOOTH_ADMIN permission");
1725 if (!isEnabledInternal()) return false;
1727 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1730 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
1731 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
1732 address = address.toUpperCase();
1733 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1735 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1736 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1737 "by the remote or by bluez.\n");
1740 return cancelPairingUserInputNative(address, data.intValue());
1743 /*package*/ void updateDeviceServiceChannelCache(String address) {
1744 ParcelUuid[] deviceUuids = getRemoteUuids(address);
1745 // We are storing the rfcomm channel numbers only for the uuids
1746 // we are interested in.
1748 if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
1750 ArrayList<ParcelUuid> applicationUuids = new ArrayList();
1752 synchronized (this) {
1753 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1754 if (service.address.equals(address)) {
1755 applicationUuids.add(service.uuid);
1760 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
1762 // Retrieve RFCOMM channel for default uuids
1763 for (ParcelUuid uuid : RFCOMM_UUIDS) {
1764 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1765 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1766 uuid.toString(), 0x0004);
1767 if (DBG) log("\tuuid(system): " + uuid + " " + channel);
1768 value.put(uuid, channel);
1771 // Retrieve RFCOMM channel for application requested uuids
1772 for (ParcelUuid uuid : applicationUuids) {
1773 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1774 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1775 uuid.toString(), 0x0004);
1776 if (DBG) log("\tuuid(application): " + uuid + " " + channel);
1777 value.put(uuid, channel);
1781 synchronized (this) {
1782 // Make application callbacks
1783 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1785 RemoteService service = iter.next();
1786 if (service.address.equals(address)) {
1788 if (value.get(service.uuid) != null) {
1789 channel = value.get(service.uuid);
1791 if (channel != -1) {
1792 if (DBG) log("Making callback for " + service.uuid + " with result " +
1794 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1795 if (callback != null) {
1797 callback.onRfcommChannelFound(channel);
1798 } catch (RemoteException e) {Log.e(TAG, "", e);}
1807 mDeviceServiceChannelCache.put(address, value);
1812 * b is a handle to a Binder instance, so that this service can be notified
1813 * for Applications that terminate unexpectedly, to clean there service
1816 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1817 int channel, IBinder b) {
1818 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1819 if (!isEnabledInternal()) return -1;
1821 if (serviceName == null || uuid == null || channel < 1 ||
1822 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1825 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1826 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1829 int handle = addRfcommServiceRecordNative(serviceName,
1830 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1832 if (DBG) log("new handle " + Integer.toHexString(handle));
1837 int pid = Binder.getCallingPid();
1838 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
1840 b.linkToDeath(new Reaper(handle, pid), 0);
1841 } catch (RemoteException e) {}
1845 public void removeServiceRecord(int handle) {
1846 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1847 "Need BLUETOOTH permission");
1848 checkAndRemoveRecord(handle, Binder.getCallingPid());
1851 private synchronized void checkAndRemoveRecord(int handle, int pid) {
1852 Integer handleInt = new Integer(handle);
1853 Integer owner = mServiceRecordToPid.get(handleInt);
1854 if (owner != null && pid == owner.intValue()) {
1855 if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
1857 mServiceRecordToPid.remove(handleInt);
1858 removeServiceRecordNative(handle);
1862 private class Reaper implements IBinder.DeathRecipient {
1865 Reaper(int handle, int pid) {
1867 this.handle = handle;
1869 public void binderDied() {
1870 synchronized (BluetoothService.this) {
1871 if (DBG) log("Tracked app " + pid + " died");
1872 checkAndRemoveRecord(handle, pid);
1877 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1879 public void onReceive(Context context, Intent intent) {
1880 if (intent == null) return;
1882 String action = intent.getAction();
1883 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1884 ContentResolver resolver = context.getContentResolver();
1885 // Query the airplane mode from Settings.System just to make sure that
1886 // some random app is not sending this intent and disabling bluetooth
1887 boolean enabled = !isAirplaneModeOn();
1888 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1889 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1896 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
1897 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1898 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1899 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
1900 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1901 mDockAddress = null;
1904 SharedPreferences.Editor editor =
1905 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1906 mContext.MODE_PRIVATE).edit();
1907 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
1914 private void registerForAirplaneMode(IntentFilter filter) {
1915 final ContentResolver resolver = mContext.getContentResolver();
1916 final String airplaneModeRadios = Settings.System.getString(resolver,
1917 Settings.System.AIRPLANE_MODE_RADIOS);
1918 final String toggleableRadios = Settings.System.getString(resolver,
1919 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1921 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
1922 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1923 mIsAirplaneToggleable = toggleableRadios == null ? false :
1924 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
1926 if (mIsAirplaneSensitive) {
1927 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1931 /* Returns true if airplane mode is currently on */
1932 private final boolean isAirplaneModeOn() {
1933 return Settings.System.getInt(mContext.getContentResolver(),
1934 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1937 /* Broadcast the Uuid intent */
1938 /*package*/ synchronized void sendUuidIntent(String address) {
1939 ParcelUuid[] uuid = getUuidFromCache(address);
1940 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
1941 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
1942 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1943 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
1945 if (mUuidIntentTracker.contains(address))
1946 mUuidIntentTracker.remove(address);
1950 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1951 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1953 RemoteService service = iter.next();
1954 if (service.address.equals(address)) {
1955 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
1956 " " + service.uuid);
1957 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1958 if (callback != null) {
1960 callback.onRfcommChannelFound(-1);
1961 } catch (RemoteException e) {Log.e(TAG, "", e);}
1970 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1971 switch(mBluetoothState) {
1972 case BluetoothAdapter.STATE_OFF:
1973 pw.println("Bluetooth OFF\n");
1975 case BluetoothAdapter.STATE_TURNING_ON:
1976 pw.println("Bluetooth TURNING ON\n");
1978 case BluetoothAdapter.STATE_TURNING_OFF:
1979 pw.println("Bluetooth TURNING OFF\n");
1981 case BluetoothAdapter.STATE_ON:
1982 pw.println("Bluetooth ON\n");
1985 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
1986 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
1988 pw.println("Local address = " + getAddress());
1989 pw.println("Local name = " + getName());
1990 pw.println("isDiscovering() = " + isDiscovering());
1992 BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1994 pw.println("\n--Known devices--");
1995 for (String address : mDeviceProperties.keySet()) {
1996 int bondState = mBondState.getBondState(address);
1997 pw.printf("%s %10s (%d) %s\n", address,
1998 toBondStateString(bondState),
1999 mBondState.getAttempt(address),
2000 getRemoteName(address));
2002 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
2003 if (uuidChannels == null) {
2004 pw.println("\tuuids = null");
2006 for (ParcelUuid uuid : uuidChannels.keySet()) {
2007 Integer channel = uuidChannels.get(uuid);
2008 if (channel == null) {
2009 pw.println("\t" + uuid);
2011 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
2015 for (RemoteService service : mUuidCallbackTracker.keySet()) {
2016 if (service.address.equals(address)) {
2017 pw.println("\tPENDING CALLBACK: " + service.uuid);
2022 String value = getProperty("Devices");
2023 String[] devicesObjectPath = null;
2024 if (value != null) {
2025 devicesObjectPath = value.split(",");
2027 pw.println("\n--ACL connected devices--");
2028 if (devicesObjectPath != null) {
2029 for (String device : devicesObjectPath) {
2030 pw.println(getAddressFromObjectPath(device));
2034 // Rather not do this from here, but no-where else and I need this
2036 pw.println("\n--Headset Service--");
2037 switch (headset.getState(headset.getCurrentHeadset())) {
2038 case BluetoothHeadset.STATE_DISCONNECTED:
2039 pw.println("getState() = STATE_DISCONNECTED");
2041 case BluetoothHeadset.STATE_CONNECTING:
2042 pw.println("getState() = STATE_CONNECTING");
2044 case BluetoothHeadset.STATE_CONNECTED:
2045 pw.println("getState() = STATE_CONNECTED");
2047 case BluetoothHeadset.STATE_ERROR:
2048 pw.println("getState() = STATE_ERROR");
2052 pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
2053 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
2055 pw.println("\n--Application Service Records--");
2056 for (Integer handle : mServiceRecordToPid.keySet()) {
2057 Integer pid = mServiceRecordToPid.get(handle);
2058 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
2062 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
2063 if (pairable && discoverable)
2064 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
2065 else if (pairable && !discoverable)
2066 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
2068 return BluetoothAdapter.SCAN_MODE_NONE;
2071 /* package */ static String scanModeToBluezString(int mode) {
2073 case BluetoothAdapter.SCAN_MODE_NONE:
2075 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
2076 return "connectable";
2077 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
2078 return "discoverable";
2083 /*package*/ String getAddressFromObjectPath(String objectPath) {
2084 String adapterObjectPath = getPropertyInternal("ObjectPath");
2085 if (adapterObjectPath == null || objectPath == null) {
2086 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
2087 " or deviceObjectPath:" + objectPath + " is null");
2090 if (!objectPath.startsWith(adapterObjectPath)) {
2091 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
2092 " is not a prefix of deviceObjectPath:" + objectPath +
2093 "bluetoothd crashed ?");
2096 String address = objectPath.substring(adapterObjectPath.length());
2097 if (address != null) return address.replace('_', ':');
2099 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
2103 /*package*/ String getObjectPathFromAddress(String address) {
2104 String path = getPropertyInternal("ObjectPath");
2106 Log.e(TAG, "Error: Object Path is null");
2109 path = path + address.replace(":", "_");
2113 /*package */ void setLinkTimeout(String address, int num_slots) {
2114 String path = getObjectPathFromAddress(address);
2115 boolean result = setLinkTimeoutNative(path, num_slots);
2117 if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
2120 public boolean connectHeadset(String address) {
2121 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2122 if (state != null) {
2123 Message msg = new Message();
2124 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2126 mHfpProfileState.sendMessage(msg);
2132 public boolean disconnectHeadset(String address) {
2133 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2134 if (state != null) {
2135 Message msg = new Message();
2136 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2138 mHfpProfileState.sendMessage(msg);
2144 public boolean connectSink(String address) {
2145 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2146 if (state != null) {
2147 Message msg = new Message();
2148 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2150 mA2dpProfileState.sendMessage(msg);
2156 public boolean disconnectSink(String address) {
2157 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2158 if (state != null) {
2159 Message msg = new Message();
2160 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2162 mA2dpProfileState.sendMessage(msg);
2168 private BluetoothDeviceProfileState addProfileState(String address) {
2169 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2170 if (state != null) return state;
2172 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
2173 mDeviceProfileState.put(address, state);
2178 private void removeProfileState(String address) {
2179 mDeviceProfileState.remove(address);
2182 private void initProfileState() {
2183 String []bonds = null;
2184 String val = getPropertyInternal("Devices");
2186 bonds = val.split(",");
2188 if (bonds == null) {
2192 for (String path : bonds) {
2193 String address = getAddressFromObjectPath(path);
2194 BluetoothDeviceProfileState state = addProfileState(address);
2195 // Allow 8 secs for SDP records to get registered.
2196 Message msg = new Message();
2197 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
2198 state.sendMessageDelayed(msg, 8000);
2202 public boolean notifyIncomingConnection(String address) {
2203 BluetoothDeviceProfileState state =
2204 mDeviceProfileState.get(address);
2205 if (state != null) {
2206 Message msg = new Message();
2207 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
2208 state.sendMessage(msg);
2214 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
2215 BluetoothDeviceProfileState state =
2216 mDeviceProfileState.get(address);
2217 if (state != null) {
2218 Message msg = new Message();
2219 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
2220 state.sendMessage(msg);
2226 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2227 mA2dpService = a2dpService;
2230 public void sendProfileStateMessage(int profile, int cmd) {
2231 Message msg = new Message();
2233 if (profile == BluetoothProfileState.HFP) {
2234 mHfpProfileState.sendMessage(msg);
2235 } else if (profile == BluetoothProfileState.A2DP) {
2236 mA2dpProfileState.sendMessage(msg);
2240 private static void log(String msg) {
2244 private native static void classInitNative();
2245 private native void initializeNativeDataNative();
2246 private native boolean setupNativeDataNative();
2247 private native boolean tearDownNativeDataNative();
2248 private native void cleanupNativeDataNative();
2249 private native String getAdapterPathNative();
2251 private native int isEnabledNative();
2252 private native int enableNative();
2253 private native int disableNative();
2255 private native Object[] getAdapterPropertiesNative();
2256 private native Object[] getDevicePropertiesNative(String objectPath);
2257 private native boolean setAdapterPropertyStringNative(String key, String value);
2258 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2259 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2261 private native boolean startDiscoveryNative();
2262 private native boolean stopDiscoveryNative();
2264 private native boolean createPairedDeviceNative(String address, int timeout_ms);
2265 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2266 private native byte[] readAdapterOutOfBandDataNative();
2268 private native boolean cancelDeviceCreationNative(String address);
2269 private native boolean removeDeviceNative(String objectPath);
2270 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2273 private native boolean cancelPairingUserInputNative(String address, int nativeData);
2274 private native boolean setPinNative(String address, String pin, int nativeData);
2275 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2276 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2278 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2279 byte[] randomizer, int nativeData);
2281 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2283 private native boolean createDeviceNative(String address);
2284 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
2286 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2288 private native boolean removeServiceRecordNative(int handle);
2289 private native boolean setLinkTimeoutNative(String path, int num_slots);