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