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 
256             return false;
257         }
258 
startScoUsingVirtualVoiceCall(BluetoothDevice device)259         public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
260             HeadsetService service = getService();
261             if (service == null) return false;
262             return service.startScoUsingVirtualVoiceCall(device);
263         }
264 
stopScoUsingVirtualVoiceCall(BluetoothDevice device)265         public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
266             HeadsetService service = getService();
267             if (service == null) return false;
268             return service.stopScoUsingVirtualVoiceCall(device);
269         }
270 
phoneStateChanged( int numActive, int numHeld, int callState, String number, int type)271         public void phoneStateChanged(
272                 int numActive, int numHeld, int callState, String number, int type) {
273             HeadsetService service = getService();
274             if (service == null) return;
275             service.phoneStateChanged(numActive, numHeld, callState, number, type);
276         }
277 
clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type)278         public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
279                 String number, int type) {
280             HeadsetService service = getService();
281             if (service == null) return;
282             service.clccResponse(index, direction, status, mode, mpty, number, type);
283         }
284 
sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg)285         public boolean sendVendorSpecificResultCode(
286                 BluetoothDevice device, String command, String arg) {
287             HeadsetService service = getService();
288             if (service == null) {
289                 return false;
290             }
291             return service.sendVendorSpecificResultCode(device, command, arg);
292         }
293 
enableWBS()294         public boolean enableWBS() {
295             HeadsetService service = getService();
296             if (service == null) return false;
297             return service.enableWBS();
298         }
299 
disableWBS()300         public boolean disableWBS() {
301             HeadsetService service = getService();
302             if (service == null) return false;
303             return service.disableWBS();
304         }
305 
bindResponse(int ind_id, boolean ind_status)306         public void bindResponse(int ind_id, boolean ind_status) {
307             HeadsetService service = getService();
308             if (service == null) return;
309             service.bindResponse(ind_id, ind_status);
310         }
311     };
312 
313     // API methods
getHeadsetService()314     public static synchronized HeadsetService getHeadsetService() {
315         if (sHeadsetService != null && sHeadsetService.isAvailable()) {
316             if (DBG) Log.d(TAG, "getHeadsetService(): returning " + sHeadsetService);
317             return sHeadsetService;
318         }
319         if (DBG) {
320             if (sHeadsetService == null) {
321                 Log.d(TAG, "getHeadsetService(): service is NULL");
322             } else if (!(sHeadsetService.isAvailable())) {
323                 Log.d(TAG, "getHeadsetService(): service is not available");
324             }
325         }
326         return null;
327     }
328 
setHeadsetService(HeadsetService instance)329     private static synchronized void setHeadsetService(HeadsetService instance) {
330         if (instance != null && instance.isAvailable()) {
331             if (DBG) Log.d(TAG, "setHeadsetService(): set to: " + sHeadsetService);
332             sHeadsetService = instance;
333         } else {
334             if (DBG) {
335                 if (sHeadsetService == null) {
336                     Log.d(TAG, "setHeadsetService(): service not available");
337                 } else if (!sHeadsetService.isAvailable()) {
338                     Log.d(TAG, "setHeadsetService(): service is cleaning up");
339                 }
340             }
341         }
342     }
343 
clearHeadsetService()344     private static synchronized void clearHeadsetService() {
345         sHeadsetService = null;
346     }
347 
connect(BluetoothDevice device)348     public boolean connect(BluetoothDevice device) {
349         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
350 
351         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
352             return false;
353         }
354 
355         int connectionState = mStateMachine.getConnectionState(device);
356         Log.d(TAG, "connectionState = " + connectionState);
357         if (connectionState == BluetoothProfile.STATE_CONNECTED
358                 || connectionState == BluetoothProfile.STATE_CONNECTING) {
359             return false;
360         }
361 
362         mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
363         return true;
364     }
365 
disconnect(BluetoothDevice device)366     boolean disconnect(BluetoothDevice device) {
367         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
368         int connectionState = mStateMachine.getConnectionState(device);
369         if (connectionState != BluetoothProfile.STATE_CONNECTED
370                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
371             return false;
372         }
373 
374         mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
375         return true;
376     }
377 
getConnectedDevices()378     public List<BluetoothDevice> getConnectedDevices() {
379         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
380         return mStateMachine.getConnectedDevices();
381     }
382 
getDevicesMatchingConnectionStates(int[] states)383     private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
384         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
385         return mStateMachine.getDevicesMatchingConnectionStates(states);
386     }
387 
getConnectionState(BluetoothDevice device)388     public int getConnectionState(BluetoothDevice device) {
389         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
390         return mStateMachine.getConnectionState(device);
391     }
392 
setPriority(BluetoothDevice device, int priority)393     public boolean setPriority(BluetoothDevice device, int priority) {
394         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
395         Settings.Global.putInt(getContentResolver(),
396                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority);
397         if (DBG) Log.d(TAG, "Saved priority " + device + " = " + priority);
398         return true;
399     }
400 
getPriority(BluetoothDevice device)401     public int getPriority(BluetoothDevice device) {
402         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
403         int priority = Settings.Global.getInt(getContentResolver(),
404                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
405                 BluetoothProfile.PRIORITY_UNDEFINED);
406         return priority;
407     }
408 
startVoiceRecognition(BluetoothDevice device)409     boolean startVoiceRecognition(BluetoothDevice device) {
410         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
411         int connectionState = mStateMachine.getConnectionState(device);
412         if (connectionState != BluetoothProfile.STATE_CONNECTED
413                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
414             return false;
415         }
416         mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START);
417         return true;
418     }
419 
stopVoiceRecognition(BluetoothDevice device)420     boolean stopVoiceRecognition(BluetoothDevice device) {
421         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
422         // It seem that we really need to check the AudioOn state.
423         // But since we allow startVoiceRecognition in STATE_CONNECTED and
424         // STATE_CONNECTING state, we do these 2 in this method
425         int connectionState = mStateMachine.getConnectionState(device);
426         if (connectionState != BluetoothProfile.STATE_CONNECTED
427                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
428             return false;
429         }
430         mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP);
431         // TODO is this return correct when the voice recognition is not on?
432         return true;
433     }
434 
isAudioOn()435     boolean isAudioOn() {
436         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
437         return mStateMachine.isAudioOn();
438     }
439 
isAudioConnected(BluetoothDevice device)440     boolean isAudioConnected(BluetoothDevice device) {
441         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
442         return mStateMachine.isAudioConnected(device);
443     }
444 
getBatteryUsageHint(BluetoothDevice device)445     int getBatteryUsageHint(BluetoothDevice device) {
446         // TODO(BT) ask for BT stack support?
447         return 0;
448     }
449 
acceptIncomingConnect(BluetoothDevice device)450     boolean acceptIncomingConnect(BluetoothDevice device) {
451         // TODO(BT) remove it if stack does access control
452         return false;
453     }
454 
rejectIncomingConnect(BluetoothDevice device)455     boolean rejectIncomingConnect(BluetoothDevice device) {
456         // TODO(BT) remove it if stack does access control
457         return false;
458     }
459 
getAudioState(BluetoothDevice device)460     int getAudioState(BluetoothDevice device) {
461         return mStateMachine.getAudioState(device);
462     }
463 
setAudioRouteAllowed(boolean allowed)464     public void setAudioRouteAllowed(boolean allowed) {
465         mStateMachine.setAudioRouteAllowed(allowed);
466     }
467 
getAudioRouteAllowed()468     public boolean getAudioRouteAllowed() {
469         return mStateMachine.getAudioRouteAllowed();
470     }
471 
connectAudio()472     boolean connectAudio() {
473         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
474         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
475         if (!mStateMachine.isConnected()) {
476             return false;
477         }
478         if (!mStateMachine.isSlcConnected()) {
479             return false;
480         }
481         if (mStateMachine.isAudioOn()) {
482             return false;
483         }
484         mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);
485         return true;
486     }
487 
disconnectAudio()488     boolean disconnectAudio() {
489         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
490         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
491         if (!mStateMachine.isAudioOn()) {
492             return false;
493         }
494         mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO);
495         return true;
496     }
497 
startScoUsingVirtualVoiceCall(BluetoothDevice device)498     boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
499         /* Do not ignore request if HSM state is still Disconnected or
500            Pending, it will be processed when transitioned to Connected */
501         mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_START, device);
502         return true;
503     }
504 
stopScoUsingVirtualVoiceCall(BluetoothDevice device)505     boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
506         int connectionState = mStateMachine.getConnectionState(device);
507         if (connectionState != BluetoothProfile.STATE_CONNECTED
508                 && connectionState != BluetoothProfile.STATE_CONNECTING) {
509             return false;
510         }
511         mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_STOP, device);
512         return true;
513     }
514 
phoneStateChanged( int numActive, int numHeld, int callState, String number, int type)515     private void phoneStateChanged(
516             int numActive, int numHeld, int callState, String number, int type) {
517         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
518         Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.CALL_STATE_CHANGED);
519         msg.obj = new HeadsetCallState(numActive, numHeld, callState, number, type);
520         msg.arg1 = 0; // false
521         mStateMachine.sendMessage(msg);
522     }
523 
clccResponse( int index, int direction, int status, int mode, boolean mpty, String number, int type)524     private void clccResponse(
525             int index, int direction, int status, int mode, boolean mpty, String number, int type) {
526         enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);
527         mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
528                 new HeadsetClccResponse(index, direction, status, mode, mpty, number, type));
529     }
530 
sendVendorSpecificResultCode( BluetoothDevice device, String command, String arg)531     private boolean sendVendorSpecificResultCode(
532             BluetoothDevice device, String command, String arg) {
533         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
534         int connectionState = mStateMachine.getConnectionState(device);
535         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
536             return false;
537         }
538         // Currently we support only "+ANDROID".
539         if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) {
540             Log.w(TAG, "Disallowed unsolicited result code command: " + command);
541             return false;
542         }
543         mStateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE,
544                 new HeadsetVendorSpecificResultCode(device, command, arg));
545         return true;
546     }
547 
enableWBS()548     boolean enableWBS() {
549         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
550         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
551         if (!mStateMachine.isConnected()) {
552             return false;
553         }
554         if (mStateMachine.isAudioOn()) {
555             return false;
556         }
557 
558         for (BluetoothDevice device : getConnectedDevices()) {
559             mStateMachine.sendMessage(HeadsetStateMachine.ENABLE_WBS, device);
560         }
561 
562         return true;
563     }
564 
disableWBS()565     boolean disableWBS() {
566         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
567         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
568         if (!mStateMachine.isConnected()) {
569             return false;
570         }
571         if (mStateMachine.isAudioOn()) {
572             return false;
573         }
574         for (BluetoothDevice device : getConnectedDevices()) {
575             mStateMachine.sendMessage(HeadsetStateMachine.DISABLE_WBS, device);
576         }
577         return true;
578     }
579 
bindResponse(int ind_id, boolean ind_status)580     private boolean bindResponse(int ind_id, boolean ind_status) {
581         for (BluetoothDevice device : getConnectedDevices()) {
582             int connectionState = mStateMachine.getConnectionState(device);
583             if (connectionState != BluetoothProfile.STATE_CONNECTED
584                     && connectionState != BluetoothProfile.STATE_CONNECTING) {
585                 continue;
586             }
587             if (DBG) Log.d("Bind Response sent for", device.getAddress());
588             Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.BIND_RESPONSE);
589             msg.obj = device;
590             msg.arg1 = ind_id;
591             msg.arg2 = ind_status ? 1 : 0;
592             mStateMachine.sendMessage(msg);
593             return true;
594         }
595         return false;
596     }
597 
598     @Override
dump(StringBuilder sb)599     public void dump(StringBuilder sb) {
600         super.dump(sb);
601         if (mStateMachine != null) {
602             mStateMachine.dump(sb);
603         }
604     }
605 }
606