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