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