1 /*
2  * Copyright (c) 2014 The Android Open Source Project
3  * Copyright (C) 2012 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bluetooth.hfpclient;
19 
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothProfile;
22 import android.bluetooth.BluetoothHeadsetClient;
23 import android.bluetooth.BluetoothHeadsetClientCall;
24 import android.bluetooth.IBluetoothHeadsetClient;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.media.AudioManager;
30 import android.os.Bundle;
31 import android.os.Message;
32 import android.provider.Settings;
33 import android.util.Log;
34 import com.android.bluetooth.btservice.ProfileService;
35 import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
36 import com.android.bluetooth.Utils;
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 
41 /**
42  * Provides Bluetooth Headset Client (HF Role) profile, as a service in the
43  * Bluetooth application.
44  *
45  * @hide
46  */
47 public class HeadsetClientService extends ProfileService {
48     private static final boolean DBG = false;
49     private static final String TAG = "HeadsetClientService";
50 
51     private HeadsetClientStateMachine mStateMachine;
52     private static HeadsetClientService sHeadsetClientService;
53 
54     public static String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";
55 
56     @Override
getName()57     protected String getName() {
58         return TAG;
59     }
60 
61     @Override
initBinder()62     public IProfileServiceBinder initBinder() {
63         return new BluetoothHeadsetClientBinder(this);
64     }
65 
66     @Override
start()67     protected boolean start() {
68         mStateMachine = HeadsetClientStateMachine.make(this);
69         IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
70         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
71         try {
72             registerReceiver(mBroadcastReceiver, filter);
73         } catch (Exception e) {
74             Log.w(TAG, "Unable to register broadcat receiver", e);
75         }
76         setHeadsetClientService(this);
77 
78         // Start the HfpClientConnectionService to create connection with telecom when HFP
79         // connection is available.
80         Intent startIntent = new Intent(this, HfpClientConnectionService.class);
81         startService(startIntent);
82 
83         return true;
84     }
85 
86     @Override
stop()87     protected boolean stop() {
88         try {
89             unregisterReceiver(mBroadcastReceiver);
90         } catch (Exception e) {
91             Log.w(TAG, "Unable to unregister broadcast receiver", e);
92         }
93         mStateMachine.doQuit();
94 
95         // Stop the HfpClientConnectionService.
96         Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
97         stopIntent.putExtra(HFP_CLIENT_STOP_TAG, true);
98         startService(stopIntent);
99 
100         return true;
101     }
102 
103     @Override
cleanup()104     protected boolean cleanup() {
105         if (mStateMachine != null) {
106             mStateMachine.cleanup();
107         }
108         clearHeadsetClientService();
109         return true;
110     }
111 
112     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
113         @Override
114         public void onReceive(Context context, Intent intent) {
115             String action = intent.getAction();
116 
117             // We handle the volume changes for Voice calls here since HFP audio volume control does
118             // not go through audio manager (audio mixer). We check if the voice call volume has
119             // changed and subsequently change the SCO volume see
120             // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in
121             // {@link HeadsetClientStateMachine} for details.
122             if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
123                 Log.d(TAG, "Volume changed for stream: " +
124                     intent.getExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE));
125                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
126                 if (streamType == AudioManager.STREAM_VOICE_CALL) {
127                     int streamValue = intent
128                             .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
129                     int streamPrevValue = intent.getIntExtra(
130                             AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
131 
132                     if (streamValue != -1 && streamValue != streamPrevValue) {
133                         mStateMachine.sendMessage(mStateMachine.obtainMessage(
134                                 HeadsetClientStateMachine.SET_SPEAKER_VOLUME, streamValue, 0));
135                     }
136                 }
137             }
138         }
139     };
140 
141     /**
142      * Handlers for incoming service calls
143      */
144     private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
145             implements IProfileServiceBinder {
146         private HeadsetClientService mService;
147 
BluetoothHeadsetClientBinder(HeadsetClientService svc)148         public BluetoothHeadsetClientBinder(HeadsetClientService svc) {
149             mService = svc;
150         }
151 
152         @Override
cleanup()153         public boolean cleanup() {
154             mService = null;
155             return true;
156         }
157 
getService()158         private HeadsetClientService getService() {
159             if (!Utils.checkCaller()) {
160                 Log.w(TAG, "HeadsetClient call not allowed for non-active user");
161                 return null;
162             }
163 
164             if (mService != null && mService.isAvailable()) {
165                 return mService;
166             }
167             return null;
168         }
169 
170         @Override
connect(BluetoothDevice device)171         public boolean connect(BluetoothDevice device) {
172             HeadsetClientService service = getService();
173             if (service == null) {
174                 return false;
175             }
176             return service.connect(device);
177         }
178 
179         @Override
disconnect(BluetoothDevice device)180         public boolean disconnect(BluetoothDevice device) {
181             HeadsetClientService service = getService();
182             if (service == null) {
183                 return false;
184             }
185             return service.disconnect(device);
186         }
187 
188         @Override
getConnectedDevices()189         public List<BluetoothDevice> getConnectedDevices() {
190             HeadsetClientService service = getService();
191             if (service == null) {
192                 return new ArrayList<BluetoothDevice>(0);
193             }
194             return service.getConnectedDevices();
195         }
196 
197         @Override
getDevicesMatchingConnectionStates(int[] states)198         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
199             HeadsetClientService service = getService();
200             if (service == null) {
201                 return new ArrayList<BluetoothDevice>(0);
202             }
203             return service.getDevicesMatchingConnectionStates(states);
204         }
205 
206         @Override
getConnectionState(BluetoothDevice device)207         public int getConnectionState(BluetoothDevice device) {
208             HeadsetClientService service = getService();
209             if (service == null) {
210                 return BluetoothProfile.STATE_DISCONNECTED;
211             }
212             return service.getConnectionState(device);
213         }
214 
215         @Override
setPriority(BluetoothDevice device, int priority)216         public boolean setPriority(BluetoothDevice device, int priority) {
217             HeadsetClientService service = getService();
218             if (service == null) {
219                 return false;
220             }
221             return service.setPriority(device, priority);
222         }
223 
224         @Override
getPriority(BluetoothDevice device)225         public int getPriority(BluetoothDevice device) {
226             HeadsetClientService service = getService();
227             if (service == null) {
228                 return BluetoothProfile.PRIORITY_UNDEFINED;
229             }
230             return service.getPriority(device);
231         }
232 
233         @Override
startVoiceRecognition(BluetoothDevice device)234         public boolean startVoiceRecognition(BluetoothDevice device) {
235             HeadsetClientService service = getService();
236             if (service == null) {
237                 return false;
238             }
239             return service.startVoiceRecognition(device);
240         }
241 
242         @Override
stopVoiceRecognition(BluetoothDevice device)243         public boolean stopVoiceRecognition(BluetoothDevice device) {
244             HeadsetClientService service = getService();
245             if (service == null) {
246                 return false;
247             }
248             return service.stopVoiceRecognition(device);
249         }
250 
251         @Override
acceptIncomingConnect(BluetoothDevice device)252         public boolean acceptIncomingConnect(BluetoothDevice device) {
253             HeadsetClientService service = getService();
254             if (service == null) {
255                 return false;
256             }
257             return service.acceptIncomingConnect(device);
258         }
259 
260         @Override
rejectIncomingConnect(BluetoothDevice device)261         public boolean rejectIncomingConnect(BluetoothDevice device) {
262             HeadsetClientService service = getService();
263             if (service == null) {
264                 return false;
265             }
266             return service.rejectIncomingConnect(device);
267         }
268 
269         @Override
getAudioState(BluetoothDevice device)270         public int getAudioState(BluetoothDevice device) {
271             HeadsetClientService service = getService();
272             if (service == null) {
273                 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
274             }
275             return service.getAudioState(device);
276         }
277 
278         @Override
setAudioRouteAllowed(boolean allowed)279         public void setAudioRouteAllowed(boolean allowed) {
280             HeadsetClientService service = getService();
281             if (service != null) {
282                 service.setAudioRouteAllowed(allowed);
283             }
284         }
285 
286         @Override
getAudioRouteAllowed()287         public boolean getAudioRouteAllowed() {
288             HeadsetClientService service = getService();
289             if (service != null) {
290                 return service.getAudioRouteAllowed();
291             }
292 
293             return false;
294         }
295 
296         @Override
connectAudio()297         public boolean connectAudio() {
298             HeadsetClientService service = getService();
299             if (service == null) {
300                 return false;
301             }
302             return service.connectAudio();
303         }
304 
305         @Override
disconnectAudio()306         public boolean disconnectAudio() {
307             HeadsetClientService service = getService();
308             if (service == null) {
309                 return false;
310             }
311             return service.disconnectAudio();
312         }
313 
314         @Override
acceptCall(BluetoothDevice device, int flag)315         public boolean acceptCall(BluetoothDevice device, int flag) {
316             HeadsetClientService service = getService();
317             if (service == null) {
318                 return false;
319             }
320             return service.acceptCall(device, flag);
321         }
322 
323         @Override
rejectCall(BluetoothDevice device)324         public boolean rejectCall(BluetoothDevice device) {
325             HeadsetClientService service = getService();
326             if (service == null) {
327                 return false;
328             }
329             return service.rejectCall(device);
330         }
331 
332         @Override
holdCall(BluetoothDevice device)333         public boolean holdCall(BluetoothDevice device) {
334             HeadsetClientService service = getService();
335             if (service == null) {
336                 return false;
337             }
338             return service.holdCall(device);
339         }
340 
341         @Override
terminateCall(BluetoothDevice device, int index)342         public boolean terminateCall(BluetoothDevice device, int index) {
343             HeadsetClientService service = getService();
344             if (service == null) {
345                 return false;
346             }
347             return service.terminateCall(device, index);
348         }
349 
350         @Override
explicitCallTransfer(BluetoothDevice device)351         public boolean explicitCallTransfer(BluetoothDevice device) {
352             HeadsetClientService service = getService();
353             if (service == null) {
354                 return false;
355             }
356             return service.explicitCallTransfer(device);
357         }
358 
359         @Override
enterPrivateMode(BluetoothDevice device, int index)360         public boolean enterPrivateMode(BluetoothDevice device, int index) {
361             HeadsetClientService service = getService();
362             if (service == null) {
363                 return false;
364             }
365             return service.enterPrivateMode(device, index);
366         }
367 
368         @Override
redial(BluetoothDevice device)369         public boolean redial(BluetoothDevice device) {
370             HeadsetClientService service = getService();
371             if (service == null) {
372                 return false;
373             }
374             return service.redial(device);
375         }
376 
377         @Override
dial(BluetoothDevice device, String number)378         public boolean dial(BluetoothDevice device, String number) {
379             HeadsetClientService service = getService();
380             if (service == null) {
381                 return false;
382             }
383             return service.dial(device, number);
384         }
385 
386         @Override
dialMemory(BluetoothDevice device, int location)387         public boolean dialMemory(BluetoothDevice device, int location) {
388             HeadsetClientService service = getService();
389             if (service == null) {
390                 return false;
391             }
392             return service.dialMemory(device, location);
393         }
394 
395         @Override
getCurrentCalls(BluetoothDevice device)396         public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
397             HeadsetClientService service = getService();
398             if (service == null) {
399                 return null;
400             }
401             return service.getCurrentCalls(device);
402         }
403 
404         @Override
sendDTMF(BluetoothDevice device, byte code)405         public boolean sendDTMF(BluetoothDevice device, byte code) {
406             HeadsetClientService service = getService();
407             if (service == null) {
408                 return false;
409             }
410             return service.sendDTMF(device, code);
411         }
412 
413         @Override
getLastVoiceTagNumber(BluetoothDevice device)414         public boolean getLastVoiceTagNumber(BluetoothDevice device) {
415             HeadsetClientService service = getService();
416             if (service == null) {
417                 return false;
418             }
419             return service.getLastVoiceTagNumber(device);
420         }
421 
422         @Override
getCurrentAgEvents(BluetoothDevice device)423         public Bundle getCurrentAgEvents(BluetoothDevice device) {
424             HeadsetClientService service = getService();
425             if (service == null) {
426                 return null;
427             }
428             return service.getCurrentAgEvents(device);
429         }
430 
431         @Override
getCurrentAgFeatures(BluetoothDevice device)432         public Bundle getCurrentAgFeatures(BluetoothDevice device) {
433             HeadsetClientService service = getService();
434             if (service == null) {
435                 return null;
436             }
437             return service.getCurrentAgFeatures(device);
438         }
439     };
440 
441     // API methods
getHeadsetClientService()442     public static synchronized HeadsetClientService getHeadsetClientService() {
443         if (sHeadsetClientService != null && sHeadsetClientService.isAvailable()) {
444             if (DBG) {
445                 Log.d(TAG, "getHeadsetClientService(): returning " + sHeadsetClientService);
446             }
447             return sHeadsetClientService;
448         }
449         if (DBG) {
450             if (sHeadsetClientService == null) {
451                 Log.d(TAG, "getHeadsetClientService(): service is NULL");
452             } else if (!(sHeadsetClientService.isAvailable())) {
453                 Log.d(TAG, "getHeadsetClientService(): service is not available");
454             }
455         }
456         return null;
457     }
458 
setHeadsetClientService(HeadsetClientService instance)459     private static synchronized void setHeadsetClientService(HeadsetClientService instance) {
460         if (instance != null && instance.isAvailable()) {
461             if (DBG) {
462                 Log.d(TAG, "setHeadsetClientService(): set to: " + sHeadsetClientService);
463             }
464             sHeadsetClientService = instance;
465         } else {
466             if (DBG) {
467                 if (sHeadsetClientService == null) {
468                     Log.d(TAG, "setHeadsetClientService(): service not available");
469                 } else if (!sHeadsetClientService.isAvailable()) {
470                     Log.d(TAG, "setHeadsetClientService(): service is cleaning up");
471                 }
472             }
473         }
474     }
475 
clearHeadsetClientService()476     private static synchronized void clearHeadsetClientService() {
477         sHeadsetClientService = null;
478     }
479 
connect(BluetoothDevice device)480     public boolean connect(BluetoothDevice device) {
481         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
482                 "Need BLUETOOTH ADMIN permission");
483 
484         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
485             return false;
486         }
487 
488         int connectionState = mStateMachine.getConnectionState(device);
489         if (connectionState == BluetoothProfile.STATE_CONNECTED ||
490                 connectionState == BluetoothProfile.STATE_CONNECTING) {
491             return false;
492         }
493 
494         mStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT, device);
495         return true;
496     }
497 
disconnect(BluetoothDevice device)498     boolean disconnect(BluetoothDevice device) {
499         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
500                 "Need BLUETOOTH ADMIN permission");
501         int connectionState = mStateMachine.getConnectionState(device);
502         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
503                 connectionState != BluetoothProfile.STATE_CONNECTING) {
504             return false;
505         }
506 
507         mStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT, device);
508         return true;
509     }
510 
getConnectedDevices()511     public List<BluetoothDevice> getConnectedDevices() {
512         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
513         return mStateMachine.getConnectedDevices();
514     }
515 
getDevicesMatchingConnectionStates(int[] states)516     private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
517         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
518         return mStateMachine.getDevicesMatchingConnectionStates(states);
519     }
520 
getConnectionState(BluetoothDevice device)521     int getConnectionState(BluetoothDevice device) {
522         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
523         return mStateMachine.getConnectionState(device);
524     }
525 
526     // TODO Should new setting for HeadsetClient priority be created?
setPriority(BluetoothDevice device, int priority)527     public boolean setPriority(BluetoothDevice device, int priority) {
528         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
529                 "Need BLUETOOTH_ADMIN permission");
530         Settings.Global.putInt(getContentResolver(),
531                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
532                 priority);
533         if (DBG) {
534             Log.d(TAG, "Saved priority " + device + " = " + priority);
535         }
536         return true;
537     }
538 
getPriority(BluetoothDevice device)539     public int getPriority(BluetoothDevice device) {
540         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
541                 "Need BLUETOOTH_ADMIN permission");
542         int priority = Settings.Global.getInt(getContentResolver(),
543                 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()),
544                 BluetoothProfile.PRIORITY_UNDEFINED);
545         return priority;
546     }
547 
startVoiceRecognition(BluetoothDevice device)548     boolean startVoiceRecognition(BluetoothDevice device) {
549         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
550         int connectionState = mStateMachine.getConnectionState(device);
551         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
552                 connectionState != BluetoothProfile.STATE_CONNECTING) {
553             return false;
554         }
555         mStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START);
556         return true;
557     }
558 
stopVoiceRecognition(BluetoothDevice device)559     boolean stopVoiceRecognition(BluetoothDevice device) {
560         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
561         // It seem that we really need to check the AudioOn state.
562         // But since we allow startVoiceRecognition in STATE_CONNECTED and
563         // STATE_CONNECTING state, we do these 2 in this method
564         int connectionState = mStateMachine.getConnectionState(device);
565         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
566                 connectionState != BluetoothProfile.STATE_CONNECTING) {
567             return false;
568         }
569         mStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP);
570         return true;
571     }
572 
acceptIncomingConnect(BluetoothDevice device)573     boolean acceptIncomingConnect(BluetoothDevice device) {
574         // TODO(BT) remove it if stack does access control
575         return false;
576     }
577 
rejectIncomingConnect(BluetoothDevice device)578     boolean rejectIncomingConnect(BluetoothDevice device) {
579         // TODO(BT) remove it if stack does access control
580         return false;
581     }
582 
getAudioState(BluetoothDevice device)583     int getAudioState(BluetoothDevice device) {
584         return mStateMachine.getAudioState(device);
585     }
586 
setAudioRouteAllowed(boolean allowed)587     public void setAudioRouteAllowed(boolean allowed) {
588         mStateMachine.setAudioRouteAllowed(allowed);
589     }
590 
getAudioRouteAllowed()591     public boolean getAudioRouteAllowed() {
592         return mStateMachine.getAudioRouteAllowed();
593     }
594 
connectAudio()595     boolean connectAudio() {
596         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
597         if (!mStateMachine.isConnected()) {
598             return false;
599         }
600         if (mStateMachine.isAudioOn()) {
601             return false;
602         }
603         mStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
604         return true;
605     }
606 
disconnectAudio()607     boolean disconnectAudio() {
608         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
609         if (!mStateMachine.isAudioOn()) {
610             return false;
611         }
612         mStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
613         return true;
614     }
615 
holdCall(BluetoothDevice device)616     boolean holdCall(BluetoothDevice device) {
617         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
618         int connectionState = mStateMachine.getConnectionState(device);
619         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
620                 connectionState != BluetoothProfile.STATE_CONNECTING) {
621             return false;
622         }
623         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.HOLD_CALL);
624         mStateMachine.sendMessage(msg);
625         return true;
626     }
627 
acceptCall(BluetoothDevice device, int flag)628     boolean acceptCall(BluetoothDevice device, int flag) {
629         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
630         int connectionState = mStateMachine.getConnectionState(device);
631         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
632                 connectionState != BluetoothProfile.STATE_CONNECTING) {
633             return false;
634         }
635         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);
636         msg.arg1 = flag;
637         mStateMachine.sendMessage(msg);
638         return true;
639     }
640 
rejectCall(BluetoothDevice device)641     boolean rejectCall(BluetoothDevice device) {
642         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
643         int connectionState = mStateMachine.getConnectionState(device);
644         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
645                 connectionState != BluetoothProfile.STATE_CONNECTING) {
646             return false;
647         }
648 
649         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.REJECT_CALL);
650         mStateMachine.sendMessage(msg);
651         return true;
652     }
653 
terminateCall(BluetoothDevice device, int index)654     boolean terminateCall(BluetoothDevice device, int index) {
655         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
656         int connectionState = mStateMachine.getConnectionState(device);
657         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
658                 connectionState != BluetoothProfile.STATE_CONNECTING) {
659             return false;
660         }
661 
662         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL);
663         msg.arg1 = index;
664         mStateMachine.sendMessage(msg);
665         return true;
666     }
667 
enterPrivateMode(BluetoothDevice device, int index)668     boolean enterPrivateMode(BluetoothDevice device, int index) {
669         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
670         int connectionState = mStateMachine.getConnectionState(device);
671         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
672                 connectionState != BluetoothProfile.STATE_CONNECTING) {
673             return false;
674         }
675 
676         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE);
677         msg.arg1 = index;
678         mStateMachine.sendMessage(msg);
679         return true;
680     }
681 
redial(BluetoothDevice device)682     boolean redial(BluetoothDevice device) {
683         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
684         int connectionState = mStateMachine.getConnectionState(device);
685         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
686                 connectionState != BluetoothProfile.STATE_CONNECTING) {
687             return false;
688         }
689 
690         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.REDIAL);
691         mStateMachine.sendMessage(msg);
692         return true;
693     }
694 
dial(BluetoothDevice device, String number)695     boolean dial(BluetoothDevice device, String number) {
696         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
697         int connectionState = mStateMachine.getConnectionState(device);
698         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
699                 connectionState != BluetoothProfile.STATE_CONNECTING) {
700             return false;
701         }
702 
703         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER);
704         msg.obj = number;
705         mStateMachine.sendMessage(msg);
706         return true;
707     }
708 
dialMemory(BluetoothDevice device, int location)709     boolean dialMemory(BluetoothDevice device, int location) {
710         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
711         int connectionState = mStateMachine.getConnectionState(device);
712         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
713                 connectionState != BluetoothProfile.STATE_CONNECTING) {
714             return false;
715         }
716         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.DIAL_MEMORY);
717         msg.arg1 = location;
718         mStateMachine.sendMessage(msg);
719         return true;
720     }
721 
sendDTMF(BluetoothDevice device, byte code)722     public boolean sendDTMF(BluetoothDevice device, byte code) {
723         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
724         int connectionState = mStateMachine.getConnectionState(device);
725         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
726                 connectionState != BluetoothProfile.STATE_CONNECTING) {
727             return false;
728         }
729         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.SEND_DTMF);
730         msg.arg1 = code;
731         mStateMachine.sendMessage(msg);
732         return true;
733     }
734 
getLastVoiceTagNumber(BluetoothDevice device)735     public boolean getLastVoiceTagNumber(BluetoothDevice device) {
736         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
737         int connectionState = mStateMachine.getConnectionState(device);
738         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
739                 connectionState != BluetoothProfile.STATE_CONNECTING) {
740             return false;
741         }
742         Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.LAST_VTAG_NUMBER);
743         mStateMachine.sendMessage(msg);
744         return true;
745     }
746 
getCurrentCalls(BluetoothDevice device)747     public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
748         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
749         int connectionState = mStateMachine.getConnectionState(device);
750         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
751             return null;
752         }
753         return mStateMachine.getCurrentCalls();
754     }
755 
explicitCallTransfer(BluetoothDevice device)756     public boolean explicitCallTransfer(BluetoothDevice device) {
757         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
758         int connectionState = mStateMachine.getConnectionState(device);
759         if (connectionState != BluetoothProfile.STATE_CONNECTED &&
760                 connectionState != BluetoothProfile.STATE_CONNECTING) {
761             return false;
762         }
763         Message msg = mStateMachine
764                 .obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER);
765         mStateMachine.sendMessage(msg);
766         return true;
767     }
768 
getCurrentAgEvents(BluetoothDevice device)769     public Bundle getCurrentAgEvents(BluetoothDevice device) {
770         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
771         int connectionState = mStateMachine.getConnectionState(device);
772         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
773             return null;
774         }
775         return mStateMachine.getCurrentAgEvents();
776     }
777 
getCurrentAgFeatures(BluetoothDevice device)778     public Bundle getCurrentAgFeatures(BluetoothDevice device) {
779         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
780         int connectionState = mStateMachine.getConnectionState(device);
781         if (connectionState != BluetoothProfile.STATE_CONNECTED) {
782             return null;
783         }
784         return mStateMachine.getCurrentAgFeatures();
785     }
786 
787     @Override
dump(StringBuilder sb)788     public void dump(StringBuilder sb) {
789         super.dump(sb);
790         if (mStateMachine != null) {
791             mStateMachine.dump(sb);
792         }
793     }
794 }
795