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