• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /**
37  * LeAudio Native Interface to/from JNI.
38  */
39 public class LeAudioNativeInterface {
40     private static final String TAG = "LeAudioNativeInterface";
41     private static final boolean DBG = true;
42     private BluetoothAdapter mAdapter;
43 
44     @GuardedBy("INSTANCE_LOCK")
45     private static LeAudioNativeInterface sInstance;
46     private static final Object INSTANCE_LOCK = new Object();
47 
48     static {
classInitNative()49         classInitNative();
50     }
51 
LeAudioNativeInterface()52     private LeAudioNativeInterface() {
53         mAdapter = BluetoothAdapter.getDefaultAdapter();
54         if (mAdapter == null) {
55             Log.wtf(TAG, "No Bluetooth Adapter Available");
56         }
57     }
58 
59     /**
60      * Get singleton instance.
61      */
getInstance()62     public static LeAudioNativeInterface getInstance() {
63         synchronized (INSTANCE_LOCK) {
64             if (sInstance == null) {
65                 sInstance = new LeAudioNativeInterface();
66             }
67             return sInstance;
68         }
69     }
70 
71     /**
72      * Set singleton instance.
73      */
74     @VisibleForTesting
setInstance(LeAudioNativeInterface instance)75     static void setInstance(LeAudioNativeInterface instance) {
76         synchronized (INSTANCE_LOCK) {
77             sInstance = instance;
78         }
79     }
80 
getByteAddress(BluetoothDevice device)81     private byte[] getByteAddress(BluetoothDevice device) {
82         if (device == null) {
83             return Utils.getBytesFromAddress("00:00:00:00:00:00");
84         }
85         return Utils.getBytesFromAddress(device.getAddress());
86     }
87 
sendMessageToService(LeAudioStackEvent event)88     private void sendMessageToService(LeAudioStackEvent event) {
89         LeAudioService service = LeAudioService.getLeAudioService();
90         if (service != null) {
91             service.messageFromNative(event);
92         } else {
93             Log.e(TAG, "Event ignored, service not available: " + event);
94         }
95     }
96 
getDevice(byte[] address)97     private BluetoothDevice getDevice(byte[] address) {
98         return mAdapter.getRemoteDevice(address);
99     }
100 
101     // Callbacks from the native stack back into the Java framework.
102     // All callbacks are routed via the Service which will disambiguate which
103     // state machine the message should be routed to.
onInitialized()104     private void onInitialized() {
105         LeAudioStackEvent event =
106                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
107 
108         if (DBG) {
109             Log.d(TAG, "onInitialized: " + event);
110         }
111         sendMessageToService(event);
112     }
113 
114     @VisibleForTesting
onConnectionStateChanged(int state, byte[] address)115     void onConnectionStateChanged(int state, byte[] address) {
116         LeAudioStackEvent event =
117                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
118         event.device = getDevice(address);
119         event.valueInt1 = state;
120 
121         if (DBG) {
122             Log.d(TAG, "onConnectionStateChanged: " + event);
123         }
124         sendMessageToService(event);
125     }
126 
127     @VisibleForTesting
onGroupStatus(int groupId, int groupStatus)128     void onGroupStatus(int groupId, int groupStatus) {
129         LeAudioStackEvent event =
130                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
131         event.valueInt1 = groupId;
132         event.valueInt2 = groupStatus;
133 
134         if (DBG) {
135             Log.d(TAG, "onGroupStatus: " + event);
136         }
137         sendMessageToService(event);
138     }
139 
140     @VisibleForTesting
onGroupNodeStatus(byte[] address, int groupId, int nodeStatus)141     void onGroupNodeStatus(byte[] address, int groupId, int nodeStatus) {
142         LeAudioStackEvent event =
143                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
144         event.valueInt1 = groupId;
145         event.valueInt2 = nodeStatus;
146         event.device = getDevice(address);
147 
148         if (DBG) {
149             Log.d(TAG, "onGroupNodeStatus: " + event);
150         }
151         sendMessageToService(event);
152     }
153 
154     @VisibleForTesting
onAudioConf(int direction, int groupId, int sinkAudioLocation, int sourceAudioLocation, int availableContexts)155     void onAudioConf(int direction, int groupId, int sinkAudioLocation,
156                              int sourceAudioLocation, int availableContexts) {
157         LeAudioStackEvent event =
158                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
159         event.valueInt1 = direction;
160         event.valueInt2 = groupId;
161         event.valueInt3 = sinkAudioLocation;
162         event.valueInt4 = sourceAudioLocation;
163         event.valueInt5 = availableContexts;
164 
165         if (DBG) {
166             Log.d(TAG, "onAudioConf: " + event);
167         }
168         sendMessageToService(event);
169     }
170 
171     @VisibleForTesting
onSinkAudioLocationAvailable(byte[] address, int sinkAudioLocation)172     void onSinkAudioLocationAvailable(byte[] address, int sinkAudioLocation) {
173         LeAudioStackEvent event =
174                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE);
175         event.device = getDevice(address);
176         event.valueInt1 = sinkAudioLocation;
177 
178         if (DBG) {
179             Log.d(TAG, "onSinkAudioLocationAvailable: " + event);
180         }
181         sendMessageToService(event);
182     }
183 
184     @VisibleForTesting
onAudioLocalCodecCapabilities( BluetoothLeAudioCodecConfig[] localInputCodecCapabilities, BluetoothLeAudioCodecConfig[] localOutputCodecCapabilities)185     void onAudioLocalCodecCapabilities(
186                             BluetoothLeAudioCodecConfig[] localInputCodecCapabilities,
187                             BluetoothLeAudioCodecConfig[] localOutputCodecCapabilities) {
188         LeAudioStackEvent event =
189                 new LeAudioStackEvent(
190                         LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED);
191 
192         event.valueCodecList1 = Arrays.asList(localInputCodecCapabilities);
193         event.valueCodecList2 = Arrays.asList(localOutputCodecCapabilities);
194 
195         if (DBG) {
196             Log.d(TAG, "onAudioLocalCodecCapabilities: " + event);
197         }
198         sendMessageToService(event);
199     }
200 
201     @VisibleForTesting
onAudioGroupCodecConf(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig, BluetoothLeAudioCodecConfig [] inputSelectableCodecConfig, BluetoothLeAudioCodecConfig [] outputSelectableCodecConfig)202     void onAudioGroupCodecConf(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig,
203                             BluetoothLeAudioCodecConfig outputCodecConfig,
204                             BluetoothLeAudioCodecConfig [] inputSelectableCodecConfig,
205                             BluetoothLeAudioCodecConfig [] outputSelectableCodecConfig) {
206         LeAudioStackEvent event =
207                 new LeAudioStackEvent(
208                         LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED);
209 
210         event.valueInt1 = groupId;
211         event.valueCodec1 = inputCodecConfig;
212         event.valueCodec2 = outputCodecConfig;
213         event.valueCodecList1 = Arrays.asList(inputSelectableCodecConfig);
214         event.valueCodecList2 = Arrays.asList(outputSelectableCodecConfig);
215 
216         if (DBG) {
217             Log.d(TAG, "onAudioGroupCodecConf: " + event);
218         }
219         sendMessageToService(event);
220     }
221 
222     /**
223      * Initializes the native interface.
224      *
225      * priorities to configure.
226      */
init(BluetoothLeAudioCodecConfig[] codecConfigOffloading)227     public void init(BluetoothLeAudioCodecConfig[] codecConfigOffloading) {
228         initNative(codecConfigOffloading);
229     }
230 
231     /**
232      * Cleanup the native interface.
233      */
cleanup()234     public void cleanup() {
235         cleanupNative();
236     }
237 
238     /**
239      * Initiates LeAudio connection to a remote device.
240      *
241      * @param device the remote device
242      * @return true on success, otherwise false.
243      */
connectLeAudio(BluetoothDevice device)244     public boolean connectLeAudio(BluetoothDevice device) {
245         return connectLeAudioNative(getByteAddress(device));
246     }
247 
248     /**
249      * Disconnects LeAudio from a remote device.
250      *
251      * @param device the remote device
252      * @return true on success, otherwise false.
253      */
disconnectLeAudio(BluetoothDevice device)254     public boolean disconnectLeAudio(BluetoothDevice device) {
255         return disconnectLeAudioNative(getByteAddress(device));
256     }
257 
258     /**
259      * Add new Node into a group.
260      * @param groupId group identifier
261      * @param device remote device
262      */
groupAddNode(int groupId, BluetoothDevice device)263      public boolean groupAddNode(int groupId, BluetoothDevice device) {
264         return groupAddNodeNative(groupId, getByteAddress(device));
265     }
266 
267     /**
268      * Add new Node into a group.
269      * @param groupId group identifier
270      * @param device remote device
271      */
groupRemoveNode(int groupId, BluetoothDevice device)272     public boolean groupRemoveNode(int groupId, BluetoothDevice device) {
273         return groupRemoveNodeNative(groupId, getByteAddress(device));
274     }
275 
276     /**
277      * Set active group.
278      * @param groupId group ID to set as active
279      */
groupSetActive(int groupId)280     public void groupSetActive(int groupId) {
281         groupSetActiveNative(groupId);
282     }
283 
284     /**
285      * Set codec config preference.
286      * @param groupId group ID for the preference
287      * @param inputCodecConfig input codec configuration
288      * @param outputCodecConfig output codec configuration
289      */
setCodecConfigPreference(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig)290     public void setCodecConfigPreference(int groupId,
291             BluetoothLeAudioCodecConfig inputCodecConfig,
292             BluetoothLeAudioCodecConfig outputCodecConfig) {
293         setCodecConfigPreferenceNative(groupId, inputCodecConfig, outputCodecConfig);
294     }
295 
296     /**
297      * Set content control id (Ccid) along with context type.
298      * @param ccid content control id
299      * @param contextType assigned contextType
300      */
setCcidInformation(int ccid, int contextType)301     public void setCcidInformation(int ccid, int contextType) {
302         if (DBG) {
303             Log.d(TAG, "setCcidInformation ccid: " + ccid + " context type: " + contextType);
304         }
305         setCcidInformationNative(ccid, contextType);
306     }
307 
308     /**
309      * Set in call call flag.
310      * @param inCall true when device in call (any state), false otherwise
311      */
setInCall(boolean inCall)312     public void setInCall(boolean inCall) {
313         if (DBG) {
314             Log.d(TAG, "setInCall inCall: " + inCall);
315         }
316         setInCallNative(inCall);
317     }
318 
319     // Native methods that call into the JNI interface
classInitNative()320     private static native void classInitNative();
initNative(BluetoothLeAudioCodecConfig[] codecConfigOffloading)321     private native void initNative(BluetoothLeAudioCodecConfig[] codecConfigOffloading);
cleanupNative()322     private native void cleanupNative();
connectLeAudioNative(byte[] address)323     private native boolean connectLeAudioNative(byte[] address);
disconnectLeAudioNative(byte[] address)324     private native boolean disconnectLeAudioNative(byte[] address);
groupAddNodeNative(int groupId, byte[] address)325     private native boolean groupAddNodeNative(int groupId, byte[] address);
groupRemoveNodeNative(int groupId, byte[] address)326     private native boolean groupRemoveNodeNative(int groupId, byte[] address);
groupSetActiveNative(int groupId)327     private native void groupSetActiveNative(int groupId);
setCodecConfigPreferenceNative(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig)328     private native void setCodecConfigPreferenceNative(int groupId,
329             BluetoothLeAudioCodecConfig inputCodecConfig,
330             BluetoothLeAudioCodecConfig outputCodecConfig);
setCcidInformationNative(int ccid, int contextType)331     private native void setCcidInformationNative(int ccid, int contextType);
setInCallNative(boolean inCall)332     private native void setInCallNative(boolean inCall);
333 }
334