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