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      * Start voice recognition
302      *
303      * @param device target headset
304      * @return True on success, False on failure
305      */
306     @VisibleForTesting
startVoiceRecognition(BluetoothDevice device)307     public boolean startVoiceRecognition(BluetoothDevice device) {
308         return startVoiceRecognitionNative(Utils.getByteAddress(device));
309     }
310 
311 
312     /**
313      * Stop voice recognition
314      *
315      * @param device target headset
316      * @return True on success, False on failure
317      */
318     @VisibleForTesting
stopVoiceRecognition(BluetoothDevice device)319     public boolean stopVoiceRecognition(BluetoothDevice device) {
320         return stopVoiceRecognitionNative(Utils.getByteAddress(device));
321     }
322 
323     /**
324      * Set HFP audio (SCO) volume
325      *
326      * @param device target headset
327      * @param volumeType type of volume
328      * @param volume value value
329      * @return True on success, False on failure
330      */
331     @VisibleForTesting
setVolume(BluetoothDevice device, int volumeType, int volume)332     public boolean setVolume(BluetoothDevice device, int volumeType, int volume) {
333         return setVolumeNative(volumeType, volume, Utils.getByteAddress(device));
334     }
335 
336     /**
337      * Response for CIND command
338      *
339      * @param device target device
340      * @param service service availability, 0 - no service, 1 - presence of service
341      * @param numActive number of active calls
342      * @param numHeld number of held calls
343      * @param callState overall call state [0-6]
344      * @param signal signal quality [0-5]
345      * @param roam roaming indicator, 0 - not roaming, 1 - roaming
346      * @param batteryCharge battery charge level [0-5]
347      * @return True on success, False on failure
348      */
349     @VisibleForTesting
cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge)350     public boolean cindResponse(BluetoothDevice device, int service, int numActive, int numHeld,
351             int callState, int signal, int roam, int batteryCharge) {
352         return cindResponseNative(service, numActive, numHeld, callState, signal, roam,
353                 batteryCharge, Utils.getByteAddress(device));
354     }
355 
356     /**
357      * Combined device status change notification
358      *
359      * @param device target device
360      * @param deviceState device status object
361      * @return True on success, False on failure
362      */
363     @VisibleForTesting
notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState)364     public boolean notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState) {
365         return notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam,
366                 deviceState.mSignal, deviceState.mBatteryCharge, Utils.getByteAddress(device));
367     }
368 
369     /**
370      * Response for CLCC command. Can be iteratively called for each call index. Call index of 0
371      * will be treated as NULL termination (Completes response)
372      *
373      * @param device target device
374      * @param index index of the call given by the sequence of setting up or receiving the calls
375      * as seen by the served subscriber. Calls hold their number until they are released. New
376      * calls take the lowest available number.
377      * @param dir direction of the call, 0 (outgoing), 1 (incoming)
378      * @param status 0 = Active, 1 = Held, 2 = Dialing (outgoing calls only), 3 = Alerting
379      * (outgoing calls only), 4 = Incoming (incoming calls only), 5 = Waiting (incoming calls
380      * only), 6 = Call held by Response and Hold
381      * @param mode 0 (Voice), 1 (Data), 2 (FAX)
382      * @param mpty 0 - this call is NOT a member of a multi-party (conference) call, 1 - this
383      * call IS a member of a multi-party (conference) call
384      * @param number optional
385      * @param type optional
386      * @return True on success, False on failure
387      */
388     @VisibleForTesting
clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, boolean mpty, String number, int type)389     public boolean clccResponse(BluetoothDevice device, int index, int dir, int status, int mode,
390             boolean mpty, String number, int type) {
391         return clccResponseNative(index, dir, status, mode, mpty, number, type,
392                 Utils.getByteAddress(device));
393     }
394 
395     /**
396      * Response for COPS command
397      *
398      * @param device target device
399      * @param operatorName operator name
400      * @return True on success, False on failure
401      */
402     @VisibleForTesting
copsResponse(BluetoothDevice device, String operatorName)403     public boolean copsResponse(BluetoothDevice device, String operatorName) {
404         return copsResponseNative(operatorName, Utils.getByteAddress(device));
405     }
406 
407     /**
408      *  Notify of a call state change
409      *  Each update notifies
410      *    1. Number of active/held/ringing calls
411      *    2. call_state: This denotes the state change that triggered this msg
412      *                   This will take one of the values from BtHfCallState
413      *    3. number & type: valid only for incoming & waiting call
414      *
415      * @param device target device for this update
416      * @param callState callstate structure
417      * @return True on success, False on failure
418      */
419     @VisibleForTesting
phoneStateChange(BluetoothDevice device, HeadsetCallState callState)420     public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) {
421         return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
422                 callState.mCallState, callState.mNumber, callState.mType, callState.mName,
423                 Utils.getByteAddress(device));
424     }
425 
426     /**
427      * Set whether we will initiate SCO or not
428      *
429      * @param value True to enable, False to disable
430      * @return True on success, False on failure
431      */
432     @VisibleForTesting
setScoAllowed(boolean value)433     public boolean setScoAllowed(boolean value) {
434         return setScoAllowedNative(value);
435     }
436 
437     /**
438      * Enable or disable in-band ringing for the current service level connection through sending
439      * +BSIR AT command
440      *
441      * @param value True to enable, False to disable
442      * @return True on success, False on failure
443      */
444     @VisibleForTesting
sendBsir(BluetoothDevice device, boolean value)445     public boolean sendBsir(BluetoothDevice device, boolean value) {
446         return sendBsirNative(value, Utils.getByteAddress(device));
447     }
448 
449     /**
450      * Set the current active headset device for SCO audio
451      * @param device current active SCO device
452      * @return true on success
453      */
454     @VisibleForTesting
setActiveDevice(BluetoothDevice device)455     public boolean setActiveDevice(BluetoothDevice device) {
456         return setActiveDeviceNative(Utils.getByteAddress(device));
457     }
458 
459     /* Native methods */
classInitNative()460     private static native void classInitNative();
461 
atResponseCodeNative(int responseCode, int errorCode, byte[] address)462     private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address);
463 
atResponseStringNative(String responseString, byte[] address)464     private native boolean atResponseStringNative(String responseString, byte[] address);
465 
initializeNative(int maxHfClients, boolean inbandRingingEnabled)466     private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled);
467 
cleanupNative()468     private native void cleanupNative();
469 
connectHfpNative(byte[] address)470     private native boolean connectHfpNative(byte[] address);
471 
disconnectHfpNative(byte[] address)472     private native boolean disconnectHfpNative(byte[] address);
473 
connectAudioNative(byte[] address)474     private native boolean connectAudioNative(byte[] address);
475 
disconnectAudioNative(byte[] address)476     private native boolean disconnectAudioNative(byte[] address);
477 
startVoiceRecognitionNative(byte[] address)478     private native boolean startVoiceRecognitionNative(byte[] address);
479 
stopVoiceRecognitionNative(byte[] address)480     private native boolean stopVoiceRecognitionNative(byte[] address);
481 
setVolumeNative(int volumeType, int volume, byte[] address)482     private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
483 
cindResponseNative(int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge, byte[] address)484     private native boolean cindResponseNative(int service, int numActive, int numHeld,
485             int callState, int signal, int roam, int batteryCharge, byte[] address);
486 
notifyDeviceStatusNative(int networkState, int serviceType, int signal, int batteryCharge, byte[] address)487     private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
488             int batteryCharge, byte[] address);
489 
clccResponseNative(int index, int dir, int status, int mode, boolean mpty, String number, int type, byte[] address)490     private native boolean clccResponseNative(int index, int dir, int status, int mode,
491             boolean mpty, String number, int type, byte[] address);
492 
copsResponseNative(String operatorName, byte[] address)493     private native boolean copsResponseNative(String operatorName, byte[] address);
494 
phoneStateChangeNative(int numActive, int numHeld, int callState, String number, int type, String name, byte[] address)495     private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
496             String number, int type, String name, byte[] address);
497 
setScoAllowedNative(boolean value)498     private native boolean setScoAllowedNative(boolean value);
499 
sendBsirNative(boolean value, byte[] address)500     private native boolean sendBsirNative(boolean value, byte[] address);
501 
setActiveDeviceNative(byte[] address)502     private native boolean setActiveDeviceNative(byte[] address);
503 }
504