Home | History | Annotate | Download | only in server
      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;
    154         public RemoteService(String address, ParcelUuid uuid) {
    155             this.address = address;
    156             this.uuid = uuid;
    157         }
    158         @Override
    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
    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 {
    177         classInitNative();
    178     }
    179 
    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 
    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 
    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 
    284     /*package*/ synchronized String getDockPin() {
    285         return mDockPin;
    286     }
    287 
    288     public synchronized void initAfterRegistration() {
    289         mAdapter = BluetoothAdapter.getDefaultAdapter();
    290         mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
    291     }
    292 
    293     @Override
    294     protected void finalize() throws Throwable {
    295         mContext.unregisterReceiver(mReceiver);
    296         try {
    297             cleanupNativeDataNative();
    298         } finally {
    299             super.finalize();
    300         }
    301     }
    302 
    303     public boolean isEnabled() {
    304         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    305         return isEnabledInternal();
    306     }
    307 
    308     private boolean isEnabledInternal() {
    309         return mBluetoothState == BluetoothAdapter.STATE_ON;
    310     }
    311 
    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      */
    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      */
    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 
    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 */
    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      */
    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 */
    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 
    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;
    525         public EnableThread(boolean saveSetting) {
    526             mSaveSetting = saveSetting;
    527         }
    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 
    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 
    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 
    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 
    637     /*package*/ synchronized String getPendingOutgoingBonding() {
    638         return mBondState.getPendingOutgoingBonding();
    639     }
    640 
    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 
    694         private synchronized void setPendingOutgoingBonding(String address) {
    695             mPendingOutgoingBonding = address;
    696         }
    697 
    698         public synchronized String getPendingOutgoingBonding() {
    699             return mPendingOutgoingBonding;
    700         }
    701 
    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 
    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 */
    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 
    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 
    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 
    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 
    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 
    822         public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
    823             return getAttempt(address) != 0;
    824         }
    825 
    826         public synchronized void clearPinAttempts(String address) {
    827             mPinAttempt.remove(address);
    828         }
    829 
    830         public synchronized boolean hasAutoPairingFailed(String address) {
    831             if (mAutoPairingDynamicAddressBlacklist == null) return false;
    832 
    833             return mAutoPairingDynamicAddressBlacklist.contains(address);
    834         }
    835 
    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 
    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 
    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 
    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
    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 
    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 
    972     /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
    973         return mAdapterProperties.isEmpty();
    974     }
    975 
    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 
   1018     /* package */ synchronized void setProperty(String name, String value) {
   1019         mAdapterProperties.put(name, value);
   1020     }
   1021 
   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.
   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 
   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 
   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      */
   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 
   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 
   1099     /*package*/ synchronized String getProperty(String name) {
   1100         if (!isEnabledInternal()) return null;
   1101         return getPropertyInternal(name);
   1102     }
   1103 
   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 
   1111     public synchronized String getAddress() {
   1112         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1113         return getProperty("Address");
   1114     }
   1115 
   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      */
   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      */
   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 
   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 
   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 
   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 
   1183     public synchronized boolean isDiscovering() {
   1184         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1185         return mIsDiscovering;
   1186     }
   1187 
   1188     /* package */ void setIsDiscovering(boolean isDiscovering) {
   1189         mIsDiscovering = isDiscovering;
   1190     }
   1191 
   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 
   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 
   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 
   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 
   1270     Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
   1271         return mDeviceOobData.get(device.getAddress());
   1272     }
   1273 
   1274 
   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 
   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 
   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 
   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 
   1325     public synchronized String[] listBonds() {
   1326         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
   1327         return mBondState.listInState(BluetoothDevice.BOND_BONDED);
   1328     }
   1329 
   1330     /*package*/ synchronized String[] listInState(int state) {
   1331       return mBondState.listInState(state);
   1332     }
   1333 
   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 
   1342     /*package*/ synchronized boolean setBondState(String address, int state) {
   1343         return setBondState(address, state, 0);
   1344     }
   1345 
   1346     /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
   1347         mBondState.setBondState(address.toUpperCase(), state);
   1348         return true;
   1349     }
   1350 
   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 
   1358     /*package*/ boolean isRemoteDeviceInCache(String address) {
   1359         return (mDeviceProperties.get(address) != null);
   1360     }
   1361 
   1362     /*package*/ String[] getRemoteDeviceProperties(String address) {
   1363         if (!isEnabledInternal()) return null;
   1364 
   1365         String objectPath = getObjectPathFromAddress(address);
   1366         return (String [])getDevicePropertiesNative(objectPath);
   1367     }
   1368 
   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 
   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 
   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 
   1433     /* package */ void removeRemoteDeviceProperties(String address) {
   1434         mDeviceProperties.remove(address);
   1435     }
   1436 
   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      */
   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      */
   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      */
   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      */
   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 
   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      */
   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      */
   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 
   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 
   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 
   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 
   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 
   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 
   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      */
   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 
   1838     public void removeServiceRecord(int handle) {
   1839         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
   1840                                                 "Need BLUETOOTH permission");
   1841         checkAndRemoveRecord(handle, Binder.getCallingPid());
   1842     }
   1843 
   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;
   1858         Reaper(int handle, int pid) {
   1859             this.pid = pid;
   1860             this.handle = handle;
   1861         }
   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 
   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 */
   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 */
   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 
   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
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   2215     /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
   2216         mA2dpService = a2dpService;
   2217     }
   2218 
   2219     /*package*/ Integer getAuthorizationAgentRequestData(String address) {
   2220         Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
   2221         return data;
   2222     }
   2223 
   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 
   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 */
   2246     public Pair<Integer, String> getIncomingState(String address) {
   2247         if (mIncomingConnections.isEmpty()) {
   2248             createIncomingConnectionStateFile();
   2249             readIncomingConnectionState();
   2250         }
   2251         return mIncomingConnections.get(address);
   2252     }
   2253 
   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 
   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 */
   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 
   2344     private static void log(String msg) {
   2345         Log.d(TAG, msg);
   2346     }
   2347 
   2348     private native static void classInitNative();
   2349     private native void initializeNativeDataNative();
   2350     private native boolean setupNativeDataNative();
   2351     private native boolean tearDownNativeDataNative();
   2352     private native void cleanupNativeDataNative();
   2353     private native String getAdapterPathNative();
   2354 
   2355     private native int isEnabledNative();
   2356     private native int enableNative();
   2357     private native int disableNative();
   2358 
   2359     private native Object[] getAdapterPropertiesNative();
   2360     private native Object[] getDevicePropertiesNative(String objectPath);
   2361     private native boolean setAdapterPropertyStringNative(String key, String value);
   2362     private native boolean setAdapterPropertyIntegerNative(String key, int value);
   2363     private native boolean setAdapterPropertyBooleanNative(String key, int value);
   2364 
   2365     private native boolean startDiscoveryNative();
   2366     private native boolean stopDiscoveryNative();
   2367 
   2368     private native boolean createPairedDeviceNative(String address, int timeout_ms);
   2369     private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
   2370     private native byte[] readAdapterOutOfBandDataNative();
   2371 
   2372     private native boolean cancelDeviceCreationNative(String address);
   2373     private native boolean removeDeviceNative(String objectPath);
   2374     private native int getDeviceServiceChannelNative(String objectPath, String uuid,
   2375             int attributeId);
   2376 
   2377     private native boolean cancelPairingUserInputNative(String address, int nativeData);
   2378     private native boolean setPinNative(String address, String pin, int nativeData);
   2379     private native boolean setPasskeyNative(String address, int passkey, int nativeData);
   2380     private native boolean setPairingConfirmationNative(String address, boolean confirm,
   2381             int nativeData);
   2382     private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
   2383                                                         byte[] randomizer, int nativeData);
   2384 
   2385     private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
   2386             int value);
   2387     private native boolean createDeviceNative(String address);
   2388     /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
   2389 
   2390     private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
   2391             short channel);
   2392     private native boolean removeServiceRecordNative(int handle);
   2393     private native boolean setLinkTimeoutNative(String path, int num_slots);
   2394     native boolean setAuthorizationNative(String address, boolean value, int data);
   2395 }
   2396