• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate()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 
BluetoothRemoteHeadset()106        BluetoothRemoteHeadset() {
107            mState = BluetoothHeadset.STATE_DISCONNECTED;
108            mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN;
109            mHeadset = null;
110            mIncomingInfo = null;
111        }
112 
BluetoothRemoteHeadset(int headsetType, IncomingConnectionInfo incomingInfo)113        BluetoothRemoteHeadset(int headsetType, IncomingConnectionInfo incomingInfo) {
114            mState = BluetoothHeadset.STATE_DISCONNECTED;
115            mHeadsetType = headsetType;
116            mHeadset = null;
117            mIncomingInfo = incomingInfo;
118        }
119    }
120 
getCurrentDevice()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
onStart(Intent intent, int startId)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                     rejectIncomingConnection(info);
230                     break;
231                 }
232             }
233         }
234     };
235 
rejectIncomingConnection(IncomingConnectionInfo info)236     private void rejectIncomingConnection(IncomingConnectionInfo info) {
237         HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter,
238             info.mRemoteDevice, info.mSocketFd, info.mRfcommChan, null);
239         headset.disconnect();
240     }
241 
242 
243     private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
244 
245         @Override
246         public void onReceive(Context context, Intent intent) {
247             String action = intent.getAction();
248             BluetoothDevice device =
249                     intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
250 
251             BluetoothDevice currDevice = getCurrentDevice();
252             int state = BluetoothHeadset.STATE_DISCONNECTED;
253             if (currDevice != null) {
254                 state = mRemoteHeadsets.get(currDevice).mState;
255             }
256 
257             if ((state == BluetoothHeadset.STATE_CONNECTED ||
258                     state == BluetoothHeadset.STATE_CONNECTING) &&
259                     action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) &&
260                     device.equals(currDevice)) {
261                 try {
262                     mBinder.disconnectHeadset(currDevice);
263                 } catch (RemoteException e) {}
264             } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
265                 switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
266                                            BluetoothAdapter.ERROR)) {
267                 case BluetoothAdapter.STATE_ON:
268                     adjustPriorities();
269                     mAg.start(mIncomingConnectionHandler);
270                     mBtHandsfree.onBluetoothEnabled();
271                     break;
272                 case BluetoothAdapter.STATE_TURNING_OFF:
273                     mBtHandsfree.onBluetoothDisabled();
274                     mAg.stop();
275                     if (currDevice != null) {
276                         setState(currDevice, BluetoothHeadset.STATE_DISCONNECTED,
277                                 BluetoothHeadset.RESULT_FAILURE,
278                                 BluetoothHeadset.LOCAL_DISCONNECT);
279                     }
280                     break;
281                 }
282             } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
283                 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
284                                                    BluetoothDevice.ERROR);
285                 switch(bondState) {
286                 case BluetoothDevice.BOND_BONDED:
287                     if (getPriority(device) == BluetoothHeadset.PRIORITY_UNDEFINED) {
288                         setPriority(device, BluetoothHeadset.PRIORITY_ON);
289                     }
290                     break;
291                 case BluetoothDevice.BOND_NONE:
292                     setPriority(device, BluetoothHeadset.PRIORITY_UNDEFINED);
293                     break;
294                 }
295             } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
296                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
297                 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
298                     mBtHandsfree.sendScoGainUpdate(intent.getIntExtra(
299                             AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0));
300                 }
301 
302             } else if (action.equals(BluetoothDevice.ACTION_UUID)) {
303                 if (device.equals(mDeviceSdpQuery) && device.equals(currDevice)) {
304                     // We have got SDP records for the device we are interested in.
305                     getSdpRecordsAndConnect(device);
306                 }
307             }
308         }
309     };
310 
311     private static final int CONNECT_HEADSET_DELAYED = 1;
312     private Handler mHandler = new Handler() {
313         @Override
314         public void handleMessage(Message msg) {
315             switch (msg.what) {
316                 case CONNECT_HEADSET_DELAYED:
317                     BluetoothDevice device = (BluetoothDevice) msg.obj;
318                     getSdpRecordsAndConnect(device);
319                     break;
320             }
321         }
322     };
323 
324     @Override
onBind(Intent intent)325     public IBinder onBind(Intent intent) {
326         return mBinder;
327     }
328 
329     // ------------------------------------------------------------------
330     // Bluetooth Headset Connect
331     // ------------------------------------------------------------------
332     private static final int RFCOMM_CONNECTED             = 1;
333     private static final int RFCOMM_ERROR                 = 2;
334 
335     private long mTimestamp;
336 
337     /**
338      * Thread for RFCOMM connection
339      * Messages are sent to mConnectingStatusHandler as connection progresses.
340      */
341     private RfcommConnectThread mConnectThread;
342     private class RfcommConnectThread extends Thread {
343         private BluetoothDevice device;
344         private int channel;
345         private int type;
346 
347         private static final int EINTERRUPT = -1000;
348         private static final int ECONNREFUSED = -111;
349 
RfcommConnectThread(BluetoothDevice device, int channel, int type)350         public RfcommConnectThread(BluetoothDevice device, int channel, int type) {
351             super();
352             this.device = device;
353             this.channel = channel;
354             this.type = type;
355         }
356 
waitForConnect(HeadsetBase headset)357         private int waitForConnect(HeadsetBase headset) {
358             // Try to connect for 20 seconds
359             int result = 0;
360             for (int i=0; i < 40 && result == 0; i++) {
361                 // waitForAsyncConnect returns 0 on timeout, 1 on success, < 0 on error.
362                 result = headset.waitForAsyncConnect(500, mConnectedStatusHandler);
363                 if (isInterrupted()) {
364                     headset.disconnect();
365                     return EINTERRUPT;
366                 }
367             }
368             return result;
369         }
370 
371         @Override
run()372         public void run() {
373             long timestamp;
374 
375             timestamp = System.currentTimeMillis();
376             HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter, device, channel);
377 
378             int result = waitForConnect(headset);
379 
380             if (result != EINTERRUPT && result != 1) {
381                 if (result == ECONNREFUSED && mDeviceSdpQuery == null) {
382                     // The rfcomm channel number might have changed, do SDP
383                     // query and try to connect again.
384                     mDeviceSdpQuery = getCurrentDevice();
385                     device.fetchUuidsWithSdp();
386                     mConnectThread = null;
387                     return;
388                 } else {
389                     Log.i(TAG, "Trying to connect to rfcomm socket again after 1 sec");
390                     try {
391                       sleep(1000);  // 1 second
392                     } catch (InterruptedException e) {}
393                 }
394                 result = waitForConnect(headset);
395             }
396             mDeviceSdpQuery = null;
397             if (result == EINTERRUPT) return;
398 
399             if (DBG) log("RFCOMM connection attempt took " +
400                   (System.currentTimeMillis() - timestamp) + " ms");
401             if (isInterrupted()) {
402                 headset.disconnect();
403                 return;
404             }
405             if (result < 0) {
406                 Log.w(TAG, "headset.waitForAsyncConnect() error: " + result);
407                 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
408                 return;
409             } else if (result == 0) {
410                 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
411                 Log.w(TAG, "mHeadset.waitForAsyncConnect() error: " + result + "(timeout)");
412                 return;
413             } else {
414                 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget();
415             }
416         }
417     }
418 
419     /**
420      * Receives events from mConnectThread back in the main thread.
421      */
422     private final Handler mConnectingStatusHandler = new Handler() {
423         @Override
424         public void handleMessage(Message msg) {
425             BluetoothDevice device = getCurrentDevice();
426             if (device == null ||
427                 mRemoteHeadsets.get(device).mState != BluetoothHeadset.STATE_CONNECTING) {
428                 return;  // stale events
429             }
430 
431             switch (msg.what) {
432             case RFCOMM_ERROR:
433                 if (DBG) log("Rfcomm error");
434                 mConnectThread = null;
435                 setState(device,
436                          BluetoothHeadset.STATE_DISCONNECTED, BluetoothHeadset.RESULT_FAILURE,
437                          BluetoothHeadset.LOCAL_DISCONNECT);
438                 break;
439             case RFCOMM_CONNECTED:
440                 if (DBG) log("Rfcomm connected");
441                 mConnectThread = null;
442                 HeadsetBase headset = (HeadsetBase)msg.obj;
443                 setState(device,
444                         BluetoothHeadset.STATE_CONNECTED, BluetoothHeadset.RESULT_SUCCESS);
445 
446                 mRemoteHeadsets.get(device).mHeadset = headset;
447                 mBtHandsfree.connectHeadset(headset, mRemoteHeadsets.get(device).mHeadsetType);
448                 break;
449             }
450         }
451     };
452 
453     /**
454      * Receives events from a connected RFCOMM socket back in the main thread.
455      */
456     private final Handler mConnectedStatusHandler = new Handler() {
457         @Override
458         public void handleMessage(Message msg) {
459             switch (msg.what) {
460             case HeadsetBase.RFCOMM_DISCONNECTED:
461                 mBtHandsfree.resetAtState();
462                 BluetoothDevice device = getCurrentDevice();
463                 if (device != null) {
464                     setState(device,
465                         BluetoothHeadset.STATE_DISCONNECTED, BluetoothHeadset.RESULT_FAILURE,
466                         BluetoothHeadset.REMOTE_DISCONNECT);
467                 }
468                 break;
469             }
470         }
471     };
472 
setState(BluetoothDevice device, int state)473     private void setState(BluetoothDevice device, int state) {
474         setState(device, state, BluetoothHeadset.RESULT_SUCCESS);
475     }
476 
setState(BluetoothDevice device, int state, int result)477     private void setState(BluetoothDevice device, int state, int result) {
478         setState(device, state, result, -1);
479     }
480 
setState(BluetoothDevice device, int state, int result, int initiator)481     private synchronized void setState(BluetoothDevice device,
482         int state, int result, int initiator) {
483         int prevState = mRemoteHeadsets.get(device).mState;
484         if (state != prevState) {
485             if (DBG) log("Device: " + device +
486                 " Headset  state" + prevState + " -> " + state + ", result = " + result);
487             if (prevState == BluetoothHeadset.STATE_CONNECTED) {
488                 mBtHandsfree.disconnectHeadset();
489             }
490             Intent intent = new Intent(BluetoothHeadset.ACTION_STATE_CHANGED);
491             intent.putExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, prevState);
492             intent.putExtra(BluetoothHeadset.EXTRA_STATE, state);
493             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
494             // Add Extra EXTRA_DISCONNECT_INITIATOR for DISCONNECTED state
495             if (state == BluetoothHeadset.STATE_DISCONNECTED) {
496                 if (initiator == -1) {
497                     log("Headset Disconnected Intent without Disconnect Initiator extra");
498                 } else {
499                     intent.putExtra(BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
500                                     initiator);
501                 }
502                 mRemoteHeadsets.get(device).mHeadset = null;
503                 mRemoteHeadsets.get(device).mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN;
504             }
505 
506             mRemoteHeadsets.get(device).mState = state;
507 
508             sendBroadcast(intent, BLUETOOTH_PERM);
509             if (state == BluetoothHeadset.STATE_CONNECTED) {
510                 // Set the priority to AUTO_CONNECT
511                 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
512                 adjustOtherHeadsetPriorities(device);
513             }
514        }
515     }
516 
adjustOtherHeadsetPriorities(BluetoothDevice connectedDevice)517     private void adjustOtherHeadsetPriorities(BluetoothDevice connectedDevice) {
518        for (BluetoothDevice device : mAdapter.getBondedDevices()) {
519           if (getPriority(device) >= BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
520               !device.equals(connectedDevice)) {
521               setPriority(device, BluetoothHeadset.PRIORITY_ON);
522           }
523        }
524     }
525 
setPriority(BluetoothDevice device, int priority)526     private void setPriority(BluetoothDevice device, int priority) {
527         try {
528             mBinder.setPriority(device, priority);
529         } catch (RemoteException e) {
530             Log.e(TAG, "Error while setting priority for: " + device);
531         }
532     }
533 
getPriority(BluetoothDevice device)534     private int getPriority(BluetoothDevice device) {
535         try {
536             return mBinder.getPriority(device);
537         } catch (RemoteException e) {
538             Log.e(TAG, "Error while getting priority for: " + device);
539         }
540         return BluetoothHeadset.PRIORITY_UNDEFINED;
541     }
542 
adjustPriorities()543     private void adjustPriorities() {
544         // This is to ensure backward compatibility.
545         // Only 1 device is set to AUTO_CONNECT
546         BluetoothDevice savedDevice = null;
547         int max_priority = BluetoothHeadset.PRIORITY_AUTO_CONNECT;
548         if (mAdapter.getBondedDevices() != null) {
549             for (BluetoothDevice device : mAdapter.getBondedDevices()) {
550                 int priority = getPriority(device);
551                 if (priority >= BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
552                     setPriority(device, BluetoothHeadset.PRIORITY_ON);
553                 }
554                 if (priority >= max_priority) {
555                     max_priority = priority;
556                     savedDevice = device;
557                 }
558             }
559             if (savedDevice != null) {
560                 setPriority(savedDevice, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
561             }
562         }
563     }
564 
getSdpRecordsAndConnect(BluetoothDevice device)565     private synchronized void getSdpRecordsAndConnect(BluetoothDevice device) {
566         if (!device.equals(getCurrentDevice())) {
567             // stale
568             return;
569         }
570 
571         // Check if incoming connection has already connected.
572         if (mRemoteHeadsets.get(device).mState == BluetoothHeadset.STATE_CONNECTED) {
573             return;
574         }
575 
576         ParcelUuid[] uuids = device.getUuids();
577         int type = BluetoothHandsfree.TYPE_UNKNOWN;
578         if (uuids != null) {
579             if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) {
580                 log("SDP UUID: TYPE_HANDSFREE");
581                 type = BluetoothHandsfree.TYPE_HANDSFREE;
582                 mRemoteHeadsets.get(device).mHeadsetType = type;
583                 int channel = device.getServiceChannel(BluetoothUuid.Handsfree);
584                 mConnectThread = new RfcommConnectThread(device, channel, type);
585                 mConnectThread.start();
586                 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
587                     setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
588                 }
589                 return;
590             } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) {
591                 log("SDP UUID: TYPE_HEADSET");
592                 type = BluetoothHandsfree.TYPE_HEADSET;
593                 mRemoteHeadsets.get(device).mHeadsetType = type;
594                 int channel = device.getServiceChannel(BluetoothUuid.HSP);
595                 mConnectThread = new RfcommConnectThread(device, channel, type);
596                 mConnectThread.start();
597                 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) {
598                     setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT);
599                 }
600                 return;
601             }
602         }
603         log("SDP UUID: TYPE_UNKNOWN");
604         mRemoteHeadsets.get(device).mHeadsetType = type;
605         setState(device, BluetoothHeadset.STATE_DISCONNECTED,
606                 BluetoothHeadset.RESULT_FAILURE, BluetoothHeadset.LOCAL_DISCONNECT);
607         return;
608     }
609 
610     /**
611      * Handlers for incoming service calls
612      */
613     private final IBluetoothHeadset.Stub mBinder = new IBluetoothHeadset.Stub() {
614         public int getState(BluetoothDevice device) {
615             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
616             BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
617             if (headset == null) {
618                 return BluetoothHeadset.STATE_DISCONNECTED;
619             }
620             return headset.mState;
621         }
622         public BluetoothDevice getCurrentHeadset() {
623             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
624             return getCurrentDevice();
625         }
626         public boolean connectHeadset(BluetoothDevice device) {
627             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
628                                            "Need BLUETOOTH_ADMIN permission");
629             synchronized (BluetoothHeadsetService.this) {
630                 try {
631                     return mBluetoothService.connectHeadset(device.getAddress());
632                 } catch (RemoteException e) {
633                     Log.e(TAG, "connectHeadset");
634                     return false;
635                 }
636             }
637         }
638         public void disconnectHeadset(BluetoothDevice device) {
639             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
640                                            "Need BLUETOOTH_ADMIN permission");
641             synchronized (BluetoothHeadsetService.this) {
642                 try {
643                     mBluetoothService.disconnectHeadset(device.getAddress());
644                 } catch (RemoteException e) {
645                     Log.e(TAG, "disconnectHeadset");
646                 }
647             }
648         }
649         public boolean isConnected(BluetoothDevice device) {
650             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
651 
652             BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
653             return headset != null && headset.mState == BluetoothHeadset.STATE_CONNECTED;
654         }
655         public boolean startVoiceRecognition() {
656             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
657             synchronized (BluetoothHeadsetService.this) {
658                 BluetoothDevice device = getCurrentDevice();
659 
660                 if (device == null ||
661                     mRemoteHeadsets.get(device).mState != BluetoothHeadset.STATE_CONNECTED) {
662                     return false;
663                 }
664                 return mBtHandsfree.startVoiceRecognition();
665             }
666         }
667         public boolean stopVoiceRecognition() {
668             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
669             synchronized (BluetoothHeadsetService.this) {
670                 BluetoothDevice device = getCurrentDevice();
671 
672                 if (device == null ||
673                     mRemoteHeadsets.get(device).mState != BluetoothHeadset.STATE_CONNECTED) {
674                     return false;
675                 }
676 
677                 return mBtHandsfree.stopVoiceRecognition();
678             }
679         }
680         public int getBatteryUsageHint() {
681             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
682 
683             return HeadsetBase.getAtInputCount();
684         }
685         public int getPriority(BluetoothDevice device) {
686             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
687                 "Need BLUETOOTH_ADMIN permission");
688             synchronized (BluetoothHeadsetService.this) {
689                 int priority = Settings.Secure.getInt(getContentResolver(),
690                         Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
691                         BluetoothHeadset.PRIORITY_UNDEFINED);
692                 return priority;
693             }
694         }
695 
696         public boolean setPriority(BluetoothDevice device, int priority) {
697             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
698                 "Need BLUETOOTH_ADMIN permission");
699             synchronized (BluetoothHeadsetService.this) {
700                 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
701                         return false;
702                 }
703                 if (priority < BluetoothHeadset.PRIORITY_OFF) {
704                     return false;
705                 }
706                 Settings.Secure.putInt(getContentResolver(),
707                         Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
708                         priority);
709                 if (DBG) log("Saved priority " + device + " = " + priority);
710                 return true;
711             }
712         }
713 
714         public boolean createIncomingConnect(BluetoothDevice device) {
715             synchronized (BluetoothHeadsetService.this) {
716                 HeadsetBase headset;
717                 setState(device, BluetoothHeadset.STATE_CONNECTING);
718 
719                 IncomingConnectionInfo info = mRemoteHeadsets.get(device).mIncomingInfo;
720                 headset = new HeadsetBase(mPowerManager, mAdapter, device,
721                         info.mSocketFd, info.mRfcommChan,
722                         mConnectedStatusHandler);
723 
724                 mRemoteHeadsets.get(device).mHeadset = headset;
725 
726                 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget();
727                 return true;
728             }
729         }
730 
731         public boolean rejectIncomingConnect(BluetoothDevice device) {
732             synchronized (BluetoothHeadsetService.this) {
733                 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device);
734                 if (headset != null) {
735                     IncomingConnectionInfo info = headset.mIncomingInfo;
736                     rejectIncomingConnection(info);
737                 } else {
738                     Log.e(TAG, "Error no record of remote headset");
739                 }
740                 return true;
741             }
742         }
743 
744         public boolean acceptIncomingConnect(BluetoothDevice device) {
745             synchronized (BluetoothHeadsetService.this) {
746                 HeadsetBase headset;
747                 BluetoothRemoteHeadset cachedHeadset = mRemoteHeadsets.get(device);
748                 if (cachedHeadset == null) {
749                     Log.e(TAG, "Cached Headset is Null in acceptIncomingConnect");
750                     return false;
751                 }
752                 IncomingConnectionInfo info = cachedHeadset.mIncomingInfo;
753                 headset = new HeadsetBase(mPowerManager, mAdapter, device,
754                         info.mSocketFd, info.mRfcommChan, mConnectedStatusHandler);
755 
756                 setState(device, BluetoothHeadset.STATE_CONNECTED, BluetoothHeadset.RESULT_SUCCESS);
757 
758                 cachedHeadset.mHeadset = headset;
759                 mBtHandsfree.connectHeadset(headset, cachedHeadset.mHeadsetType);
760 
761                 if (DBG) log("Successfully used incoming connection");
762                 return true;
763             }
764         }
765 
766         public  boolean cancelConnectThread() {
767             synchronized (BluetoothHeadsetService.this) {
768                 if (mConnectThread != null) {
769                     // cancel the connection thread
770                     mConnectThread.interrupt();
771                     try {
772                         mConnectThread.join();
773                     } catch (InterruptedException e) {
774                         Log.e(TAG, "Connection cancelled twice?", e);
775                     }
776                     mConnectThread = null;
777                 }
778                 return true;
779             }
780         }
781 
782         public boolean connectHeadsetInternal(BluetoothDevice device) {
783             synchronized (BluetoothHeadsetService.this) {
784                 BluetoothDevice currDevice = getCurrentDevice();
785                 if (currDevice == null) {
786                     BluetoothRemoteHeadset headset = new BluetoothRemoteHeadset();
787                     mRemoteHeadsets.put(device, headset);
788 
789                     setState(device, BluetoothHeadset.STATE_CONNECTING);
790                     if (device.getUuids() == null) {
791                         // We might not have got the UUID change notification from
792                         // Bluez yet, if we have just paired. Try after 1.5 secs.
793                         Message msg = new Message();
794                         msg.what = CONNECT_HEADSET_DELAYED;
795                         msg.obj = device;
796                         mHandler.sendMessageDelayed(msg, 1500);
797                     } else {
798                         getSdpRecordsAndConnect(device);
799                     }
800                     return true;
801                 } else {
802                       Log.w(TAG, "connectHeadset(" + device + "): failed: already in state " +
803                             mRemoteHeadsets.get(currDevice).mState +
804                             " with headset " + currDevice);
805                 }
806                 return false;
807             }
808         }
809 
810         public boolean disconnectHeadsetInternal(BluetoothDevice device) {
811             synchronized (BluetoothHeadsetService.this) {
812                 BluetoothRemoteHeadset remoteHeadset = mRemoteHeadsets.get(device);
813                 if (remoteHeadset == null) return false;
814 
815                 if (remoteHeadset.mState == BluetoothHeadset.STATE_CONNECTED) {
816                     // Send a dummy battery level message to force headset
817                     // out of sniff mode so that it will immediately notice
818                     // the disconnection. We are currently sending it for
819                     // handsfree only.
820                     // TODO: Call hci_conn_enter_active_mode() from
821                     // rfcomm_send_disc() in the kernel instead.
822                     // See http://b/1716887
823                     HeadsetBase headset = remoteHeadset.mHeadset;
824                     if (remoteHeadset.mHeadsetType == BluetoothHandsfree.TYPE_HANDSFREE) {
825                         headset.sendURC("+CIEV: 7,3");
826                     }
827 
828                     if (headset != null) {
829                         headset.disconnect();
830                         headset = null;
831                     }
832                     setState(device, BluetoothHeadset.STATE_DISCONNECTED,
833                              BluetoothHeadset.RESULT_CANCELED,
834                              BluetoothHeadset.LOCAL_DISCONNECT);
835                     return true;
836                 } else if (remoteHeadset.mState == BluetoothHeadset.STATE_CONNECTING) {
837                     // The state machine would have canceled the connect thread.
838                     // Just set the state here.
839                     setState(device, BluetoothHeadset.STATE_DISCONNECTED,
840                               BluetoothHeadset.RESULT_CANCELED,
841                               BluetoothHeadset.LOCAL_DISCONNECT);
842                     return true;
843                 }
844                 return false;
845             }
846         }
847     };
848 
849     @Override
onDestroy()850     public void onDestroy() {
851         super.onDestroy();
852         if (DBG) log("Stopping BluetoothHeadsetService");
853         unregisterReceiver(mBluetoothReceiver);
854         mBtHandsfree.onBluetoothDisabled();
855         mAg.stop();
856         sHasStarted = false;
857         if (getCurrentDevice() != null) {
858             setState(getCurrentDevice(), BluetoothHeadset.STATE_DISCONNECTED,
859                  BluetoothHeadset.RESULT_CANCELED,
860                  BluetoothHeadset.LOCAL_DISCONNECT);
861         }
862     }
863 
864 
865 
log(String msg)866     private static void log(String msg) {
867         Log.d(TAG, msg);
868     }
869 }
870