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