• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.util.Log;
22 
23 import com.android.bluetooth.btservice.AdapterService;
24 import com.android.internal.annotations.VisibleForTesting;
25 
26 import java.util.Objects;
27 
28 /**
29  * Defines native calls that are used by state machine/service to either send or receive
30  * messages to/from the native stack. This file is registered for the native methods in
31  * corresponding CPP file.
32  */
33 public class HeadsetNativeInterface {
34     private static final String TAG = "HeadsetNativeInterface";
35 
36     private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
37 
38     static {
classInitNative()39         classInitNative();
40     }
41 
42     private static HeadsetNativeInterface sInterface;
43     private static final Object INSTANCE_LOCK = new Object();
44     private AdapterService mAdapterService;
45 
HeadsetNativeInterface()46     private HeadsetNativeInterface() {
47         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
48                 "AdapterService cannot be null when HeadsetNativeInterface init");
49     }
50 
51     /**
52      * This class is a singleton because native library should only be loaded once
53      *
54      * @return default instance
55      */
getInstance()56     public static HeadsetNativeInterface getInstance() {
57         synchronized (INSTANCE_LOCK) {
58             if (sInterface == null) {
59                 sInterface = new HeadsetNativeInterface();
60             }
61         }
62         return sInterface;
63     }
64 
sendMessageToService(HeadsetStackEvent event)65     private void sendMessageToService(HeadsetStackEvent event) {
66         HeadsetService service = HeadsetService.getHeadsetService();
67         if (service != null) {
68             service.messageFromNative(event);
69         } else {
70             // Service must call cleanup() when quiting and native stack shouldn't send any event
71             // after cleanup() -> cleanupNative() is called.
72             Log.w(TAG, "Stack sent event while service is not available: " + event);
73         }
74     }
75 
getDevice(byte[] address)76     private BluetoothDevice getDevice(byte[] address) {
77         return mAdapterService.getDeviceFromByte(address);
78     }
79 
getByteAddress(BluetoothDevice device)80     private byte[] getByteAddress(BluetoothDevice device) {
81         return mAdapterService.getByteIdentityAddress(device);
82     }
83 
onConnectionStateChanged(int state, byte[] address)84     void onConnectionStateChanged(int state, byte[] address) {
85         HeadsetStackEvent event =
86                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED, state,
87                         getDevice(address));
88         sendMessageToService(event);
89     }
90 
91     // Callbacks for native code
92 
onAudioStateChanged(int state, byte[] address)93     private void onAudioStateChanged(int state, byte[] address) {
94         HeadsetStackEvent event =
95                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, state,
96                         getDevice(address));
97         sendMessageToService(event);
98     }
99 
onVrStateChanged(int state, byte[] address)100     private void onVrStateChanged(int state, byte[] address) {
101         HeadsetStackEvent event =
102                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED, state,
103                         getDevice(address));
104         sendMessageToService(event);
105     }
106 
onAnswerCall(byte[] address)107     private void onAnswerCall(byte[] address) {
108         HeadsetStackEvent event =
109                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL, getDevice(address));
110         sendMessageToService(event);
111     }
112 
onHangupCall(byte[] address)113     private void onHangupCall(byte[] address) {
114         HeadsetStackEvent event =
115                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL, getDevice(address));
116         sendMessageToService(event);
117     }
118 
onVolumeChanged(int type, int volume, byte[] address)119     private void onVolumeChanged(int type, int volume, byte[] address) {
120         HeadsetStackEvent event =
121                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED, type, volume,
122                         getDevice(address));
123         sendMessageToService(event);
124     }
125 
onDialCall(String number, byte[] address)126     private void onDialCall(String number, byte[] address) {
127         HeadsetStackEvent event =
128                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, number,
129                         getDevice(address));
130         sendMessageToService(event);
131     }
132 
onSendDtmf(int dtmf, byte[] address)133     private void onSendDtmf(int dtmf, byte[] address) {
134         HeadsetStackEvent event =
135                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SEND_DTMF, dtmf,
136                         getDevice(address));
137         sendMessageToService(event);
138     }
139 
onNoiseReductionEnable(boolean enable, byte[] address)140     private void onNoiseReductionEnable(boolean enable, byte[] address) {
141         HeadsetStackEvent event =
142                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_NOISE_REDUCTION, enable ? 1 : 0,
143                         getDevice(address));
144         sendMessageToService(event);
145     }
146 
onWBS(int codec, byte[] address)147     private void onWBS(int codec, byte[] address) {
148         HeadsetStackEvent event =
149                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_WBS, codec, getDevice(address));
150         sendMessageToService(event);
151     }
152 
onAtChld(int chld, byte[] address)153     private void onAtChld(int chld, byte[] address) {
154         HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CHLD, chld,
155                 getDevice(address));
156         sendMessageToService(event);
157     }
158 
onAtCnum(byte[] address)159     private void onAtCnum(byte[] address) {
160         HeadsetStackEvent event =
161                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST,
162                         getDevice(address));
163         sendMessageToService(event);
164     }
165 
onAtCind(byte[] address)166     private void onAtCind(byte[] address) {
167         HeadsetStackEvent event =
168                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CIND, getDevice(address));
169         sendMessageToService(event);
170     }
171 
onAtCops(byte[] address)172     private void onAtCops(byte[] address) {
173         HeadsetStackEvent event =
174                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_COPS, getDevice(address));
175         sendMessageToService(event);
176     }
177 
onAtClcc(byte[] address)178     private void onAtClcc(byte[] address) {
179         HeadsetStackEvent event =
180                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CLCC, getDevice(address));
181         sendMessageToService(event);
182     }
183 
onUnknownAt(String atString, byte[] address)184     private void onUnknownAt(String atString, byte[] address) {
185         HeadsetStackEvent event =
186                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT, atString,
187                         getDevice(address));
188         sendMessageToService(event);
189     }
190 
onKeyPressed(byte[] address)191     private void onKeyPressed(byte[] address) {
192         HeadsetStackEvent event =
193                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED, getDevice(address));
194         sendMessageToService(event);
195     }
196 
onATBind(String atString, byte[] address)197     private void onATBind(String atString, byte[] address) {
198         HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIND, atString,
199                 getDevice(address));
200         sendMessageToService(event);
201     }
202 
onATBiev(int indId, int indValue, byte[] address)203     private void onATBiev(int indId, int indValue, byte[] address) {
204         HeadsetStackEvent event =
205                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIEV, indId, indValue,
206                         getDevice(address));
207         sendMessageToService(event);
208     }
209 
onAtBia(boolean service, boolean roam, boolean signal, boolean battery, byte[] address)210     private void onAtBia(boolean service, boolean roam, boolean signal, boolean battery,
211             byte[] address) {
212         HeadsetAgIndicatorEnableState agIndicatorEnableState =
213                 new HeadsetAgIndicatorEnableState(service, roam, signal, battery);
214         HeadsetStackEvent event =
215                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIA, agIndicatorEnableState,
216                         getDevice(address));
217         sendMessageToService(event);
218     }
219 
220     // Native wrappers to help unit testing
221 
222     /**
223      * Initialize native stack
224      *
225      * @param maxHfClients maximum number of headset clients that can be connected simultaneously
226      * @param inbandRingingEnabled whether in-band ringing is enabled on this AG
227      */
228     @VisibleForTesting
init(int maxHfClients, boolean inbandRingingEnabled)229     public void init(int maxHfClients, boolean inbandRingingEnabled) {
230         initializeNative(maxHfClients, inbandRingingEnabled);
231     }
232 
233     /**
234      * Closes the interface
235      */
236     @VisibleForTesting
cleanup()237     public void cleanup() {
238         cleanupNative();
239     }
240 
241     /**
242      * ok/error response
243      *
244      * @param device target device
245      * @param responseCode 0 - ERROR, 1 - OK
246      * @param errorCode error code in case of ERROR
247      * @return True on success, False on failure
248      */
249     @VisibleForTesting
atResponseCode(BluetoothDevice device, int responseCode, int errorCode)250     public boolean atResponseCode(BluetoothDevice device, int responseCode, int errorCode) {
251         return atResponseCodeNative(responseCode, errorCode, getByteAddress(device));
252     }
253 
254     /**
255      * Pre-formatted AT response, typically in response to unknown AT cmd
256      *
257      * @param device target device
258      * @param responseString formatted AT response string
259      * @return True on success, False on failure
260      */
261     @VisibleForTesting
atResponseString(BluetoothDevice device, String responseString)262     public boolean atResponseString(BluetoothDevice device, String responseString) {
263         return atResponseStringNative(responseString, getByteAddress(device));
264     }
265 
266     /**
267      * Connect to headset
268      *
269      * @param device target headset
270      * @return True on success, False on failure
271      */
272     @VisibleForTesting
connectHfp(BluetoothDevice device)273     public boolean connectHfp(BluetoothDevice device) {
274         return connectHfpNative(getByteAddress(device));
275     }
276 
277     /**
278      * Disconnect from headset
279      *
280      * @param device target headset
281      * @return True on success, False on failure
282      */
283     @VisibleForTesting
disconnectHfp(BluetoothDevice device)284     public boolean disconnectHfp(BluetoothDevice device) {
285         return disconnectHfpNative(getByteAddress(device));
286     }
287 
288     /**
289      * Connect HFP audio (SCO) to headset
290      *
291      * @param device target headset
292      * @return True on success, False on failure
293      */
294     @VisibleForTesting
connectAudio(BluetoothDevice device)295     public boolean connectAudio(BluetoothDevice device) {
296         return connectAudioNative(getByteAddress(device));
297     }
298 
299     /**
300      * Disconnect HFP audio (SCO) from to headset
301      *
302      * @param device target headset
303      * @return True on success, False on failure
304      */
305     @VisibleForTesting
disconnectAudio(BluetoothDevice device)306     public boolean disconnectAudio(BluetoothDevice device) {
307         return disconnectAudioNative(getByteAddress(device));
308     }
309 
310     /**
311      * Checks whether the device support echo cancellation and/or noise reduction via the AT+BRSF
312      * bitmask
313      *
314      * @param device target headset
315      * @return true if the device support echo cancellation or noise reduction, false otherwise
316      */
isNoiseReductionSupported(BluetoothDevice device)317     public boolean isNoiseReductionSupported(BluetoothDevice device) {
318         return isNoiseReductionSupportedNative(getByteAddress(device));
319     }
320 
321     /**
322      * Checks whether the device supports voice recognition via the AT+BRSF bitmask
323      *
324      * @param device target headset
325      * @return true if the device supports voice recognition, false otherwise
326      */
isVoiceRecognitionSupported(BluetoothDevice device)327     public boolean isVoiceRecognitionSupported(BluetoothDevice device) {
328         return isVoiceRecognitionSupportedNative(getByteAddress(device));
329     }
330 
331     /**
332      * Start voice recognition
333      *
334      * @param device target headset
335      * @return True on success, False on failure
336      */
337     @VisibleForTesting
startVoiceRecognition(BluetoothDevice device)338     public boolean startVoiceRecognition(BluetoothDevice device) {
339         return startVoiceRecognitionNative(getByteAddress(device));
340     }
341 
342 
343     /**
344      * Stop voice recognition
345      *
346      * @param device target headset
347      * @return True on success, False on failure
348      */
349     @VisibleForTesting
stopVoiceRecognition(BluetoothDevice device)350     public boolean stopVoiceRecognition(BluetoothDevice device) {
351         return stopVoiceRecognitionNative(getByteAddress(device));
352     }
353 
354     /**
355      * Set HFP audio (SCO) volume
356      *
357      * @param device target headset
358      * @param volumeType type of volume
359      * @param volume value value
360      * @return True on success, False on failure
361      */
362     @VisibleForTesting
setVolume(BluetoothDevice device, int volumeType, int volume)363     public boolean setVolume(BluetoothDevice device, int volumeType, int volume) {
364         return setVolumeNative(volumeType, volume, getByteAddress(device));
365     }
366 
367     /**
368      * Response for CIND command
369      *
370      * @param device target device
371      * @param service service availability, 0 - no service, 1 - presence of service
372      * @param numActive number of active calls
373      * @param numHeld number of held calls
374      * @param callState overall call state [0-6]
375      * @param signal signal quality [0-5]
376      * @param roam roaming indicator, 0 - not roaming, 1 - roaming
377      * @param batteryCharge battery charge level [0-5]
378      * @return True on success, False on failure
379      */
380     @VisibleForTesting
cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge)381     public boolean cindResponse(BluetoothDevice device, int service, int numActive, int numHeld,
382             int callState, int signal, int roam, int batteryCharge) {
383         return cindResponseNative(service, numActive, numHeld, callState, signal, roam,
384                 batteryCharge, getByteAddress(device));
385     }
386 
387     /**
388      * Combined device status change notification
389      *
390      * @param device target device
391      * @param deviceState device status object
392      * @return True on success, False on failure
393      */
394     @VisibleForTesting
notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState)395     public boolean notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState) {
396         return notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam,
397                 deviceState.mSignal, deviceState.mBatteryCharge, getByteAddress(device));
398     }
399 
400     /**
401      * Response for CLCC command. Can be iteratively called for each call index. Call index of 0
402      * will be treated as NULL termination (Completes response)
403      *
404      * @param device target device
405      * @param index index of the call given by the sequence of setting up or receiving the calls
406      * as seen by the served subscriber. Calls hold their number until they are released. New
407      * calls take the lowest available number.
408      * @param dir direction of the call, 0 (outgoing), 1 (incoming)
409      * @param status 0 = Active, 1 = Held, 2 = Dialing (outgoing calls only), 3 = Alerting
410      * (outgoing calls only), 4 = Incoming (incoming calls only), 5 = Waiting (incoming calls
411      * only), 6 = Call held by Response and Hold
412      * @param mode 0 (Voice), 1 (Data), 2 (FAX)
413      * @param mpty 0 - this call is NOT a member of a multi-party (conference) call, 1 - this
414      * call IS a member of a multi-party (conference) call
415      * @param number optional
416      * @param type optional
417      * @return True on success, False on failure
418      */
419     @VisibleForTesting
clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, boolean mpty, String number, int type)420     public boolean clccResponse(BluetoothDevice device, int index, int dir, int status, int mode,
421             boolean mpty, String number, int type) {
422         return clccResponseNative(index, dir, status, mode, mpty, number, type,
423                 getByteAddress(device));
424     }
425 
426     /**
427      * Response for COPS command
428      *
429      * @param device target device
430      * @param operatorName operator name
431      * @return True on success, False on failure
432      */
433     @VisibleForTesting
copsResponse(BluetoothDevice device, String operatorName)434     public boolean copsResponse(BluetoothDevice device, String operatorName) {
435         return copsResponseNative(operatorName, getByteAddress(device));
436     }
437 
438     /**
439      *  Notify of a call state change
440      *  Each update notifies
441      *    1. Number of active/held/ringing calls
442      *    2. call_state: This denotes the state change that triggered this msg
443      *                   This will take one of the values from BtHfCallState
444      *    3. number & type: valid only for incoming & waiting call
445      *
446      * @param device target device for this update
447      * @param callState callstate structure
448      * @return True on success, False on failure
449      */
450     @VisibleForTesting
phoneStateChange(BluetoothDevice device, HeadsetCallState callState)451     public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) {
452         return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
453                 callState.mCallState, callState.mNumber, callState.mType, callState.mName,
454                 getByteAddress(device));
455     }
456 
457     /**
458      * Set whether we will initiate SCO or not
459      *
460      * @param value True to enable, False to disable
461      * @return True on success, False on failure
462      */
463     @VisibleForTesting
setScoAllowed(boolean value)464     public boolean setScoAllowed(boolean value) {
465         return setScoAllowedNative(value);
466     }
467 
468     /**
469      * Enable or disable in-band ringing for the current service level connection through sending
470      * +BSIR AT command
471      *
472      * @param value True to enable, False to disable
473      * @return True on success, False on failure
474      */
475     @VisibleForTesting
sendBsir(BluetoothDevice device, boolean value)476     public boolean sendBsir(BluetoothDevice device, boolean value) {
477         return sendBsirNative(value, getByteAddress(device));
478     }
479 
480     /**
481      * Set the current active headset device for SCO audio
482      * @param device current active SCO device
483      * @return true on success
484      */
485     @VisibleForTesting
setActiveDevice(BluetoothDevice device)486     public boolean setActiveDevice(BluetoothDevice device) {
487         return setActiveDeviceNative(getByteAddress(device));
488     }
489 
490     /* Native methods */
classInitNative()491     private static native void classInitNative();
492 
atResponseCodeNative(int responseCode, int errorCode, byte[] address)493     private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address);
494 
atResponseStringNative(String responseString, byte[] address)495     private native boolean atResponseStringNative(String responseString, byte[] address);
496 
initializeNative(int maxHfClients, boolean inbandRingingEnabled)497     private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled);
498 
cleanupNative()499     private native void cleanupNative();
500 
connectHfpNative(byte[] address)501     private native boolean connectHfpNative(byte[] address);
502 
disconnectHfpNative(byte[] address)503     private native boolean disconnectHfpNative(byte[] address);
504 
connectAudioNative(byte[] address)505     private native boolean connectAudioNative(byte[] address);
506 
disconnectAudioNative(byte[] address)507     private native boolean disconnectAudioNative(byte[] address);
508 
isNoiseReductionSupportedNative(byte[] address)509     private native boolean isNoiseReductionSupportedNative(byte[] address);
510 
isVoiceRecognitionSupportedNative(byte[] address)511     private native boolean isVoiceRecognitionSupportedNative(byte[] address);
512 
startVoiceRecognitionNative(byte[] address)513     private native boolean startVoiceRecognitionNative(byte[] address);
514 
stopVoiceRecognitionNative(byte[] address)515     private native boolean stopVoiceRecognitionNative(byte[] address);
516 
setVolumeNative(int volumeType, int volume, byte[] address)517     private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
518 
cindResponseNative(int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge, byte[] address)519     private native boolean cindResponseNative(int service, int numActive, int numHeld,
520             int callState, int signal, int roam, int batteryCharge, byte[] address);
521 
notifyDeviceStatusNative(int networkState, int serviceType, int signal, int batteryCharge, byte[] address)522     private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
523             int batteryCharge, byte[] address);
524 
clccResponseNative(int index, int dir, int status, int mode, boolean mpty, String number, int type, byte[] address)525     private native boolean clccResponseNative(int index, int dir, int status, int mode,
526             boolean mpty, String number, int type, byte[] address);
527 
copsResponseNative(String operatorName, byte[] address)528     private native boolean copsResponseNative(String operatorName, byte[] address);
529 
phoneStateChangeNative(int numActive, int numHeld, int callState, String number, int type, String name, byte[] address)530     private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
531             String number, int type, String name, byte[] address);
532 
setScoAllowedNative(boolean value)533     private native boolean setScoAllowedNative(boolean value);
534 
sendBsirNative(boolean value, byte[] address)535     private native boolean sendBsirNative(boolean value, byte[] address);
536 
setActiveDeviceNative(byte[] address)537     private native boolean setActiveDeviceNative(byte[] address);
538 }
539