1 /*
2  * Copyright (c) 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 /*
18  * Defines the native inteface that is used by state machine/service to either or receive messages
19  * from the native stack. This file is registered for the native methods in corresponding CPP file.
20  */
21 package com.android.bluetooth.hfpclient;
22 
23 import android.bluetooth.BluetoothDevice;
24 import android.util.Log;
25 
26 import com.android.bluetooth.Utils;
27 import com.android.bluetooth.btservice.AdapterService;
28 import com.android.bluetooth.flags.Flags;
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 import java.util.Objects;
33 
34 /**
35  * Defines native calls that are used by state machine/service to either send or receive messages
36  * to/from the native stack. This file is registered for the native methods in corresponding CPP
37  * file.
38  */
39 public class NativeInterface {
40     private static final String TAG = NativeInterface.class.getSimpleName();
41 
42     private AdapterService mAdapterService;
43 
44     @GuardedBy("INSTANCE_LOCK")
45     private static NativeInterface sInstance;
46 
47     private static final Object INSTANCE_LOCK = new Object();
48 
NativeInterface()49     private NativeInterface() {
50         mAdapterService =
51                 Objects.requireNonNull(
52                         AdapterService.getAdapterService(),
53                         "AdapterService cannot be null when NativeInterface init");
54     }
55 
56     /**
57      * This class is a singleton because native library should only be loaded once
58      *
59      * @return default instance
60      */
getInstance()61     public static NativeInterface getInstance() {
62         synchronized (INSTANCE_LOCK) {
63             if (sInstance == null) {
64                 sInstance = new NativeInterface();
65             }
66             return sInstance;
67         }
68     }
69 
70     /** Set singleton instance. */
71     @VisibleForTesting
setInstance(NativeInterface instance)72     public static void setInstance(NativeInterface instance) {
73         synchronized (INSTANCE_LOCK) {
74             sInstance = instance;
75         }
76     }
77 
78     // Native wrappers to help unit testing
79     /** Initialize native stack */
80     @VisibleForTesting
initialize()81     public void initialize() {
82         initializeNative();
83     }
84 
85     /** Close and clean up native stack */
86     @VisibleForTesting
cleanup()87     public void cleanup() {
88         cleanupNative();
89     }
90 
91     /**
92      * Connect to the specified paired device
93      *
94      * @param device target device
95      * @return True on success, False on failure
96      */
97     @VisibleForTesting
connect(BluetoothDevice device)98     public boolean connect(BluetoothDevice device) {
99         return connectNative(getByteAddress(device));
100     }
101 
102     /**
103      * Disconnect from the specified paired device
104      *
105      * @param device target device
106      * @return True on success, False on failure
107      */
108     @VisibleForTesting
disconnect(BluetoothDevice device)109     public boolean disconnect(BluetoothDevice device) {
110         return disconnectNative(getByteAddress(device));
111     }
112 
113     /**
114      * Initiate audio connection to the specified paired device
115      *
116      * @param device target device
117      * @return True on success, False on failure
118      */
119     @VisibleForTesting
connectAudio(BluetoothDevice device)120     public boolean connectAudio(BluetoothDevice device) {
121         return connectAudioNative(getByteAddress(device));
122     }
123 
124     /**
125      * Close audio connection from the specified paired device
126      *
127      * @param device target device
128      * @return True on success, False on failure
129      */
disconnectAudio(BluetoothDevice device)130     public boolean disconnectAudio(BluetoothDevice device) {
131         return disconnectAudioNative(getByteAddress(device));
132     }
133 
134     /**
135      * Initiate voice recognition to the specified paired device
136      *
137      * @param device target device
138      * @return True on success, False on failure
139      */
140     @VisibleForTesting
startVoiceRecognition(BluetoothDevice device)141     public boolean startVoiceRecognition(BluetoothDevice device) {
142         return startVoiceRecognitionNative(getByteAddress(device));
143     }
144 
145     /**
146      * Close voice recognition to the specified paired device
147      *
148      * @param device target device
149      * @return True on success, False on failure
150      */
151     @VisibleForTesting
stopVoiceRecognition(BluetoothDevice device)152     public boolean stopVoiceRecognition(BluetoothDevice device) {
153         return stopVoiceRecognitionNative(getByteAddress(device));
154     }
155 
156     /**
157      * Set volume to the specified paired device
158      *
159      * @param device target device
160      * @param volumeType type of volume as in HeadsetClientHalConstants.VOLUME_TYPE_xxxx
161      * @param volume volume level
162      * @return True on success, False on failure
163      */
164     @VisibleForTesting
setVolume(BluetoothDevice device, int volumeType, int volume)165     public boolean setVolume(BluetoothDevice device, int volumeType, int volume) {
166         return setVolumeNative(getByteAddress(device), volumeType, volume);
167     }
168 
169     /**
170      * dial number from the specified paired device
171      *
172      * @param device target device
173      * @param number phone number to be dialed
174      * @return True on success, False on failure
175      */
176     @VisibleForTesting
dial(BluetoothDevice device, String number)177     public boolean dial(BluetoothDevice device, String number) {
178         return dialNative(getByteAddress(device), number);
179     }
180 
181     /**
182      * Memory dialing from the specified paired device
183      *
184      * @param device target device
185      * @param location memory location
186      * @return True on success, False on failure
187      */
188     @VisibleForTesting
dialMemory(BluetoothDevice device, int location)189     public boolean dialMemory(BluetoothDevice device, int location) {
190         return dialMemoryNative(getByteAddress(device), location);
191     }
192 
193     /**
194      * Apply action to call
195      *
196      * @param device target device
197      * @param action action (e.g. hold, terminate etc)
198      * @param index call index
199      * @return True on success, False on failure
200      */
201     @VisibleForTesting
handleCallAction(BluetoothDevice device, int action, int index)202     public boolean handleCallAction(BluetoothDevice device, int action, int index) {
203         return handleCallActionNative(getByteAddress(device), action, index);
204     }
205 
206     /**
207      * Query current call status from the specified paired device
208      *
209      * @param device target device
210      * @return True on success, False on failure
211      */
212     @VisibleForTesting
queryCurrentCalls(BluetoothDevice device)213     public boolean queryCurrentCalls(BluetoothDevice device) {
214         return queryCurrentCallsNative(getByteAddress(device));
215     }
216 
217     /**
218      * Query operator name from the specified paired device
219      *
220      * @param device target device
221      * @return True on success, False on failure
222      */
223     @VisibleForTesting
queryCurrentOperatorName(BluetoothDevice device)224     public boolean queryCurrentOperatorName(BluetoothDevice device) {
225         return queryCurrentOperatorNameNative(getByteAddress(device));
226     }
227 
228     /**
229      * Retrieve subscriber number from the specified paired device
230      *
231      * @param device target device
232      * @return True on success, False on failure
233      */
234     @VisibleForTesting
retrieveSubscriberInfo(BluetoothDevice device)235     public boolean retrieveSubscriberInfo(BluetoothDevice device) {
236         return retrieveSubscriberInfoNative(getByteAddress(device));
237     }
238 
239     /**
240      * Transmit DTMF code
241      *
242      * @param device target device
243      * @param code DTMF code
244      * @return True on success, False on failure
245      */
246     @VisibleForTesting
sendDtmf(BluetoothDevice device, byte code)247     public boolean sendDtmf(BluetoothDevice device, byte code) {
248         return sendDtmfNative(getByteAddress(device), code);
249     }
250 
251     /**
252      * Request last voice tag
253      *
254      * @param device target device
255      * @return True on success, False on failure
256      */
257     @VisibleForTesting
requestLastVoiceTagNumber(BluetoothDevice device)258     public boolean requestLastVoiceTagNumber(BluetoothDevice device) {
259         return requestLastVoiceTagNumberNative(getByteAddress(device));
260     }
261 
262     /**
263      * Send an AT command
264      *
265      * @param device target device
266      * @param atCmd command code
267      * @param val1 command specific argurment1
268      * @param val2 command specific argurment2
269      * @param arg other command specific argurments
270      * @return True on success, False on failure
271      */
272     @VisibleForTesting
sendATCmd(BluetoothDevice device, int atCmd, int val1, int val2, String arg)273     public boolean sendATCmd(BluetoothDevice device, int atCmd, int val1, int val2, String arg) {
274         return sendATCmdNative(getByteAddress(device), atCmd, val1, val2, arg);
275     }
276 
277     /**
278      * Set call audio policy to the specified paired device
279      *
280      * @param cmd Android specific command string
281      * @return True on success, False on failure
282      */
283     @VisibleForTesting
sendAndroidAt(BluetoothDevice device, String cmd)284     public boolean sendAndroidAt(BluetoothDevice device, String cmd) {
285         if (device == null) {
286             Log.w(TAG, "Don't need to send " + cmd + " because no remote device");
287             return false;
288         }
289         return sendAndroidAtNative(getByteAddress(device), cmd);
290     }
291 
292     /**********************************************************************************************/
293     /******************************************* native *******************************************/
294     /**********************************************************************************************/
295 
initializeNative()296     private native void initializeNative();
297 
cleanupNative()298     private native void cleanupNative();
299 
connectNative(byte[] address)300     private static native boolean connectNative(byte[] address);
301 
disconnectNative(byte[] address)302     private static native boolean disconnectNative(byte[] address);
303 
connectAudioNative(byte[] address)304     private static native boolean connectAudioNative(byte[] address);
305 
disconnectAudioNative(byte[] address)306     private static native boolean disconnectAudioNative(byte[] address);
307 
startVoiceRecognitionNative(byte[] address)308     private static native boolean startVoiceRecognitionNative(byte[] address);
309 
stopVoiceRecognitionNative(byte[] address)310     private static native boolean stopVoiceRecognitionNative(byte[] address);
311 
setVolumeNative(byte[] address, int volumeType, int volume)312     private static native boolean setVolumeNative(byte[] address, int volumeType, int volume);
313 
dialNative(byte[] address, String number)314     private static native boolean dialNative(byte[] address, String number);
315 
dialMemoryNative(byte[] address, int location)316     private static native boolean dialMemoryNative(byte[] address, int location);
317 
handleCallActionNative(byte[] address, int action, int index)318     private static native boolean handleCallActionNative(byte[] address, int action, int index);
319 
queryCurrentCallsNative(byte[] address)320     private static native boolean queryCurrentCallsNative(byte[] address);
321 
queryCurrentOperatorNameNative(byte[] address)322     private static native boolean queryCurrentOperatorNameNative(byte[] address);
323 
retrieveSubscriberInfoNative(byte[] address)324     private static native boolean retrieveSubscriberInfoNative(byte[] address);
325 
sendDtmfNative(byte[] address, byte code)326     private static native boolean sendDtmfNative(byte[] address, byte code);
327 
requestLastVoiceTagNumberNative(byte[] address)328     private static native boolean requestLastVoiceTagNumberNative(byte[] address);
329 
sendATCmdNative( byte[] address, int atCmd, int val1, int val2, String arg)330     private static native boolean sendATCmdNative(
331             byte[] address, int atCmd, int val1, int val2, String arg);
332 
sendAndroidAtNative(byte[] address, String cmd)333     private static native boolean sendAndroidAtNative(byte[] address, String cmd);
334 
getDevice(byte[] address)335     private BluetoothDevice getDevice(byte[] address) {
336         return mAdapterService.getDeviceFromByte(address);
337     }
338 
getByteAddress(BluetoothDevice device)339     private byte[] getByteAddress(BluetoothDevice device) {
340         if (Flags.identityAddressNullIfUnknown()) {
341             return Utils.getByteBrEdrAddress(device);
342         } else {
343             return mAdapterService.getByteIdentityAddress(device);
344         }
345     }
346 
347     // Callbacks from the native back into the java framework. All callbacks are routed via the
348     // Service which will disambiguate which state machine the message should be routed through.
349     @VisibleForTesting
onConnectionStateChanged(int state, int peerFeat, int chldFeat, byte[] address)350     void onConnectionStateChanged(int state, int peerFeat, int chldFeat, byte[] address) {
351         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
352         event.valueInt = state;
353         event.valueInt2 = peerFeat;
354         event.valueInt3 = chldFeat;
355         event.device = getDevice(address);
356         // BluetoothAdapter.getDefaultAdapter().getRemoteDevice(Utils.getAddressStringFromByte
357         // (address));
358         Log.d(TAG, "Device addr " + event.device + " State " + state);
359         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
360         if (service != null) {
361             service.messageFromNative(event);
362         } else {
363             Log.w(TAG, "Ignoring message because service not available: " + event);
364         }
365     }
366 
367     @VisibleForTesting
onAudioStateChanged(int state, byte[] address)368     void onAudioStateChanged(int state, byte[] address) {
369         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED);
370         event.valueInt = state;
371         event.device = getDevice(address);
372         Log.d(TAG, "onAudioStateChanged: event " + event);
373         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
374         if (service != null) {
375             service.messageFromNative(event);
376         } else {
377             Log.w(
378                     TAG,
379                     "onAudioStateChanged: Ignoring message because service not available: "
380                             + event);
381         }
382     }
383 
384     @VisibleForTesting
onVrStateChanged(int state, byte[] address)385     void onVrStateChanged(int state, byte[] address) {
386         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_VR_STATE_CHANGED);
387         event.valueInt = state;
388         event.device = getDevice(address);
389         Log.d(TAG, "onVrStateChanged: event " + event);
390 
391         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
392         if (service != null) {
393             service.messageFromNative(event);
394         } else {
395             Log.w(
396                     TAG,
397                     "onVrStateChanged: Ignoring message because service not available: " + event);
398         }
399     }
400 
401     @VisibleForTesting
onNetworkState(int state, byte[] address)402     void onNetworkState(int state, byte[] address) {
403         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_NETWORK_STATE);
404         event.valueInt = state;
405         event.device = getDevice(address);
406         Log.d(TAG, "onNetworkStateChanged: event " + event);
407 
408         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
409         if (service != null) {
410             service.messageFromNative(event);
411         } else {
412             Log.w(
413                     TAG,
414                     "onNetworkStateChanged: Ignoring message because service not available: "
415                             + event);
416         }
417     }
418 
419     @VisibleForTesting
onNetworkRoaming(int state, byte[] address)420     void onNetworkRoaming(int state, byte[] address) {
421         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_ROAMING_STATE);
422         event.valueInt = state;
423         event.device = getDevice(address);
424         Log.d(TAG, "onNetworkRoaming: incoming: " + event);
425         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
426         if (service != null) {
427             service.messageFromNative(event);
428         } else {
429             Log.w(
430                     TAG,
431                     "onNetworkRoaming: Ignoring message because service not available: " + event);
432         }
433     }
434 
435     @VisibleForTesting
onNetworkSignal(int signal, byte[] address)436     void onNetworkSignal(int signal, byte[] address) {
437         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_NETWORK_SIGNAL);
438         event.valueInt = signal;
439         event.device = getDevice(address);
440         Log.d(TAG, "onNetworkSignal: event " + event);
441         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
442         if (service != null) {
443             service.messageFromNative(event);
444         } else {
445             Log.w(TAG, "onNetworkSignal: Ignoring message because service not available: " + event);
446         }
447     }
448 
449     @VisibleForTesting
onBatteryLevel(int level, byte[] address)450     void onBatteryLevel(int level, byte[] address) {
451         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_BATTERY_LEVEL);
452         event.valueInt = level;
453         event.device = getDevice(address);
454         Log.d(TAG, "onBatteryLevel: event " + event);
455         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
456         if (service != null) {
457             service.messageFromNative(event);
458         } else {
459             Log.w(TAG, "onBatteryLevel: Ignoring message because service not available: " + event);
460         }
461     }
462 
463     @VisibleForTesting
onCurrentOperator(String name, byte[] address)464     void onCurrentOperator(String name, byte[] address) {
465         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_OPERATOR_NAME);
466         event.valueString = name;
467         event.device = getDevice(address);
468         Log.d(TAG, "onCurrentOperator: event " + event);
469         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
470         if (service != null) {
471             service.messageFromNative(event);
472         } else {
473             Log.w(
474                     TAG,
475                     "onCurrentOperator: Ignoring message because service not available: " + event);
476         }
477     }
478 
479     @VisibleForTesting
onCall(int call, byte[] address)480     void onCall(int call, byte[] address) {
481         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALL);
482         event.valueInt = call;
483         event.device = getDevice(address);
484         Log.d(TAG, "onCall: event " + event);
485         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
486         if (service != null) {
487             service.messageFromNative(event);
488         } else {
489             Log.w(TAG, "onCall: Ignoring message because service not available: " + event);
490         }
491     }
492 
493     /**
494      * CIEV (Call indicators) notifying if call(s) are getting set up.
495      *
496      * <p>Values include: 0 - No current call is in setup 1 - Incoming call process ongoing 2 -
497      * Outgoing call process ongoing 3 - Remote party being alerted for outgoing call
498      */
499     @VisibleForTesting
onCallSetup(int callsetup, byte[] address)500     void onCallSetup(int callsetup, byte[] address) {
501         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALLSETUP);
502         event.valueInt = callsetup;
503         event.device = getDevice(address);
504         Log.d(TAG, "onCallSetup: device" + event.device);
505         Log.d(TAG, "onCallSetup: event " + event);
506         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
507         if (service != null) {
508             service.messageFromNative(event);
509         } else {
510             Log.w(TAG, "onCallSetup: Ignoring message because service not available: " + event);
511         }
512     }
513 
514     /**
515      * CIEV (Call indicators) notifying call held states.
516      *
517      * <p>Values include: 0 - No calls held 1 - Call is placed on hold or active/held calls wapped
518      * (The AG has both an ACTIVE and HELD call) 2 - Call on hold, no active call
519      */
520     @VisibleForTesting
onCallHeld(int callheld, byte[] address)521     void onCallHeld(int callheld, byte[] address) {
522         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALLHELD);
523         event.valueInt = callheld;
524         event.device = getDevice(address);
525         Log.d(TAG, "onCallHeld: event " + event);
526         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
527         if (service != null) {
528             service.messageFromNative(event);
529         } else {
530             Log.w(TAG, "onCallHeld: Ignoring message because service not available: " + event);
531         }
532     }
533 
534     @VisibleForTesting
onRespAndHold(int respAndHold, byte[] address)535     void onRespAndHold(int respAndHold, byte[] address) {
536         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_RESP_AND_HOLD);
537         event.valueInt = respAndHold;
538         event.device = getDevice(address);
539         Log.d(TAG, "onRespAndHold: event " + event);
540         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
541         if (service != null) {
542             service.messageFromNative(event);
543         } else {
544             Log.w(TAG, "onRespAndHold: Ignoring message because service not available: " + event);
545         }
546     }
547 
548     @VisibleForTesting
onClip(String number, byte[] address)549     void onClip(String number, byte[] address) {
550         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CLIP);
551         event.valueString = number;
552         event.device = getDevice(address);
553         Log.d(TAG, "onClip: event " + event);
554         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
555         if (service != null) {
556             service.messageFromNative(event);
557         } else {
558             Log.w(TAG, "onClip: Ignoring message because service not available: " + event);
559         }
560     }
561 
562     @VisibleForTesting
onCallWaiting(String number, byte[] address)563     void onCallWaiting(String number, byte[] address) {
564         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALL_WAITING);
565         event.valueString = number;
566         event.device = getDevice(address);
567         Log.d(TAG, "onCallWaiting: event " + event);
568         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
569         if (service != null) {
570             service.messageFromNative(event);
571         } else {
572             Log.w(TAG, "onCallWaiting: Ignoring message because service not available: " + event);
573         }
574     }
575 
576     @VisibleForTesting
onCurrentCalls(int index, int dir, int state, int mparty, String number, byte[] address)577     void onCurrentCalls(int index, int dir, int state, int mparty, String number, byte[] address) {
578         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CURRENT_CALLS);
579         event.valueInt = index;
580         event.valueInt2 = dir;
581         event.valueInt3 = state;
582         event.valueInt4 = mparty;
583         event.valueString = number;
584         event.device = getDevice(address);
585         Log.d(TAG, "onCurrentCalls: event " + event);
586         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
587         if (service != null) {
588             service.messageFromNative(event);
589         } else {
590             Log.w(TAG, "onCurrentCalls: Ignoring message because service not available: " + event);
591         }
592     }
593 
594     @VisibleForTesting
onVolumeChange(int type, int volume, byte[] address)595     void onVolumeChange(int type, int volume, byte[] address) {
596         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_VOLUME_CHANGED);
597         event.valueInt = type;
598         event.valueInt2 = volume;
599         event.device = getDevice(address);
600         Log.d(TAG, "onVolumeChange: event " + event);
601         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
602         if (service != null) {
603             service.messageFromNative(event);
604         } else {
605             Log.w(TAG, "onVolumeChange: Ignoring message because service not available: " + event);
606         }
607     }
608 
609     @VisibleForTesting
onCmdResult(int type, int cme, byte[] address)610     void onCmdResult(int type, int cme, byte[] address) {
611         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT);
612         event.valueInt = type;
613         event.valueInt2 = cme;
614         event.device = getDevice(address);
615         Log.d(TAG, "onCmdResult: event " + event);
616         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
617         if (service != null) {
618             service.messageFromNative(event);
619         } else {
620             Log.w(TAG, "onCmdResult: Ignoring message because service not available: " + event);
621         }
622     }
623 
624     @VisibleForTesting
onSubscriberInfo(String number, int type, byte[] address)625     void onSubscriberInfo(String number, int type, byte[] address) {
626         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_SUBSCRIBER_INFO);
627         event.valueInt = type;
628         event.valueString = number;
629         event.device = getDevice(address);
630         Log.d(TAG, "onSubscriberInfo: event " + event);
631         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
632         if (service != null) {
633             service.messageFromNative(event);
634         } else {
635             Log.w(
636                     TAG,
637                     "onSubscriberInfo: Ignoring message because service not available: " + event);
638         }
639     }
640 
641     @VisibleForTesting
onInBandRing(int inBand, byte[] address)642     void onInBandRing(int inBand, byte[] address) {
643         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_IN_BAND_RINGTONE);
644         event.valueInt = inBand;
645         event.device = getDevice(address);
646         Log.d(TAG, "onInBandRing: event " + event);
647         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
648         if (service != null) {
649             service.messageFromNative(event);
650         } else {
651             Log.w(TAG, "onInBandRing: Ignoring message because service not available: " + event);
652         }
653     }
654 
655     @VisibleForTesting
onLastVoiceTagNumber(String number, byte[] address)656     void onLastVoiceTagNumber(String number, byte[] address) {
657         Log.w(TAG, "onLastVoiceTagNumber not supported");
658     }
659 
660     @VisibleForTesting
onRingIndication(byte[] address)661     void onRingIndication(byte[] address) {
662         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_RING_INDICATION);
663         event.device = getDevice(address);
664         Log.d(TAG, "onRingIndication: event " + event);
665         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
666         if (service != null) {
667             service.messageFromNative(event);
668         } else {
669             Log.w(
670                     TAG,
671                     "onRingIndication: Ignoring message because service not available: " + event);
672         }
673     }
674 
675     @VisibleForTesting
onUnknownEvent(String eventString, byte[] address)676     void onUnknownEvent(String eventString, byte[] address) {
677         StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT);
678         event.device = getDevice(address);
679         event.valueString = eventString;
680         Log.d(TAG, "onUnknownEvent: event " + event);
681         HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
682         if (service != null) {
683             service.messageFromNative(event);
684         } else {
685             Log.w(TAG, "onUnknowEvent: Ignoring message because service not available: " + event);
686         }
687     }
688 }
689