• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.bluetooth.hfp;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothHeadset;
21 import android.bluetooth.BluetoothProfile;
22 import android.bluetooth.IBluetoothHeadset;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.media.AudioManager;
28 import android.os.Message;
29 import android.provider.Settings;
30 import android.util.Log;
31 import com.android.bluetooth.btservice.ProfileService;
32 import com.android.bluetooth.Utils;
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * Provides Bluetooth Headset and Handsfree profile, as a service in
38  * the Bluetooth application.
39  * @hide
40  */
41 public class HeadsetService extends ProfileService {
42     private static final boolean DBG = false;
43     private static final String TAG = "HeadsetService";
44     private static final String MODIFY_PHONE_STATE = android.Manifest.permission.MODIFY_PHONE_STATE;
45 
46     private HeadsetStateMachine mStateMachine;
47     private static HeadsetService sHeadsetService;
48 
getName()49     protected String getName() {
50         return TAG;
51     }
52 
initBinder()53     public IProfileServiceBinder initBinder() {
54         return new BluetoothHeadsetBinder(this);
55     }
56 
start()57     protected boolean start() {
58         mStateMachine = HeadsetStateMachine.make(this);
59         IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
60         filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
61         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
62         try {
63             registerReceiver(mHeadsetReceiver, filter);
64         } catch (Exception e) {
65             Log.w(TAG, "Unable to register headset receiver", e);
66         }
67         setHeadsetService(this);
68         return true;
69     }
70 
stop()71     protected boolean stop() {
72         try {
73             unregisterReceiver(mHeadsetReceiver);
74         } catch (Exception e) {
75             Log.w(TAG, "Unable to unregister headset receiver", e);
76         }
77         if (mStateMachine != null) {
78             mStateMachine.doQuit();
79         }
80         return true;
81     }
82 
cleanup()83     protected boolean cleanup() {
84         if (mStateMachine != null) {
85             mStateMachine.cleanup();
86         }
87         clearHeadsetService();
88         return true;
89     }
90 
91     private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
92         @Override
93         public void onReceive(Context context, Intent intent) {
94             String action = intent.getAction();
95             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
96                 mStateMachine.sendMessage(HeadsetStateMachine.INTENT_BATTERY_CHANGED, intent);
97             } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
98                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
99                 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
100                     mStateMachine.sendMessage(
101                             HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent);
102                 }
103             } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
104                 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
105                         BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
106                 if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
107                     Log.v(TAG, "Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
108                     mStateMachine.handleAccessPermissionResult(intent);
109                 }
110             }
111         }
112     };
113 
114     /**
115      * Handlers for incoming service calls
116      */
117     private static class BluetoothHeadsetBinder
118             extends IBluetoothHeadset.Stub implements IProfileServiceBinder {
119         private HeadsetService mService;
120 
BluetoothHeadsetBinder(HeadsetService svc)121         public BluetoothHeadsetBinder(HeadsetService svc) {
122             mService = svc;
123         }
cleanup()124         public boolean cleanup() {
125             mService = null;
126             return true;
127         }
128 
getService()129         private HeadsetService getService() {
130             if (!Utils.checkCallerAllowManagedProfiles(mService)) {
131                 Log.w(TAG, "Headset call not allowed for non-active user");
132                 return null;
133             }
134 
135             if (mService != null && mService.isAvailable()) {
136                 return mService;
137             }
138             return null;
139         }
140 
connect(BluetoothDevice device)141         public boolean connect(BluetoothDevice device) {
142             HeadsetService service = getService();
143             if (service == null) return false;
144             return service.connect(device);
145         }
146 
disconnect(BluetoothDevice device)147         public boolean disconnect(BluetoothDevice device) {
148             HeadsetService service = getService();
149             if (service == null) return false;
150             if (DBG) Log.d(TAG, "disconnect in HeadsetService");
151             return service.disconnect(device);
152         }
153 
getConnectedDevices()154         public List<BluetoothDevice> getConnectedDevices() {
155             HeadsetService service = getService();
156             if (service == null) return new ArrayList<BluetoothDevice>(0);
157             return service.getConnectedDevices();
158         }
159 
getDevicesMatchingConnectionStates(int[] states)160         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
161             HeadsetService service = getService();
162             if (service == null) return new ArrayList<BluetoothDevice>(0);
163             return service.getDevicesMatchingConnectionStates(states);
164         }
165 
getConnectionState(BluetoothDevice device)166         public int getConnectionState(BluetoothDevice device) {
167             HeadsetService service = getService();
168             if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
169             return service.getConnectionState(device);
170         }
171 
setPriority(BluetoothDevice device, int priority)172         public boolean setPriority(BluetoothDevice device, int priority) {
173             HeadsetService service = getService();
174             if (service == null) return false;
175             return service.setPriority(device, priority);
176         }
177 
getPriority(BluetoothDevice device)178         public int getPriority(BluetoothDevice device) {
179             HeadsetService service = getService();
180             if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
181             return service.getPriority(device);
182         }
183 
startVoiceRecognition(BluetoothDevice device)184         public boolean startVoiceRecognition(BluetoothDevice device) {
185             HeadsetService service = getService();
186             if (service == null) return false;
187             return service.startVoiceRecognition(device);
188         }
189 
stopVoiceRecognition(BluetoothDevice device)190         public boolean stopVoiceRecognition(BluetoothDevice device) {
191             HeadsetService service = getService();
192             if (service == null) return false;
193             return service.stopVoiceRecognition(device);
194         }
195 
isAudioOn()196         public boolean isAudioOn() {
197             HeadsetService service = getService();
198             if (service == null) return false;
199             return service.isAudioOn();
200         }
201 
isAudioConnected(BluetoothDevice device)202         public boolean isAudioConnected(BluetoothDevice device) {
203             HeadsetService service = getService();
204             if (service == null) return false;
205             return service.isAudioConnected(device);
206         }
207 
getBatteryUsageHint(BluetoothDevice device)208         public int getBatteryUsageHint(BluetoothDevice device) {
209             HeadsetService service = getService();
210             if (service == null) return 0;
211             return service.getBatteryUsageHint(device);
212         }
213 
acceptIncomingConnect(BluetoothDevice device)214         public boolean acceptIncomingConnect(BluetoothDevice device) {
215             HeadsetService service = getService();
216             if (service == null) return false;
217             return service.acceptIncomingConnect(device);
218         }
219 
rejectIncomingConnect(BluetoothDevice device)220         public boolean rejectIncomingConnect(BluetoothDevice device) {
221             HeadsetService service = getService();
222             if (service == null) return false;
223             return service.rejectIncomingConnect(device);
224         }
225 
getAudioState(BluetoothDevice device)226         public int getAudioState(BluetoothDevice device) {
227             HeadsetService service = getService();
228             if (service == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
229             return service.getAudioState(device);
230         }
231 
connectAudio()232         public boolean connectAudio() {
233             HeadsetService service = getService();
234             if (service == null) return false;
235             return service.connectAudio();
236         }
237 
disconnectAudio()238         public boolean disconnectAudio() {
239             HeadsetService service = getService();
240             if (service == null) return false;
241             return service.disconnectAudio();
242         }
243 
setAudioRouteAllowed(boolean allowed)244         public void setAudioRouteAllowed(boolean allowed) {
245             HeadsetService service = getService();
246             if (service == null) return;
247             service.setAudioRouteAllowed(allowed);
248         }
249 
getAudioRouteAllowed()250         public boolean getAudioRouteAllowed() {
251             HeadsetService service = getService();
252             if (service != null) {
253                 return service.getAudioRouteAllowed();
254             }
255             return false;
256         }
257 
setForceScoAudio(boolean forced)258         public void setForceScoAudio(boolean forced) {
259             HeadsetService service = getService();
260             if (service == null) return;
261             service.setForceScoAudio(forced);
262         }
263 
startScoUsingVirtualVoiceCall(BluetoothDevice device)264         public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
265             HeadsetService service = getService();
266             if (service == null) return false;
267             return service.startScoUsingVirtualVoiceCall(device);
268         }
269 
stopScoUsingVirtualVoiceCall(BluetoothDevice device)270         public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
271             HeadsetService service = getService();
272             if (service == null) return false;
273             return service.stopScoUsingVirtualVoiceCall(device);
274         }
275 
phoneStateChanged( int numActive, int numHeld, int callState, String number, int type)276         public void phoneStateChanged(
277                 int numActive, int numHeld, int callState, String number, int type) {
278             HeadsetService service = getService();
279             if (service == null) return;
280             service.phoneStateChanged(numActive, numHeld, callState, number, type);
281         }
282 
clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)283         public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
284                 String number, int type) {
285             HeadsetService service = getService();
286             if (service == null) return;
287             service.clccResponse(index, direction, status, mode, mpty, number, type);
288         }
289 
sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg)290         public boolean sendVendorSpecificResultCode(
291                 BluetoothDevice device, String command, String arg) {
292             HeadsetService service = getService();
293             if (service == null) {
294                 return false;
295             }
296             return service.sendVendorSpecificResultCode(device, command, arg);
297         }
298 
enableWBS()299         public boolean enableWBS() {
300             HeadsetService service = getService();
301             if (service == null) return false;
302             return service.enableWBS();
303         }
304 
disableWBS()305         public boolean disableWBS() {
306             HeadsetService service = getService();
307             if (service == null) return false;
308             return service.disableWBS();
309         }
310 
bindResponse(int ind_id, boolean ind_status)311         public void bindResponse(int ind_id, boolean ind_status) {
312             HeadsetService service = getService();
313             if (service == null) return;
314             service.bindResponse(ind_id, ind_status);
315         }
316     };
317 
318     // API methods
getHeadsetService()319     public static synchronized HeadsetService getHeadsetService() {
320         if (sHeadsetService != null && sHeadsetService.isAvailable()) {
321             if (DBG) Log.d(TAG, "getHeadsetService(): returning " + sHeadsetService);
322             return sHeadsetService;
323         }
324         if (DBG) {
325             if (sHeadsetService == null) {
326                 Log.d(TAG, "getHeadsetService(): service is NULL");
327             } else if (!(sHeadsetService.isAvailable())) {
328                 Log.d(TAG, "getHeadsetService(): service is not available");
329             }
330         }
331         return null;
332     }
333 
setHeadsetService(HeadsetService instance)334     private static synchronized void setHeadsetService(HeadsetService instance) {
335         if (instance != null && instance.isAvailable()) {
336             if (DBG) Log.d(TAG, "setHeadsetService(): set to: " + sHeadsetService);
337             sHeadsetService = instance;
338         } else {
339             if (DBG) {
340                 if (sHeadsetService == null) {
341                     Log.d(TAG, "setHeadsetService(): service not available");
342                 } else if (!sHeadsetService.isAvailable()) {
343                     Log.d(TAG, "setHeadsetService(): service is cleaning up");
344                 }
345             }
346         }
347     }
348 
clearHeadsetService()349     private static synchronized void clearHeadsetService() {
350         sHeadsetService = null;
351     }
352 
connect(BluetoothDevice device)353     public boolean connect(BluetoothDevice device) {
354         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
355         Log.d(TAG, "connect: device=" + device);
356         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
357             Log.w(TAG, "connect: PRIORITY_OFF, device=" + device);
358             return false;
359         }
360         int connectionState = mStateMachine.getConnectionState(device);
361         if (connectionState == BluetoothProfile.STATE_CONNECTED
362                 || connectionState == BluetoothProfile.STATE_CONNECTING) {
363             Log.w(TAG,
364                     "connect: already connected/connecting, connectionState=" + connectionState
365                             + ", device=" + device);
366             return false;
367         }
368         mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
369         return true;
370     }
371 
disconnect(BluetoothDevice device)372     boolean disconnect(BluetoothDevice device) {
373         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
374         Log.d(TAG, "disconnect: device=" + device);
375         int connectionState = mStateMachine.getConnectionState(device);
376         if (connectionState != BluetoothProfile.STATE_CONNECTED
377                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
378             Log.w(TAG,
379                     "disconnect: not connected/connecting, connectionState=" + connectionState
380                             + ", device=" + device);
381             return false;
382         }
383         mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
384         return true;
385     }
386 
getConnectedDevices()387     public List<BluetoothDevice> getConnectedDevices() {
388         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
389         return mStateMachine.getConnectedDevices();
390     }
391 
getDevicesMatchingConnectionStates(int[] states)392     private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
393         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
394         return mStateMachine.getDevicesMatchingConnectionStates(states);
395     }
396 
getConnectionState(BluetoothDevice device)397     public int getConnectionState(BluetoothDevice device) {
398         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
399         return mStateMachine.getConnectionState(device);
400     }
401 
setPriority(BluetoothDevice device, int priority)402     public boolean setPriority(BluetoothDevice device, int priority) {
403         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
404         Settings.Global.putInt(getContentResolver(),
405                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority);
406         if (DBG) Log.d(TAG, "Saved priority " + device + " = " + priority);
407         return true;
408     }
409 
getPriority(BluetoothDevice device)410     public int getPriority(BluetoothDevice device) {
411         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
412         int priority = Settings.Global.getInt(getContentResolver(),
413                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
414                 BluetoothProfile.PRIORITY_UNDEFINED);
415         return priority;
416     }
417 
startVoiceRecognition(BluetoothDevice device)418     boolean startVoiceRecognition(BluetoothDevice device) {
419         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
420         int connectionState = mStateMachine.getConnectionState(device);
421         if (connectionState != BluetoothProfile.STATE_CONNECTED
422                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
423             return false;
424         }
425         mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START);
426         return true;
427     }
428 
stopVoiceRecognition(BluetoothDevice device)429     boolean stopVoiceRecognition(BluetoothDevice device) {
430         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
431         // It seem that we really need to check the AudioOn state.
432         // But since we allow startVoiceRecognition in STATE_CONNECTED and
433         // STATE_CONNECTING state, we do these 2 in this method
434         int connectionState = mStateMachine.getConnectionState(device);
435         if (connectionState != BluetoothProfile.STATE_CONNECTED
436                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
437             return false;
438         }
439         mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP);
440         // TODO is this return correct when the voice recognition is not on?
441         return true;
442     }
443 
isAudioOn()444     boolean isAudioOn() {
445         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
446         return mStateMachine.isAudioOn();
447     }
448 
isAudioConnected(BluetoothDevice device)449     boolean isAudioConnected(BluetoothDevice device) {
450         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
451         return mStateMachine.isAudioConnected(device);
452     }
453 
getBatteryUsageHint(BluetoothDevice device)454     int getBatteryUsageHint(BluetoothDevice device) {
455         // TODO(BT) ask for BT stack support?
456         return 0;
457     }
458 
acceptIncomingConnect(BluetoothDevice device)459     boolean acceptIncomingConnect(BluetoothDevice device) {
460         // TODO(BT) remove it if stack does access control
461         return false;
462     }
463 
rejectIncomingConnect(BluetoothDevice device)464     boolean rejectIncomingConnect(BluetoothDevice device) {
465         // TODO(BT) remove it if stack does access control
466         return false;
467     }
468 
getAudioState(BluetoothDevice device)469     int getAudioState(BluetoothDevice device) {
470         return mStateMachine.getAudioState(device);
471     }
472 
setAudioRouteAllowed(boolean allowed)473     public void setAudioRouteAllowed(boolean allowed) {
474         mStateMachine.setAudioRouteAllowed(allowed);
475     }
476 
getAudioRouteAllowed()477     public boolean getAudioRouteAllowed() {
478         return mStateMachine.getAudioRouteAllowed();
479     }
480 
setForceScoAudio(boolean forced)481     public void setForceScoAudio(boolean forced) {
482         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
483         mStateMachine.setForceScoAudio(forced);
484     }
485 
connectAudio()486     boolean connectAudio() {
487         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
488         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
489         if (!mStateMachine.isConnected()) {
490             Log.w(TAG, "connectAudio: profile not connected");
491             return false;
492         }
493         if (mStateMachine.isAudioOn()) {
494             Log.w(TAG, "connectAudio: audio is already ON");
495             return false;
496         }
497         mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);
498         return true;
499     }
500 
disconnectAudio()501     boolean disconnectAudio() {
502         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
503         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
504         if (!mStateMachine.isAudioOn()) {
505             return false;
506         }
507         mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO);
508         return true;
509     }
510 
startScoUsingVirtualVoiceCall(BluetoothDevice device)511     boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
512         /* Do not ignore request if HSM state is still Disconnected or
513            Pending, it will be processed when transitioned to Connected */
514         mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_START, device);
515         return true;
516     }
517 
stopScoUsingVirtualVoiceCall(BluetoothDevice device)518     boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
519         int connectionState = mStateMachine.getConnectionState(device);
520         if (connectionState != BluetoothProfile.STATE_CONNECTED
521                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
522             return false;
523         }
524         mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_STOP, device);
525         return true;
526     }
527 
phoneStateChanged( int numActive, int numHeld, int callState, String number, int type)528     private void phoneStateChanged(
529             int numActive, int numHeld, int callState, String number, int type) {
530         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
531         Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.CALL_STATE_CHANGED);
532         msg.obj = new HeadsetCallState(numActive, numHeld, callState, number, type);
533         msg.arg1 = 0; // false
534         mStateMachine.sendMessage(msg);
535     }
536 
clccResponse( int index, int direction, int status, int mode, boolean mpty, String number, int type)537     private void clccResponse(
538             int index, int direction, int status, int mode, boolean mpty, String number, int type) {
539         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
540         mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
541                 new HeadsetClccResponse(index, direction, status, mode, mpty, number, type));
542     }
543 
sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg)544     private boolean sendVendorSpecificResultCode(
545             BluetoothDevice device, String command, String arg) {
546         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
547         int connectionState = mStateMachine.getConnectionState(device);
548         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
549             return false;
550         }
551         // Currently we support only "+ANDROID".
552         if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) {
553             Log.w(TAG, "Disallowed unsolicited result code command: " + command);
554             return false;
555         }
556         mStateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE,
557                 new HeadsetVendorSpecificResultCode(device, command, arg));
558         return true;
559     }
560 
enableWBS()561     boolean enableWBS() {
562         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
563         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
564         if (!mStateMachine.isConnected()) {
565             return false;
566         }
567         if (mStateMachine.isAudioOn()) {
568             return false;
569         }
570 
571         for (BluetoothDevice device : getConnectedDevices()) {
572             mStateMachine.sendMessage(HeadsetStateMachine.ENABLE_WBS, device);
573         }
574 
575         return true;
576     }
577 
disableWBS()578     boolean disableWBS() {
579         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
580         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
581         if (!mStateMachine.isConnected()) {
582             return false;
583         }
584         if (mStateMachine.isAudioOn()) {
585             return false;
586         }
587         for (BluetoothDevice device : getConnectedDevices()) {
588             mStateMachine.sendMessage(HeadsetStateMachine.DISABLE_WBS, device);
589         }
590         return true;
591     }
592 
bindResponse(int ind_id, boolean ind_status)593     private boolean bindResponse(int ind_id, boolean ind_status) {
594         for (BluetoothDevice device : getConnectedDevices()) {
595             int connectionState = mStateMachine.getConnectionState(device);
596             if (connectionState != BluetoothProfile.STATE_CONNECTED
597                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
598                 continue;
599             }
600             if (DBG) Log.d("Bind Response sent for", device.getAddress());
601             Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.BIND_RESPONSE);
602             msg.obj = device;
603             msg.arg1 = ind_id;
604             msg.arg2 = ind_status ? 1 : 0;
605             mStateMachine.sendMessage(msg);
606             return true;
607         }
608         return false;
609     }
610 
611     @Override
dump(StringBuilder sb)612     public void dump(StringBuilder sb) {
613         super.dump(sb);
614         if (mStateMachine != null) {
615             mStateMachine.dump(sb);
616         }
617     }
618 }
619