• 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 
onSWB(int codec, byte[] address)153     private void onSWB(int codec, byte[] address) {
154         HeadsetStackEvent event =
155                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SWB, codec, getDevice(address));
156         sendMessageToService(event);
157     }
158 
onAtChld(int chld, byte[] address)159     private void onAtChld(int chld, byte[] address) {
160         HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CHLD, chld,
161                 getDevice(address));
162         sendMessageToService(event);
163     }
164 
onAtCnum(byte[] address)165     private void onAtCnum(byte[] address) {
166         HeadsetStackEvent event =
167                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST,
168                         getDevice(address));
169         sendMessageToService(event);
170     }
171 
onAtCind(byte[] address)172     private void onAtCind(byte[] address) {
173         HeadsetStackEvent event =
174                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CIND, getDevice(address));
175         sendMessageToService(event);
176     }
177 
onAtCops(byte[] address)178     private void onAtCops(byte[] address) {
179         HeadsetStackEvent event =
180                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_COPS, getDevice(address));
181         sendMessageToService(event);
182     }
183 
onAtClcc(byte[] address)184     private void onAtClcc(byte[] address) {
185         HeadsetStackEvent event =
186                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CLCC, getDevice(address));
187         sendMessageToService(event);
188     }
189 
onUnknownAt(String atString, byte[] address)190     private void onUnknownAt(String atString, byte[] address) {
191         HeadsetStackEvent event =
192                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT, atString,
193                         getDevice(address));
194         sendMessageToService(event);
195     }
196 
onKeyPressed(byte[] address)197     private void onKeyPressed(byte[] address) {
198         HeadsetStackEvent event =
199                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED, getDevice(address));
200         sendMessageToService(event);
201     }
202 
onATBind(String atString, byte[] address)203     private void onATBind(String atString, byte[] address) {
204         HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIND, atString,
205                 getDevice(address));
206         sendMessageToService(event);
207     }
208 
onATBiev(int indId, int indValue, byte[] address)209     private void onATBiev(int indId, int indValue, byte[] address) {
210         HeadsetStackEvent event =
211                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIEV, indId, indValue,
212                         getDevice(address));
213         sendMessageToService(event);
214     }
215 
onAtBia(boolean service, boolean roam, boolean signal, boolean battery, byte[] address)216     private void onAtBia(boolean service, boolean roam, boolean signal, boolean battery,
217             byte[] address) {
218         HeadsetAgIndicatorEnableState agIndicatorEnableState =
219                 new HeadsetAgIndicatorEnableState(service, roam, signal, battery);
220         HeadsetStackEvent event =
221                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIA, agIndicatorEnableState,
222                         getDevice(address));
223         sendMessageToService(event);
224     }
225 
226     // Native wrappers to help unit testing
227 
228     /**
229      * Initialize native stack
230      *
231      * @param maxHfClients maximum number of headset clients that can be connected simultaneously
232      * @param inbandRingingEnabled whether in-band ringing is enabled on this AG
233      */
234     @VisibleForTesting
init(int maxHfClients, boolean inbandRingingEnabled)235     public void init(int maxHfClients, boolean inbandRingingEnabled) {
236         initializeNative(maxHfClients, inbandRingingEnabled);
237     }
238 
239     /**
240      * Closes the interface
241      */
242     @VisibleForTesting
cleanup()243     public void cleanup() {
244         cleanupNative();
245     }
246 
247     /**
248      * ok/error response
249      *
250      * @param device target device
251      * @param responseCode 0 - ERROR, 1 - OK
252      * @param errorCode error code in case of ERROR
253      * @return True on success, False on failure
254      */
255     @VisibleForTesting
atResponseCode(BluetoothDevice device, int responseCode, int errorCode)256     public boolean atResponseCode(BluetoothDevice device, int responseCode, int errorCode) {
257         return atResponseCodeNative(responseCode, errorCode, getByteAddress(device));
258     }
259 
260     /**
261      * Pre-formatted AT response, typically in response to unknown AT cmd
262      *
263      * @param device target device
264      * @param responseString formatted AT response string
265      * @return True on success, False on failure
266      */
267     @VisibleForTesting
atResponseString(BluetoothDevice device, String responseString)268     public boolean atResponseString(BluetoothDevice device, String responseString) {
269         return atResponseStringNative(responseString, getByteAddress(device));
270     }
271 
272     /**
273      * Connect to headset
274      *
275      * @param device target headset
276      * @return True on success, False on failure
277      */
278     @VisibleForTesting
connectHfp(BluetoothDevice device)279     public boolean connectHfp(BluetoothDevice device) {
280         return connectHfpNative(getByteAddress(device));
281     }
282 
283     /**
284      * Disconnect from headset
285      *
286      * @param device target headset
287      * @return True on success, False on failure
288      */
289     @VisibleForTesting
disconnectHfp(BluetoothDevice device)290     public boolean disconnectHfp(BluetoothDevice device) {
291         return disconnectHfpNative(getByteAddress(device));
292     }
293 
294     /**
295      * Connect HFP audio (SCO) to headset
296      *
297      * @param device target headset
298      * @return True on success, False on failure
299      */
300     @VisibleForTesting
connectAudio(BluetoothDevice device)301     public boolean connectAudio(BluetoothDevice device) {
302         return connectAudioNative(getByteAddress(device));
303     }
304 
305     /**
306      * Disconnect HFP audio (SCO) from to headset
307      *
308      * @param device target headset
309      * @return True on success, False on failure
310      */
311     @VisibleForTesting
disconnectAudio(BluetoothDevice device)312     public boolean disconnectAudio(BluetoothDevice device) {
313         return disconnectAudioNative(getByteAddress(device));
314     }
315 
316     /**
317      * Checks whether the device support echo cancellation and/or noise reduction via the AT+BRSF
318      * bitmask
319      *
320      * @param device target headset
321      * @return true if the device support echo cancellation or noise reduction, false otherwise
322      */
isNoiseReductionSupported(BluetoothDevice device)323     public boolean isNoiseReductionSupported(BluetoothDevice device) {
324         return isNoiseReductionSupportedNative(getByteAddress(device));
325     }
326 
327     /**
328      * Checks whether the device supports voice recognition via the AT+BRSF bitmask
329      *
330      * @param device target headset
331      * @return true if the device supports voice recognition, false otherwise
332      */
isVoiceRecognitionSupported(BluetoothDevice device)333     public boolean isVoiceRecognitionSupported(BluetoothDevice device) {
334         return isVoiceRecognitionSupportedNative(getByteAddress(device));
335     }
336 
337     /**
338      * Start voice recognition
339      *
340      * @param device target headset
341      * @return True on success, False on failure
342      */
343     @VisibleForTesting
startVoiceRecognition(BluetoothDevice device)344     public boolean startVoiceRecognition(BluetoothDevice device) {
345         return startVoiceRecognitionNative(getByteAddress(device));
346     }
347 
348 
349     /**
350      * Stop voice recognition
351      *
352      * @param device target headset
353      * @return True on success, False on failure
354      */
355     @VisibleForTesting
stopVoiceRecognition(BluetoothDevice device)356     public boolean stopVoiceRecognition(BluetoothDevice device) {
357         return stopVoiceRecognitionNative(getByteAddress(device));
358     }
359 
360     /**
361      * Set HFP audio (SCO) volume
362      *
363      * @param device target headset
364      * @param volumeType type of volume
365      * @param volume value value
366      * @return True on success, False on failure
367      */
368     @VisibleForTesting
setVolume(BluetoothDevice device, int volumeType, int volume)369     public boolean setVolume(BluetoothDevice device, int volumeType, int volume) {
370         return setVolumeNative(volumeType, volume, getByteAddress(device));
371     }
372 
373     /**
374      * Response for CIND command
375      *
376      * @param device target device
377      * @param service service availability, 0 - no service, 1 - presence of service
378      * @param numActive number of active calls
379      * @param numHeld number of held calls
380      * @param callState overall call state [0-6]
381      * @param signal signal quality [0-5]
382      * @param roam roaming indicator, 0 - not roaming, 1 - roaming
383      * @param batteryCharge battery charge level [0-5]
384      * @return True on success, False on failure
385      */
386     @VisibleForTesting
cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge)387     public boolean cindResponse(BluetoothDevice device, int service, int numActive, int numHeld,
388             int callState, int signal, int roam, int batteryCharge) {
389         return cindResponseNative(service, numActive, numHeld, callState, signal, roam,
390                 batteryCharge, getByteAddress(device));
391     }
392 
393     /**
394      * Combined device status change notification
395      *
396      * @param device target device
397      * @param deviceState device status object
398      * @return True on success, False on failure
399      */
400     @VisibleForTesting
notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState)401     public boolean notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState) {
402         return notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam,
403                 deviceState.mSignal, deviceState.mBatteryCharge, getByteAddress(device));
404     }
405 
406     /**
407      * Response for CLCC command. Can be iteratively called for each call index. Call index of 0
408      * will be treated as NULL termination (Completes response)
409      *
410      * @param device target device
411      * @param index index of the call given by the sequence of setting up or receiving the calls
412      * as seen by the served subscriber. Calls hold their number until they are released. New
413      * calls take the lowest available number.
414      * @param dir direction of the call, 0 (outgoing), 1 (incoming)
415      * @param status 0 = Active, 1 = Held, 2 = Dialing (outgoing calls only), 3 = Alerting
416      * (outgoing calls only), 4 = Incoming (incoming calls only), 5 = Waiting (incoming calls
417      * only), 6 = Call held by Response and Hold
418      * @param mode 0 (Voice), 1 (Data), 2 (FAX)
419      * @param mpty 0 - this call is NOT a member of a multi-party (conference) call, 1 - this
420      * call IS a member of a multi-party (conference) call
421      * @param number optional
422      * @param type optional
423      * @return True on success, False on failure
424      */
425     @VisibleForTesting
clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, boolean mpty, String number, int type)426     public boolean clccResponse(BluetoothDevice device, int index, int dir, int status, int mode,
427             boolean mpty, String number, int type) {
428         return clccResponseNative(index, dir, status, mode, mpty, number, type,
429                 getByteAddress(device));
430     }
431 
432     /**
433      * Response for COPS command
434      *
435      * @param device target device
436      * @param operatorName operator name
437      * @return True on success, False on failure
438      */
439     @VisibleForTesting
copsResponse(BluetoothDevice device, String operatorName)440     public boolean copsResponse(BluetoothDevice device, String operatorName) {
441         return copsResponseNative(operatorName, getByteAddress(device));
442     }
443 
444     /**
445      *  Notify of a call state change
446      *  Each update notifies
447      *    1. Number of active/held/ringing calls
448      *    2. call_state: This denotes the state change that triggered this msg
449      *                   This will take one of the values from BtHfCallState
450      *    3. number & type: valid only for incoming & waiting call
451      *
452      * @param device target device for this update
453      * @param callState callstate structure
454      * @return True on success, False on failure
455      */
456     @VisibleForTesting
phoneStateChange(BluetoothDevice device, HeadsetCallState callState)457     public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) {
458         return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
459                 callState.mCallState, callState.mNumber, callState.mType, callState.mName,
460                 getByteAddress(device));
461     }
462 
463     /**
464      * Set whether we will initiate SCO or not
465      *
466      * @param value True to enable, False to disable
467      * @return True on success, False on failure
468      */
469     @VisibleForTesting
setScoAllowed(boolean value)470     public boolean setScoAllowed(boolean value) {
471         return setScoAllowedNative(value);
472     }
473 
474     /**
475      * Enable or disable in-band ringing for the current service level connection through sending
476      * +BSIR AT command
477      *
478      * @param value True to enable, False to disable
479      * @return True on success, False on failure
480      */
481     @VisibleForTesting
sendBsir(BluetoothDevice device, boolean value)482     public boolean sendBsir(BluetoothDevice device, boolean value) {
483         return sendBsirNative(value, getByteAddress(device));
484     }
485 
486     /**
487      * Set the current active headset device for SCO audio
488      * @param device current active SCO device
489      * @return true on success
490      */
491     @VisibleForTesting
setActiveDevice(BluetoothDevice device)492     public boolean setActiveDevice(BluetoothDevice device) {
493         return setActiveDeviceNative(getByteAddress(device));
494     }
495 
496     /* Native methods */
classInitNative()497     private static native void classInitNative();
498 
atResponseCodeNative(int responseCode, int errorCode, byte[] address)499     private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address);
500 
atResponseStringNative(String responseString, byte[] address)501     private native boolean atResponseStringNative(String responseString, byte[] address);
502 
initializeNative(int maxHfClients, boolean inbandRingingEnabled)503     private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled);
504 
cleanupNative()505     private native void cleanupNative();
506 
connectHfpNative(byte[] address)507     private native boolean connectHfpNative(byte[] address);
508 
disconnectHfpNative(byte[] address)509     private native boolean disconnectHfpNative(byte[] address);
510 
connectAudioNative(byte[] address)511     private native boolean connectAudioNative(byte[] address);
512 
disconnectAudioNative(byte[] address)513     private native boolean disconnectAudioNative(byte[] address);
514 
isNoiseReductionSupportedNative(byte[] address)515     private native boolean isNoiseReductionSupportedNative(byte[] address);
516 
isVoiceRecognitionSupportedNative(byte[] address)517     private native boolean isVoiceRecognitionSupportedNative(byte[] address);
518 
startVoiceRecognitionNative(byte[] address)519     private native boolean startVoiceRecognitionNative(byte[] address);
520 
stopVoiceRecognitionNative(byte[] address)521     private native boolean stopVoiceRecognitionNative(byte[] address);
522 
setVolumeNative(int volumeType, int volume, byte[] address)523     private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
524 
cindResponseNative(int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge, byte[] address)525     private native boolean cindResponseNative(int service, int numActive, int numHeld,
526             int callState, int signal, int roam, int batteryCharge, byte[] address);
527 
notifyDeviceStatusNative(int networkState, int serviceType, int signal, int batteryCharge, byte[] address)528     private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
529             int batteryCharge, byte[] address);
530 
clccResponseNative(int index, int dir, int status, int mode, boolean mpty, String number, int type, byte[] address)531     private native boolean clccResponseNative(int index, int dir, int status, int mode,
532             boolean mpty, String number, int type, byte[] address);
533 
copsResponseNative(String operatorName, byte[] address)534     private native boolean copsResponseNative(String operatorName, byte[] address);
535 
phoneStateChangeNative(int numActive, int numHeld, int callState, String number, int type, String name, byte[] address)536     private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
537             String number, int type, String name, byte[] address);
538 
setScoAllowedNative(boolean value)539     private native boolean setScoAllowedNative(boolean value);
540 
sendBsirNative(boolean value, byte[] address)541     private native boolean sendBsirNative(boolean value, byte[] address);
542 
setActiveDeviceNative(byte[] address)543     private native boolean setActiveDeviceNative(byte[] address);
544 }
545