1 /*
2  * Copyright 2020 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /*
19  * Defines the native interface that is used by state machine/service to
20  * send or receive messages from the native stack. This file is registered
21  * for the native methods in the corresponding JNI C++ file.
22  */
23 package com.android.bluetooth.le_audio;
24 
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.BluetoothDevice;
27 import android.bluetooth.BluetoothLeAudioCodecConfig;
28 import android.util.Log;
29 
30 import com.android.bluetooth.Utils;
31 import com.android.internal.annotations.GuardedBy;
32 import com.android.internal.annotations.VisibleForTesting;
33 
34 import java.util.Arrays;
35 
36 /** LeAudio Native Interface to/from JNI. */
37 public class LeAudioNativeInterface {
38     private static final String TAG = LeAudioNativeInterface.class.getSimpleName();
39 
40     private BluetoothAdapter mAdapter;
41 
42     @GuardedBy("INSTANCE_LOCK")
43     private static LeAudioNativeInterface sInstance;
44 
45     private static final Object INSTANCE_LOCK = new Object();
46 
LeAudioNativeInterface()47     private LeAudioNativeInterface() {
48         mAdapter = BluetoothAdapter.getDefaultAdapter();
49         if (mAdapter == null) {
50             Log.wtf(TAG, "No Bluetooth Adapter Available");
51         }
52     }
53 
54     /** Get singleton instance. */
getInstance()55     public static LeAudioNativeInterface getInstance() {
56         synchronized (INSTANCE_LOCK) {
57             if (sInstance == null) {
58                 sInstance = new LeAudioNativeInterface();
59             }
60             return sInstance;
61         }
62     }
63 
64     /** Set singleton instance. */
65     @VisibleForTesting
setInstance(LeAudioNativeInterface instance)66     public static void setInstance(LeAudioNativeInterface instance) {
67         synchronized (INSTANCE_LOCK) {
68             sInstance = instance;
69         }
70     }
71 
getByteAddress(BluetoothDevice device)72     private byte[] getByteAddress(BluetoothDevice device) {
73         if (device == null) {
74             return Utils.getBytesFromAddress("00:00:00:00:00:00");
75         }
76         return Utils.getBytesFromAddress(device.getAddress());
77     }
78 
sendMessageToService(LeAudioStackEvent event)79     private void sendMessageToService(LeAudioStackEvent event) {
80         LeAudioService service = LeAudioService.getLeAudioService();
81         if (service != null) {
82             service.messageFromNative(event);
83         } else {
84             Log.e(TAG, "Event ignored, service not available: " + event);
85         }
86     }
87 
getDevice(byte[] address)88     private BluetoothDevice getDevice(byte[] address) {
89         return mAdapter.getRemoteDevice(address);
90     }
91 
92     // Callbacks from the native stack back into the Java framework.
93     // All callbacks are routed via the Service which will disambiguate which
94     // state machine the message should be routed to.
onInitialized()95     private void onInitialized() {
96         LeAudioStackEvent event =
97                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
98 
99         Log.d(TAG, "onInitialized: " + event);
100         sendMessageToService(event);
101     }
102 
103     @VisibleForTesting
onConnectionStateChanged(int state, byte[] address)104     void onConnectionStateChanged(int state, byte[] address) {
105         LeAudioStackEvent event =
106                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
107         event.device = getDevice(address);
108         event.valueInt1 = state;
109 
110         Log.d(TAG, "onConnectionStateChanged: " + event);
111         sendMessageToService(event);
112     }
113 
114     @VisibleForTesting
onGroupStatus(int groupId, int groupStatus)115     void onGroupStatus(int groupId, int groupStatus) {
116         LeAudioStackEvent event =
117                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
118         event.valueInt1 = groupId;
119         event.valueInt2 = groupStatus;
120 
121         Log.d(TAG, "onGroupStatus: " + event);
122         sendMessageToService(event);
123     }
124 
125     @VisibleForTesting
onGroupNodeStatus(byte[] address, int groupId, int nodeStatus)126     void onGroupNodeStatus(byte[] address, int groupId, int nodeStatus) {
127         LeAudioStackEvent event =
128                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
129         event.valueInt1 = groupId;
130         event.valueInt2 = nodeStatus;
131         event.device = getDevice(address);
132 
133         Log.d(TAG, "onGroupNodeStatus: " + event);
134         sendMessageToService(event);
135     }
136 
137     @VisibleForTesting
onAudioConf( int direction, int groupId, int sinkAudioLocation, int sourceAudioLocation, int availableContexts)138     void onAudioConf(
139             int direction,
140             int groupId,
141             int sinkAudioLocation,
142             int sourceAudioLocation,
143             int availableContexts) {
144         LeAudioStackEvent event =
145                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
146         event.valueInt1 = direction;
147         event.valueInt2 = groupId;
148         event.valueInt3 = sinkAudioLocation;
149         event.valueInt4 = sourceAudioLocation;
150         event.valueInt5 = availableContexts;
151 
152         Log.d(TAG, "onAudioConf: " + event);
153         sendMessageToService(event);
154     }
155 
156     @VisibleForTesting
onSinkAudioLocationAvailable(byte[] address, int sinkAudioLocation)157     void onSinkAudioLocationAvailable(byte[] address, int sinkAudioLocation) {
158         LeAudioStackEvent event =
159                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE);
160         event.device = getDevice(address);
161         event.valueInt1 = sinkAudioLocation;
162 
163         Log.d(TAG, "onSinkAudioLocationAvailable: " + event);
164         sendMessageToService(event);
165     }
166 
167     @VisibleForTesting
onAudioLocalCodecCapabilities( BluetoothLeAudioCodecConfig[] localInputCodecCapabilities, BluetoothLeAudioCodecConfig[] localOutputCodecCapabilities)168     void onAudioLocalCodecCapabilities(
169             BluetoothLeAudioCodecConfig[] localInputCodecCapabilities,
170             BluetoothLeAudioCodecConfig[] localOutputCodecCapabilities) {
171         LeAudioStackEvent event =
172                 new LeAudioStackEvent(
173                         LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED);
174 
175         event.valueCodecList1 = Arrays.asList(localInputCodecCapabilities);
176         event.valueCodecList2 = Arrays.asList(localOutputCodecCapabilities);
177 
178         Log.d(TAG, "onAudioLocalCodecCapabilities: " + event);
179         sendMessageToService(event);
180     }
181 
182     @VisibleForTesting
onAudioGroupCurrentCodecConf( int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig)183     void onAudioGroupCurrentCodecConf(
184             int groupId,
185             BluetoothLeAudioCodecConfig inputCodecConfig,
186             BluetoothLeAudioCodecConfig outputCodecConfig) {
187         LeAudioStackEvent event =
188                 new LeAudioStackEvent(
189                         LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED);
190 
191         event.valueInt1 = groupId;
192         event.valueCodec1 = inputCodecConfig;
193         event.valueCodec2 = outputCodecConfig;
194 
195         Log.d(TAG, "onAudioGroupCurrentCodecConf: " + event);
196         sendMessageToService(event);
197     }
198 
199     @VisibleForTesting
onAudioGroupSelectableCodecConf( int groupId, BluetoothLeAudioCodecConfig[] inputSelectableCodecConfig, BluetoothLeAudioCodecConfig[] outputSelectableCodecConfig)200     void onAudioGroupSelectableCodecConf(
201             int groupId,
202             BluetoothLeAudioCodecConfig[] inputSelectableCodecConfig,
203             BluetoothLeAudioCodecConfig[] outputSelectableCodecConfig) {
204         LeAudioStackEvent event =
205                 new LeAudioStackEvent(
206                         LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED);
207 
208         event.valueInt1 = groupId;
209         event.valueCodecList1 = Arrays.asList(inputSelectableCodecConfig);
210         event.valueCodecList2 = Arrays.asList(outputSelectableCodecConfig);
211 
212         Log.d(TAG, "onAudioGroupSelectableCodecConf: " + event);
213         sendMessageToService(event);
214     }
215 
216     @VisibleForTesting
onHealthBasedRecommendationAction(byte[] address, int action)217     void onHealthBasedRecommendationAction(byte[] address, int action) {
218         LeAudioStackEvent event =
219                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION);
220         event.device = getDevice(address);
221         event.valueInt1 = action;
222 
223         Log.d(TAG, "onHealthBasedRecommendationAction: " + event);
224         sendMessageToService(event);
225     }
226 
227     @VisibleForTesting
onHealthBasedGroupRecommendationAction(int groupId, int action)228     void onHealthBasedGroupRecommendationAction(int groupId, int action) {
229         LeAudioStackEvent event =
230                 new LeAudioStackEvent(
231                         LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION);
232         event.valueInt1 = groupId;
233         event.valueInt2 = action;
234 
235         Log.d(TAG, "onHealthBasedGroupRecommendationAction: " + event);
236         sendMessageToService(event);
237     }
238 
239     @VisibleForTesting
onUnicastMonitorModeStatus(int direction, int status)240     void onUnicastMonitorModeStatus(int direction, int status) {
241         LeAudioStackEvent event =
242                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS);
243         event.valueInt1 = direction;
244         event.valueInt2 = status;
245 
246         Log.d(TAG, "onUnicastMonitorModeStatus: " + event);
247         sendMessageToService(event);
248     }
249 
250     @VisibleForTesting
onGroupStreamStatus(int groupId, int groupStreamStatus)251     void onGroupStreamStatus(int groupId, int groupStreamStatus) {
252         LeAudioStackEvent event =
253                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STREAM_STATUS_CHANGED);
254         event.valueInt1 = groupId;
255         event.valueInt2 = groupStreamStatus;
256 
257         Log.d(TAG, "onGroupStreamStatus: " + event);
258         sendMessageToService(event);
259     }
260 
261     /**
262      * Initializes the native interface.
263      *
264      * <p>priorities to configure.
265      */
init(BluetoothLeAudioCodecConfig[] codecConfigOffloading)266     public void init(BluetoothLeAudioCodecConfig[] codecConfigOffloading) {
267         initNative(codecConfigOffloading);
268     }
269 
270     /** Cleanup the native interface. */
cleanup()271     public void cleanup() {
272         cleanupNative();
273     }
274 
275     /**
276      * Initiates LeAudio connection to a remote device.
277      *
278      * @param device the remote device
279      * @return true on success, otherwise false.
280      */
connectLeAudio(BluetoothDevice device)281     public boolean connectLeAudio(BluetoothDevice device) {
282         return connectLeAudioNative(getByteAddress(device));
283     }
284 
285     /**
286      * Disconnects LeAudio from a remote device.
287      *
288      * @param device the remote device
289      * @return true on success, otherwise false.
290      */
disconnectLeAudio(BluetoothDevice device)291     public boolean disconnectLeAudio(BluetoothDevice device) {
292         return disconnectLeAudioNative(getByteAddress(device));
293     }
294 
295     /**
296      * Enable/Disable LeAudio for the group.
297      *
298      * @param device the remote device
299      * @param enabled true if enabled, false to disabled
300      * @return true on success, otherwise false.
301      */
setEnableState(BluetoothDevice device, boolean enabled)302     public boolean setEnableState(BluetoothDevice device, boolean enabled) {
303         return setEnableStateNative(getByteAddress(device), enabled);
304     }
305 
306     /**
307      * Add new Node into a group.
308      *
309      * @param groupId group identifier
310      * @param device remote device
311      */
groupAddNode(int groupId, BluetoothDevice device)312     public boolean groupAddNode(int groupId, BluetoothDevice device) {
313         return groupAddNodeNative(groupId, getByteAddress(device));
314     }
315 
316     /**
317      * Add new Node into a group.
318      *
319      * @param groupId group identifier
320      * @param device remote device
321      */
groupRemoveNode(int groupId, BluetoothDevice device)322     public boolean groupRemoveNode(int groupId, BluetoothDevice device) {
323         return groupRemoveNodeNative(groupId, getByteAddress(device));
324     }
325 
326     /**
327      * Set active group.
328      *
329      * @param groupId group ID to set as active
330      */
groupSetActive(int groupId)331     public void groupSetActive(int groupId) {
332         groupSetActiveNative(groupId);
333     }
334 
335     /**
336      * Set codec config preference.
337      *
338      * @param groupId group ID for the preference
339      * @param inputCodecConfig input codec configuration
340      * @param outputCodecConfig output codec configuration
341      */
setCodecConfigPreference( int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig)342     public void setCodecConfigPreference(
343             int groupId,
344             BluetoothLeAudioCodecConfig inputCodecConfig,
345             BluetoothLeAudioCodecConfig outputCodecConfig) {
346         setCodecConfigPreferenceNative(groupId, inputCodecConfig, outputCodecConfig);
347     }
348 
349     /**
350      * Set content control id (Ccid) along with context type.
351      *
352      * @param ccid content control id
353      * @param contextType assigned contextType
354      */
setCcidInformation(int ccid, int contextType)355     public void setCcidInformation(int ccid, int contextType) {
356         Log.d(TAG, "setCcidInformation ccid: " + ccid + " context type: " + contextType);
357         setCcidInformationNative(ccid, contextType);
358     }
359 
360     /**
361      * Set in call call flag.
362      *
363      * @param inCall true when device in call (any state), false otherwise
364      */
setInCall(boolean inCall)365     public void setInCall(boolean inCall) {
366         Log.d(TAG, "setInCall inCall: " + inCall);
367         setInCallNative(inCall);
368     }
369 
370     /**
371      * Set unicast monitor mode flag.
372      *
373      * @param direction direction for which monitor mode should be used
374      * @param enable true when LE Audio device should be listening for streaming status on direction
375      *     stream. false otherwise
376      */
setUnicastMonitorMode(int direction, boolean enable)377     public void setUnicastMonitorMode(int direction, boolean enable) {
378         Log.d(TAG, "setUnicastMonitorMode enable: " + enable + ", direction : " + direction);
379         setUnicastMonitorModeNative(direction, enable);
380     }
381 
382     /**
383      * Sends the audio preferences for the groupId to the native stack.
384      *
385      * @param groupId is the groupId corresponding to the preferences
386      * @param isOutputPreferenceLeAudio whether LEA is preferred for OUTPUT_ONLY
387      * @param isDuplexPreferenceLeAudio whether LEA is preferred for DUPLEX
388      */
sendAudioProfilePreferences( int groupId, boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio)389     public void sendAudioProfilePreferences(
390             int groupId, boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio) {
391         Log.d(
392                 TAG,
393                 "sendAudioProfilePreferences groupId="
394                         + groupId
395                         + ", isOutputPreferenceLeAudio="
396                         + isOutputPreferenceLeAudio
397                         + ", isDuplexPreferenceLeAudio="
398                         + isDuplexPreferenceLeAudio);
399         sendAudioProfilePreferencesNative(
400                 groupId, isOutputPreferenceLeAudio, isDuplexPreferenceLeAudio);
401     }
402 
403     /**
404      * Set allowed context which should be considered while Audio Framework would request streaming.
405      *
406      * @param groupId is the groupId corresponding to the allowed context
407      * @param sinkContextTypes sink context types that would be allowed to stream
408      * @param sourceContextTypes source context types that would be allowed to stream
409      */
setGroupAllowedContextMask( int groupId, int sinkContextTypes, int sourceContextTypes)410     public void setGroupAllowedContextMask(
411             int groupId, int sinkContextTypes, int sourceContextTypes) {
412         Log.d(
413                 TAG,
414                 "setGroupAllowedContextMask groupId="
415                         + groupId
416                         + ", sinkContextTypes="
417                         + sinkContextTypes
418                         + ", sourceContextTypes="
419                         + sourceContextTypes);
420         setGroupAllowedContextMaskNative(groupId, sinkContextTypes, sourceContextTypes);
421     }
422 
423     // Native methods that call into the JNI interface
initNative(BluetoothLeAudioCodecConfig[] codecConfigOffloading)424     private native void initNative(BluetoothLeAudioCodecConfig[] codecConfigOffloading);
425 
cleanupNative()426     private native void cleanupNative();
427 
connectLeAudioNative(byte[] address)428     private native boolean connectLeAudioNative(byte[] address);
429 
disconnectLeAudioNative(byte[] address)430     private native boolean disconnectLeAudioNative(byte[] address);
431 
setEnableStateNative(byte[] address, boolean enabled)432     private native boolean setEnableStateNative(byte[] address, boolean enabled);
433 
groupAddNodeNative(int groupId, byte[] address)434     private native boolean groupAddNodeNative(int groupId, byte[] address);
435 
groupRemoveNodeNative(int groupId, byte[] address)436     private native boolean groupRemoveNodeNative(int groupId, byte[] address);
437 
groupSetActiveNative(int groupId)438     private native void groupSetActiveNative(int groupId);
439 
setCodecConfigPreferenceNative( int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig)440     private native void setCodecConfigPreferenceNative(
441             int groupId,
442             BluetoothLeAudioCodecConfig inputCodecConfig,
443             BluetoothLeAudioCodecConfig outputCodecConfig);
444 
setCcidInformationNative(int ccid, int contextType)445     private native void setCcidInformationNative(int ccid, int contextType);
446 
setInCallNative(boolean inCall)447     private native void setInCallNative(boolean inCall);
448 
setUnicastMonitorModeNative(int direction, boolean enable)449     private native void setUnicastMonitorModeNative(int direction, boolean enable);
450 
confirmUnicastStreamRequestNative()451     private native void confirmUnicastStreamRequestNative();
452 
453     /*package*/
sendAudioProfilePreferencesNative( int groupId, boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio)454     private native void sendAudioProfilePreferencesNative(
455             int groupId, boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio);
456 
setGroupAllowedContextMaskNative( int groupId, int sinkContextTypes, int sourceContextTypes)457     private native void setGroupAllowedContextMaskNative(
458             int groupId, int sinkContextTypes, int sourceContextTypes);
459 }
460