Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2006 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 package com.android.phone;
     18 
     19 import android.app.Service;
     20 import android.bluetooth.BluetoothAdapter;
     21 import android.bluetooth.BluetoothAudioGateway;
     22 import android.bluetooth.BluetoothAudioGateway.IncomingConnectionInfo;
     23 import android.bluetooth.BluetoothDevice;
     24 import android.bluetooth.BluetoothHeadset;
     25 import android.bluetooth.BluetoothProfile;
     26 import android.bluetooth.BluetoothUuid;
     27 import android.bluetooth.HeadsetBase;
     28 import android.bluetooth.IBluetooth;
     29 import android.bluetooth.IBluetoothHeadset;
     30 import android.content.BroadcastReceiver;
     31 import android.content.Context;
     32 import android.content.Intent;
     33 import android.content.IntentFilter;
     34 import android.media.AudioManager;
     35 import android.os.Handler;
     36 import android.os.IBinder;
     37 import android.os.Message;
     38 import android.os.ParcelUuid;
     39 import android.os.PowerManager;
     40 import android.os.RemoteException;
     41 import android.os.ServiceManager;
     42 import android.provider.Settings;
     43 import android.util.Log;
     44 
     45 import java.util.ArrayList;
     46 import java.util.List;
     47 import java.util.concurrent.ConcurrentHashMap;
     48 
     49 /**
     50  * Provides Bluetooth Headset and Handsfree profile, as a service in
     51  * the Phone application.
     52  * @hide
     53  */
     54 public class BluetoothHeadsetService extends Service {
     55     private static final String TAG = "Bluetooth HSHFP";
     56     private static final boolean DBG = true;
     57 
     58     private static final String PREF_NAME = BluetoothHeadsetService.class.getSimpleName();
     59     private static final String PREF_LAST_HEADSET = "lastHeadsetAddress";
     60 
     61     private static final int PHONE_STATE_CHANGED = 1;
     62 
     63     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
     64     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
     65 
     66     private static boolean sHasStarted = false;
     67 
     68     private BluetoothDevice mDeviceSdpQuery;
     69     private BluetoothAdapter mAdapter;
     70     private IBluetooth mBluetoothService;
     71     private PowerManager mPowerManager;
     72     private BluetoothAudioGateway mAg;
     73     private BluetoothHandsfree mBtHandsfree;
     74     private ConcurrentHashMap<BluetoothDevice, BluetoothRemoteHeadset> mRemoteHeadsets;
     75     private BluetoothDevice mAudioConnectedDevice;
     76 
     77     @Override
     78     public void onCreate() {
     79         super.onCreate();
     80         mAdapter = BluetoothAdapter.getDefaultAdapter();
     81         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
     82         mBtHandsfree = PhoneApp.getInstance().getBluetoothHandsfree();
     83         mAg = new BluetoothAudioGateway(mAdapter);
     84         IntentFilter filter = new IntentFilter(
     85                 BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
     86         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
     87         filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
     88         filter.addAction(BluetoothDevice.ACTION_UUID);
     89         registerReceiver(mBluetoothReceiver, filter);
     90 
     91         IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
     92         if (b == null) {
     93             throw new RuntimeException("Bluetooth service not available");
     94         }
     95         mBluetoothService = IBluetooth.Stub.asInterface(b);
     96         mRemoteHeadsets = new ConcurrentHashMap<BluetoothDevice, BluetoothRemoteHeadset>();
     97    }
     98 
     99    private class BluetoothRemoteHeadset {
    100        private int mState;
    101        private int mAudioState;
    102        private int mHeadsetType;
    103        private HeadsetBase mHeadset;
    104        private IncomingConnectionInfo mIncomingInfo;
    105 
    106        BluetoothRemoteHeadset() {
    107            mState = BluetoothProfile.STATE_DISCONNECTED;
    108            mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN;
    109            mHeadset = null;
    110            mIncomingInfo = null;
    111            mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    112        }
    113 
    114        BluetoothRemoteHeadset(int headsetType, IncomingConnectionInfo incomingInfo) {
    115            mState = BluetoothProfile.STATE_DISCONNECTED;
    116            mHeadsetType = headsetType;
    117            mHeadset = null;
    118            mIncomingInfo = incomingInfo;
    119            mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    120        }
    121    }
    122 
    123    synchronized private BluetoothDevice getCurrentDevice() {
    124        for (BluetoothDevice device : mRemoteHeadsets.keySet()) {
    125            int state = mRemoteHeadsets.get(device).mState;
    126            if (state == BluetoothProfile.STATE_CONNECTING ||
    127                state == BluetoothProfile.STATE_CONNECTED) {
    128                return device;
    129            }
    130        }
    131        return null;
    132    }
    133 
    134     @Override
    135     public void onStart(Intent intent, int startId) {
    136          if (mAdapter == null) {
    137             Log.w(TAG, "Stopping BluetoothHeadsetService: device does not have BT");
    138             stopSelf();
    139         } else {
    140             if (!sHasStarted) {
    141                 if (DBG) log("Starting BluetoothHeadsetService");
    142                 if (mAdapter.isEnabled()) {
    143                     mAg.start(mIncomingConnectionHandler);
    144                     mBtHandsfree.onBluetoothEnabled();
    145                 }
    146                 sHasStarted = true;
    147             }
    148         }
    149     }
    150 
    151     private final Handler mIncomingConnectionHandler = new Handler() {
    152         @Override
    153         public void handleMessage(Message msg) {
    154             synchronized(BluetoothHeadsetService.this) {
    155                 IncomingConnectionInfo info = (IncomingConnectionInfo)msg.obj;
    156                 int type = BluetoothHandsfree.TYPE_UNKNOWN;
    157                 switch(msg.what) {
    158                 case BluetoothAudioGateway.MSG_INCOMING_HEADSET_CONNECTION:
    159                     type = BluetoothHandsfree.TYPE_HEADSET;
    160                     break;
    161                 case BluetoothAudioGateway.MSG_INCOMING_HANDSFREE_CONNECTION:
    162                     type = BluetoothHandsfree.TYPE_HANDSFREE;
    163                     break;
    164                 }
    165 
    166                 Log.i(TAG, "Incoming rfcomm (" + BluetoothHandsfree.typeToString(type) +
    167                       ") connection from " + info.mRemoteDevice + "on channel " +
    168                       info.mRfcommChan);
    169 
    170                 int priority = BluetoothProfile.PRIORITY_OFF;
    171                 HeadsetBase headset;
    172                 priority = getPriority(info.mRemoteDevice);
    173                 if (priority <= BluetoothProfile.PRIORITY_OFF) {
    174                     Log.i(TAG, "Rejecting incoming connection because priority = " + priority);
    175 
    176                     headset = new HeadsetBase(mPowerManager, mAdapter,
    177                                               info.mRemoteDevice,
    178                                               info.mSocketFd, info.mRfcommChan,
    179                                               null);
    180                     headset.disconnect();
    181                     try {
    182                         mBluetoothService.notifyIncomingConnection(info.mRemoteDevice.getAddress(),
    183                                                                    true);
    184                     } catch (RemoteException e) {
    185                         Log.e(TAG, "notifyIncomingConnection", e);
    186                     }
    187                     return;
    188                 }
    189 
    190                 BluetoothRemoteHeadset remoteHeadset;
    191                 BluetoothDevice device = getCurrentDevice();
    192 
    193                 int state = BluetoothProfile.STATE_DISCONNECTED;
    194                 if (device != null) {
    195                     state = mRemoteHeadsets.get(device).mState;
    196                 }
    197 
    198                 switch (state) {
    199                 case BluetoothProfile.STATE_DISCONNECTED:
    200                     // headset connecting us, lets join
    201                     remoteHeadset = new BluetoothRemoteHeadset(type, info);
    202                     mRemoteHeadsets.put(info.mRemoteDevice, remoteHeadset);
    203 
    204                     try {
    205                         mBluetoothService.notifyIncomingConnection(
    206                            info.mRemoteDevice.getAddress(), false);
    207                     } catch (RemoteException e) {
    208                         Log.e(TAG, "notifyIncomingConnection");
    209                     }
    210                     break;
    211                 case BluetoothProfile.STATE_CONNECTING:
    212                     if (!info.mRemoteDevice.equals(device)) {
    213                         // different headset, ignoring
    214                         Log.i(TAG, "Already attempting connect to " + device +
    215                               ", disconnecting " + info.mRemoteDevice);
    216 
    217                         headset = new HeadsetBase(mPowerManager, mAdapter,
    218                                                   info.mRemoteDevice,
    219                                                   info.mSocketFd, info.mRfcommChan,
    220                                                   null);
    221                         headset.disconnect();
    222                         break;
    223                     }
    224 
    225                     // Incoming and Outgoing connections to the same headset.
    226                     // The state machine manager will cancel outgoing and accept the incoming one.
    227                     // Update the state
    228                     mRemoteHeadsets.get(info.mRemoteDevice).mHeadsetType = type;
    229                     mRemoteHeadsets.get(info.mRemoteDevice).mIncomingInfo = info;
    230 
    231                     try {
    232                         mBluetoothService.notifyIncomingConnection(
    233                             info.mRemoteDevice.getAddress(), false);
    234                     } catch (RemoteException e) {
    235                         Log.e(TAG, "notifyIncomingConnection");
    236                     }
    237                     break;
    238                 case BluetoothProfile.STATE_CONNECTED:
    239                     Log.i(TAG, "Already connected to " + device + ", disconnecting " +
    240                             info.mRemoteDevice);
    241                     rejectIncomingConnection(info);
    242                     break;
    243                 }
    244             }
    245         }
    246     };
    247 
    248     private void rejectIncomingConnection(IncomingConnectionInfo info) {
    249         HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter,
    250             info.mRemoteDevice, info.mSocketFd, info.mRfcommChan, null);
    251         headset.disconnect();
    252     }
    253 
    254 
    255     private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
    256 
    257         @Override
    258         public void onReceive(Context context, Intent intent) {
    259             String action = intent.getAction();
    260             BluetoothDevice device =
    261                     intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    262 
    263             BluetoothDevice currDevice = getCurrentDevice();
    264             int state = BluetoothProfile.STATE_DISCONNECTED;
    265             if (currDevice != null) {
    266                 state = mRemoteHeadsets.get(currDevice).mState;
    267             }
    268 
    269             if ((state == BluetoothProfile.STATE_CONNECTED ||
    270                     state == BluetoothProfile.STATE_CONNECTING) &&
    271                     action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) &&
    272                     device.equals(currDevice)) {
    273                 try {
    274                     mBinder.disconnect(currDevice);
    275                 } catch (RemoteException e) {}
    276             } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
    277                 switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
    278                                            BluetoothAdapter.ERROR)) {
    279                 case BluetoothAdapter.STATE_ON:
    280                     mAg.start(mIncomingConnectionHandler);
    281                     mBtHandsfree.onBluetoothEnabled();
    282                     break;
    283                 case BluetoothAdapter.STATE_TURNING_OFF:
    284                     mBtHandsfree.onBluetoothDisabled();
    285                     mAg.stop();
    286                     if (currDevice != null) {
    287                         try {
    288                             mBinder.disconnect(currDevice);
    289                         } catch (RemoteException e) {}
    290                     }
    291                     break;
    292                 }
    293             } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
    294                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
    295                 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
    296                     mBtHandsfree.sendScoGainUpdate(intent.getIntExtra(
    297                             AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0));
    298                 }
    299 
    300             } else if (action.equals(BluetoothDevice.ACTION_UUID)) {
    301                 if (device.equals(mDeviceSdpQuery) && device.equals(currDevice)) {
    302                     // We have got SDP records for the device we are interested in.
    303                     getSdpRecordsAndConnect(device);
    304                 }
    305             }
    306         }
    307     };
    308 
    309     private static final int CONNECT_HEADSET_DELAYED = 1;
    310     private Handler mHandler = new Handler() {
    311         @Override
    312         public void handleMessage(Message msg) {
    313             switch (msg.what) {
    314                 case CONNECT_HEADSET_DELAYED:
    315                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    316                     getSdpRecordsAndConnect(device);
    317                     break;
    318             }
    319         }
    320     };
    321 
    322     @Override
    323     public IBinder onBind(Intent intent) {
    324         return mBinder;
    325     }
    326 
    327     // ------------------------------------------------------------------
    328     // Bluetooth Headset Connect
    329     // ------------------------------------------------------------------
    330     private static final int RFCOMM_CONNECTED             = 1;
    331     private static final int RFCOMM_ERROR                 = 2;
    332 
    333     private long mTimestamp;
    334 
    335     /**
    336      * Thread for RFCOMM connection
    337      * Messages are sent to mConnectingStatusHandler as connection progresses.
    338      */
    339     private RfcommConnectThread mConnectThread;
    340     private class RfcommConnectThread extends Thread {
    341         private BluetoothDevice device;
    342         private int channel;
    343         private int type;
    344 
    345         private static final int EINTERRUPT = -1000;
    346         private static final int ECONNREFUSED = -111;
    347 
    348         public RfcommConnectThread(BluetoothDevice device, int channel, int type) {
    349             super();
    350             this.device = device;
    351             this.channel = channel;
    352             this.type = type;
    353         }
    354 
    355         private int waitForConnect(HeadsetBase headset) {
    356             // Try to connect for 20 seconds
    357             int result = 0;
    358             for (int i=0; i < 40 && result == 0; i++) {
    359                 // waitForAsyncConnect returns 0 on timeout, 1 on success, < 0 on error.
    360                 result = headset.waitForAsyncConnect(500, mConnectedStatusHandler);
    361                 if (isInterrupted()) {
    362                     headset.disconnect();
    363                     return EINTERRUPT;
    364                 }
    365             }
    366             return result;
    367         }
    368 
    369         @Override
    370         public void run() {
    371             long timestamp;
    372 
    373             timestamp = System.currentTimeMillis();
    374             HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter,
    375                                                   device, channel);
    376 
    377             int result = waitForConnect(headset);
    378 
    379             if (result != EINTERRUPT && result != 1) {
    380                 if (result == ECONNREFUSED && mDeviceSdpQuery == null) {
    381                     // The rfcomm channel number might have changed, do SDP
    382                     // query and try to connect again.
    383                     mDeviceSdpQuery = getCurrentDevice();
    384                     device.fetchUuidsWithSdp();
    385                     mConnectThread = null;
    386                     return;
    387                 } else {
    388                     Log.i(TAG, "Trying to connect to rfcomm socket again after 1 sec");
    389                     try {
    390                       sleep(1000);  // 1 second
    391                     } catch (InterruptedException e) {
    392                       return;
    393                     }
    394                 }
    395                 result = waitForConnect(headset);
    396             }
    397             mDeviceSdpQuery = null;
    398             if (result == EINTERRUPT) return;
    399 
    400             if (DBG) log("RFCOMM connection attempt took " +
    401                   (System.currentTimeMillis() - timestamp) + " ms");
    402             if (isInterrupted()) {
    403                 headset.disconnect();
    404                 return;
    405             }
    406             if (result < 0) {
    407                 Log.w(TAG, "headset.waitForAsyncConnect() error: " + result);
    408                 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
    409                 return;
    410             } else if (result == 0) {
    411                 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
    412                 Log.w(TAG, "mHeadset.waitForAsyncConnect() error: " + result + "(timeout)");
    413                 return;
    414             } else {
    415                 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget();
    416             }
    417         }
    418     }
    419 
    420     /**
    421      * Receives events from mConnectThread back in the main thread.
    422      */
    423     private final Handler mConnectingStatusHandler = new Handler() {
    424         @Override
    425         public void handleMessage(Message msg) {
    426             BluetoothDevice device = getCurrentDevice();
    427             if (device == null ||
    428                 mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTING) {
    429                 return;  // stale events
    430             }
    431 
    432             switch (msg.what) {
    433             case RFCOMM_ERROR:
    434                 if (DBG) log("Rfcomm error");
    435                 mConnectThread = null;
    436                 setState(device, BluetoothProfile.STATE_DISCONNECTED);
    437                 break;
    438             case RFCOMM_CONNECTED:
    439                 if (DBG) log("Rfcomm connected");
    440                 mConnectThread = null;
    441                 HeadsetBase headset = (HeadsetBase)msg.obj;
    442                 setState(device, BluetoothProfile.STATE_CONNECTED);
    443 
    444                 mRemoteHeadsets.get(device).mHeadset = headset;
    445                 mBtHandsfree.connectHeadset(headset, mRemoteHeadsets.get(device).mHeadsetType);
    446                 break;
    447             }
    448         }
    449     };
    450 
    451     /**
    452      * Receives events from a connected RFCOMM socket back in the main thread.
    453      */
    454     private final Handler mConnectedStatusHandler = new Handler() {
    455         @Override
    456         public void handleMessage(Message msg) {
    457             switch (msg.what) {
    458             case HeadsetBase.RFCOMM_DISCONNECTED:
    459                 mBtHandsfree.resetAtState();
    460                 mBtHandsfree.setVirtualCallInProgress(false);
    461                 BluetoothDevice device = getCurrentDevice();
    462                 if (device != null) {
    463                     setState(device, BluetoothProfile.STATE_DISCONNECTED);
    464                 }
    465                 break;
    466             }
    467         }
    468     };
    469 
    470     private synchronized void setState(BluetoothDevice device, int state) {
    471         int prevState = mRemoteHeadsets.get(device).mState;
    472         if (state != prevState) {
    473             if (DBG) log("Device: " + device +
    474                 " Headset  state" + prevState + " -> " + state);
    475             if (prevState == BluetoothProfile.STATE_CONNECTED) {
    476                 // Headset is disconnecting, stop the parser.
    477                 mBtHandsfree.disconnectHeadset();
    478             }
    479             Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
    480             intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    481             intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
    482             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    483             if (state == BluetoothProfile.STATE_DISCONNECTED) {
    484                 mRemoteHeadsets.get(device).mHeadset = null;
    485                 mRemoteHeadsets.get(device).mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN;
    486             }
    487 
    488             mRemoteHeadsets.get(device).mState = state;
    489 
    490             sendBroadcast(intent, BLUETOOTH_PERM);
    491             if (state == BluetoothHeadset.STATE_CONNECTED) {
    492                 // Set the priority to AUTO_CONNECT
    493                 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
    494                 adjustOtherHeadsetPriorities(device);
    495             }
    496             try {
    497                 mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.HEADSET,
    498                                                             state, prevState);
    499             } catch (RemoteException e) {
    500                 Log.e(TAG, "sendConnectionStateChange: exception");
    501             }
    502        }
    503     }
    504 
    505     private void adjustOtherHeadsetPriorities(BluetoothDevice connectedDevice) {
    506        for (BluetoothDevice device : mAdapter.getBondedDevices()) {
    507           if (getPriority(device) >= BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
    508               !device.equals(connectedDevice)) {
    509               setPriority(device, BluetoothHeadset.PRIORITY_ON);
    510           }
    511        }
    512     }
    513 
    514     private void setPriority(BluetoothDevice device, int priority) {
    515         try {
    516             mBinder.setPriority(device, priority);
    517         } catch (RemoteException e) {
    518             Log.e(TAG, "Error while setting priority for: " + device);
    519         }
    520     }
    521 
    522     private int getPriority(BluetoothDevice device) {
    523         try {
    524             return mBinder.getPriority(device);
    525         } catch (RemoteException e) {
    526             Log.e(TAG, "Error while getting priority for: " + device);
    527         }
    528         return BluetoothProfile.PRIORITY_UNDEFINED;
    529     }
    530 
    531     private synchronized void getSdpRecordsAndConnect(BluetoothDevice device) {
    532         if (!device.equals(getCurrentDevice())) {
    533             // stale
    534             return;
    535         }
    536 
    537         // Check if incoming connection has already connected.
    538         if (mRemoteHeadsets.get(device).mState == BluetoothProfile.STATE_CONNECTED) {
    539             return;
    540         }
    541 
    542         ParcelUuid[] uuids = device.getUuids();
    543         ParcelUuid[] localUuids = mAdapter.getUuids();
    544         int type = BluetoothHandsfree.TYPE_UNKNOWN;
    545         if (uuids != null) {
    546             if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree) &&
    547                 BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG)) {
    548                 log("SDP UUID: TYPE_HANDSFREE");
    549                 type = BluetoothHandsfree.TYPE_HANDSFREE;
    550                 mRemoteHeadsets.get(device).mHeadsetType = type;
    551                 int channel = device.getServiceChannel(BluetoothUuid.Handsfree);
    552                 mConnectThread = new RfcommConnectThread(device, channel, type);
    553                 if (mAdapter.isDiscovering()) {
    554                     mAdapter.cancelDiscovery();
    555                 }
    556                 mConnectThread.start();
    557                 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
    558                     setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
    559                 }
    560                 return;
    561             } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP) &&
    562                 BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG)) {
    563                 log("SDP UUID: TYPE_HEADSET");
    564                 type = BluetoothHandsfree.TYPE_HEADSET;
    565                 mRemoteHeadsets.get(device).mHeadsetType = type;
    566                 int channel = device.getServiceChannel(BluetoothUuid.HSP);
    567                 mConnectThread = new RfcommConnectThread(device, channel, type);
    568                 if (mAdapter.isDiscovering()) {
    569                     mAdapter.cancelDiscovery();
    570                 }
    571                 mConnectThread.start();
    572                 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
    573                     setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
    574                 }
    575                 return;
    576             }
    577         }
    578         log("SDP UUID: TYPE_UNKNOWN");
    579         mRemoteHeadsets.get(device).mHeadsetType = type;
    580         setState(device, BluetoothProfile.STATE_DISCONNECTED);
    581         return;
    582     }
    583 
    584     /**
    585      * Handlers for incoming service calls
    586      */
    587     private final IBluetoothHeadset.Stub mBinder = new IBluetoothHeadset.Stub() {
    588         public int getConnectionState(BluetoothDevice device) {
    589             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    590             BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
    591             if (headset == null) {
    592                 return BluetoothProfile.STATE_DISCONNECTED;
    593             }
    594             return headset.mState;
    595         }
    596 
    597         public List<BluetoothDevice> getConnectedDevices() {
    598             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    599             return getDevicesMatchingConnectionStates(
    600                 new int[] {BluetoothProfile.STATE_CONNECTED});
    601         }
    602 
    603         public boolean connect(BluetoothDevice device) {
    604             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    605                                            "Need BLUETOOTH_ADMIN permission");
    606             synchronized (BluetoothHeadsetService.this) {
    607                 BluetoothDevice currDevice = getCurrentDevice();
    608 
    609                 if (currDevice == device ||
    610                     getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
    611                     return false;
    612                 }
    613                 if (currDevice != null) {
    614                     disconnect(currDevice);
    615                 }
    616                 try {
    617                     return mBluetoothService.connectHeadset(device.getAddress());
    618                 } catch (RemoteException e) {
    619                     Log.e(TAG, "connectHeadset");
    620                     return false;
    621                 }
    622             }
    623         }
    624 
    625         public boolean disconnect(BluetoothDevice device) {
    626             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    627                                            "Need BLUETOOTH_ADMIN permission");
    628             synchronized (BluetoothHeadsetService.this) {
    629                 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
    630                 if (headset == null ||
    631                     headset.mState == BluetoothProfile.STATE_DISCONNECTED ||
    632                     headset.mState == BluetoothProfile.STATE_DISCONNECTING) {
    633                     return false;
    634                 }
    635                 try {
    636                     return mBluetoothService.disconnectHeadset(device.getAddress());
    637                 } catch (RemoteException e) {
    638                     Log.e(TAG, "disconnectHeadset");
    639                     return false;
    640                 }
    641             }
    642         }
    643 
    644         public synchronized boolean isAudioConnected(BluetoothDevice device) {
    645             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    646             if (device.equals(mAudioConnectedDevice)) return true;
    647             return false;
    648         }
    649 
    650         public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    651             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    652             List<BluetoothDevice> headsets = new ArrayList<BluetoothDevice>();
    653             for (BluetoothDevice device: mRemoteHeadsets.keySet()) {
    654                 int headsetState = getConnectionState(device);
    655                 for (int state : states) {
    656                     if (state == headsetState) {
    657                         headsets.add(device);
    658                         break;
    659                     }
    660                 }
    661             }
    662             return headsets;
    663         }
    664 
    665         public boolean startVoiceRecognition(BluetoothDevice device) {
    666             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    667             synchronized (BluetoothHeadsetService.this) {
    668                 if (device == null ||
    669                     mRemoteHeadsets.get(device) == null ||
    670                     mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED) {
    671                     return false;
    672                 }
    673                 return mBtHandsfree.startVoiceRecognition();
    674             }
    675         }
    676 
    677         public boolean stopVoiceRecognition(BluetoothDevice device) {
    678             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    679             synchronized (BluetoothHeadsetService.this) {
    680                 if (device == null ||
    681                     mRemoteHeadsets.get(device) == null ||
    682                     mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED) {
    683                     return false;
    684                 }
    685 
    686                 return mBtHandsfree.stopVoiceRecognition();
    687             }
    688         }
    689 
    690         public int getBatteryUsageHint(BluetoothDevice device) {
    691             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    692 
    693             return HeadsetBase.getAtInputCount();
    694         }
    695 
    696         public int getPriority(BluetoothDevice device) {
    697             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    698                 "Need BLUETOOTH_ADMIN permission");
    699             synchronized (BluetoothHeadsetService.this) {
    700                 int priority = Settings.Secure.getInt(getContentResolver(),
    701                         Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
    702                         BluetoothProfile.PRIORITY_UNDEFINED);
    703                 return priority;
    704             }
    705         }
    706 
    707         public boolean setPriority(BluetoothDevice device, int priority) {
    708             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    709                 "Need BLUETOOTH_ADMIN permission");
    710             synchronized (BluetoothHeadsetService.this) {
    711                 Settings.Secure.putInt(getContentResolver(),
    712                         Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
    713                         priority);
    714                 if (DBG) log("Saved priority " + device + " = " + priority);
    715                 return true;
    716             }
    717         }
    718 
    719         public boolean createIncomingConnect(BluetoothDevice device) {
    720             synchronized (BluetoothHeadsetService.this) {
    721                 HeadsetBase headset;
    722                 setState(device, BluetoothProfile.STATE_CONNECTING);
    723 
    724                 IncomingConnectionInfo info = mRemoteHeadsets.get(device).mIncomingInfo;
    725                 headset = new HeadsetBase(mPowerManager, mAdapter,
    726                                           device,
    727                                           info.mSocketFd, info.mRfcommChan,
    728                                           mConnectedStatusHandler);
    729 
    730                 mRemoteHeadsets.get(device).mHeadset = headset;
    731 
    732                 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget();
    733                 return true;
    734             }
    735         }
    736 
    737         public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
    738             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    739             synchronized (BluetoothHeadsetService.this) {
    740                 if (device == null ||
    741                     mRemoteHeadsets.get(device) == null ||
    742                     mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED ||
    743                     getAudioState(device) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
    744                     return false;
    745                 }
    746                 return mBtHandsfree.initiateScoUsingVirtualVoiceCall();
    747             }
    748         }
    749 
    750         public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
    751             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    752             synchronized (BluetoothHeadsetService.this) {
    753                 if (device == null ||
    754                     mRemoteHeadsets.get(device) == null ||
    755                     mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED ||
    756                     getAudioState(device) == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
    757                     return false;
    758                 }
    759                 return mBtHandsfree.terminateScoUsingVirtualVoiceCall();
    760             }
    761         }
    762 
    763         public boolean rejectIncomingConnect(BluetoothDevice device) {
    764             synchronized (BluetoothHeadsetService.this) {
    765                 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
    766                 if (headset != null) {
    767                     IncomingConnectionInfo info = headset.mIncomingInfo;
    768                     rejectIncomingConnection(info);
    769                 } else {
    770                     Log.e(TAG, "Error no record of remote headset");
    771                 }
    772                 return true;
    773             }
    774         }
    775 
    776         public boolean acceptIncomingConnect(BluetoothDevice device) {
    777             synchronized (BluetoothHeadsetService.this) {
    778                 HeadsetBase headset;
    779                 BluetoothRemoteHeadset cachedHeadset = mRemoteHeadsets.get(device);
    780                 if (cachedHeadset == null) {
    781                     Log.e(TAG, "Cached Headset is Null in acceptIncomingConnect");
    782                     return false;
    783                 }
    784                 IncomingConnectionInfo info = cachedHeadset.mIncomingInfo;
    785                 headset = new HeadsetBase(mPowerManager, mAdapter,
    786                                           device,
    787                                           info.mSocketFd, info.mRfcommChan,
    788                                           mConnectedStatusHandler);
    789 
    790                 setState(device, BluetoothProfile.STATE_CONNECTED);
    791 
    792                 cachedHeadset.mHeadset = headset;
    793                 mBtHandsfree.connectHeadset(headset, cachedHeadset.mHeadsetType);
    794 
    795                 if (DBG) log("Successfully used incoming connection");
    796                 return true;
    797             }
    798         }
    799 
    800         public  boolean cancelConnectThread() {
    801             synchronized (BluetoothHeadsetService.this) {
    802                 if (mConnectThread != null) {
    803                     // cancel the connection thread
    804                     mConnectThread.interrupt();
    805                     try {
    806                         mConnectThread.join();
    807                     } catch (InterruptedException e) {
    808                         Log.e(TAG, "Connection cancelled twice?", e);
    809                     }
    810                     mConnectThread = null;
    811                 }
    812                 return true;
    813             }
    814         }
    815 
    816         public boolean connectHeadsetInternal(BluetoothDevice device) {
    817             synchronized (BluetoothHeadsetService.this) {
    818                 BluetoothDevice currDevice = getCurrentDevice();
    819                 if (currDevice == null) {
    820                     BluetoothRemoteHeadset headset = new BluetoothRemoteHeadset();
    821                     mRemoteHeadsets.put(device, headset);
    822 
    823                     setState(device, BluetoothProfile.STATE_CONNECTING);
    824                     if (device.getUuids() == null) {
    825                         // We might not have got the UUID change notification from
    826                         // Bluez yet, if we have just paired. Try after 1.5 secs.
    827                         Message msg = new Message();
    828                         msg.what = CONNECT_HEADSET_DELAYED;
    829                         msg.obj = device;
    830                         mHandler.sendMessageDelayed(msg, 1500);
    831                     } else {
    832                         getSdpRecordsAndConnect(device);
    833                     }
    834                     return true;
    835                 } else {
    836                       Log.w(TAG, "connectHeadset(" + device + "): failed: already in state " +
    837                             mRemoteHeadsets.get(currDevice).mState +
    838                             " with headset " + currDevice);
    839                 }
    840                 return false;
    841             }
    842         }
    843 
    844         public boolean disconnectHeadsetInternal(BluetoothDevice device) {
    845             synchronized (BluetoothHeadsetService.this) {
    846                 BluetoothRemoteHeadset remoteHeadset = mRemoteHeadsets.get(device);
    847                 if (remoteHeadset == null) return false;
    848 
    849                 if (remoteHeadset.mState == BluetoothProfile.STATE_CONNECTED) {
    850                     // Send a dummy battery level message to force headset
    851                     // out of sniff mode so that it will immediately notice
    852                     // the disconnection. We are currently sending it for
    853                     // handsfree only.
    854                     // TODO: Call hci_conn_enter_active_mode() from
    855                     // rfcomm_send_disc() in the kernel instead.
    856                     // See http://b/1716887
    857                     setState(device, BluetoothProfile.STATE_DISCONNECTING);
    858 
    859                     HeadsetBase headset = remoteHeadset.mHeadset;
    860                     if (remoteHeadset.mHeadsetType == BluetoothHandsfree.TYPE_HANDSFREE) {
    861                         headset.sendURC("+CIEV: 7,3");
    862                     }
    863 
    864                     if (headset != null) {
    865                         headset.disconnect();
    866                         headset = null;
    867                     }
    868                     setState(device, BluetoothProfile.STATE_DISCONNECTED);
    869                     return true;
    870                 } else if (remoteHeadset.mState == BluetoothProfile.STATE_CONNECTING) {
    871                     // The state machine would have canceled the connect thread.
    872                     // Just set the state here.
    873                     setState(device, BluetoothProfile.STATE_DISCONNECTED);
    874                     return true;
    875                 }
    876                 return false;
    877             }
    878         }
    879 
    880         public boolean setAudioState(BluetoothDevice device, int state) {
    881             // mRemoteHeadsets handles put/get concurrency by itself
    882             int prevState = mRemoteHeadsets.get(device).mAudioState;
    883             mRemoteHeadsets.get(device).mAudioState = state;
    884             if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
    885                 mAudioConnectedDevice = device;
    886             } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
    887                 mAudioConnectedDevice = null;
    888             }
    889             Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
    890             intent.putExtra(BluetoothHeadset.EXTRA_STATE, state);
    891             intent.putExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, prevState);
    892             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    893             sendBroadcast(intent, android.Manifest.permission.BLUETOOTH);
    894             if (DBG) log("AudioStateIntent: " + device + " State: " + state
    895                          + " PrevState: " + prevState);
    896             return true;
    897         }
    898 
    899         public int getAudioState(BluetoothDevice device) {
    900             // mRemoteHeadsets handles put/get concurrency by itself
    901             BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
    902             if (headset == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    903 
    904             return headset.mAudioState;
    905        }
    906     };
    907 
    908     @Override
    909     public void onDestroy() {
    910         super.onDestroy();
    911         if (DBG) log("Stopping BluetoothHeadsetService");
    912         unregisterReceiver(mBluetoothReceiver);
    913         mBtHandsfree.onBluetoothDisabled();
    914         mAg.stop();
    915         sHasStarted = false;
    916         BluetoothDevice device = getCurrentDevice();
    917         if (device != null) {
    918             setState(device, BluetoothProfile.STATE_DISCONNECTED);
    919         }
    920     }
    921 
    922 
    923 
    924     private static void log(String msg) {
    925         Log.d(TAG, msg);
    926     }
    927 }
    928