1 /* 2 * Copyright (C) 2021 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 package com.android.bluetooth.a2dpsink; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.util.Log; 21 22 import com.android.bluetooth.Utils; 23 import com.android.bluetooth.btservice.AdapterService; 24 import com.android.bluetooth.flags.Flags; 25 import com.android.internal.annotations.GuardedBy; 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.util.Objects; 29 30 /** A2DP Sink Native Interface to/from JNI. */ 31 public class A2dpSinkNativeInterface { 32 private static final String TAG = A2dpSinkNativeInterface.class.getSimpleName(); 33 private AdapterService mAdapterService; 34 35 @GuardedBy("INSTANCE_LOCK") 36 private static A2dpSinkNativeInterface sInstance; 37 38 private static final Object INSTANCE_LOCK = new Object(); 39 A2dpSinkNativeInterface()40 private A2dpSinkNativeInterface() { 41 mAdapterService = 42 Objects.requireNonNull( 43 AdapterService.getAdapterService(), 44 "AdapterService cannot be null when A2dpSinkNativeInterface init"); 45 } 46 47 /** Get singleton instance. */ getInstance()48 public static A2dpSinkNativeInterface getInstance() { 49 synchronized (INSTANCE_LOCK) { 50 if (sInstance == null) { 51 sInstance = new A2dpSinkNativeInterface(); 52 } 53 return sInstance; 54 } 55 } 56 57 /** Set singleton instance. */ 58 @VisibleForTesting setInstance(A2dpSinkNativeInterface instance)59 public static void setInstance(A2dpSinkNativeInterface instance) { 60 synchronized (INSTANCE_LOCK) { 61 sInstance = instance; 62 } 63 } 64 65 /** 66 * Initializes the native interface and sets the max number of connected devices 67 * 68 * @param maxConnectedAudioDevices The maximum number of devices that can be connected at once 69 */ init(int maxConnectedAudioDevices)70 public void init(int maxConnectedAudioDevices) { 71 initNative(maxConnectedAudioDevices); 72 } 73 74 /** Cleanup the native interface. */ cleanup()75 public void cleanup() { 76 cleanupNative(); 77 } 78 getDevice(byte[] address)79 private BluetoothDevice getDevice(byte[] address) { 80 return mAdapterService.getDeviceFromByte(address); 81 } 82 getByteAddress(BluetoothDevice device)83 private byte[] getByteAddress(BluetoothDevice device) { 84 if (Flags.identityAddressNullIfUnknown()) { 85 return Utils.getByteBrEdrAddress(device); 86 } else { 87 return mAdapterService.getByteIdentityAddress(device); 88 } 89 } 90 91 /** 92 * Initiates an A2DP connection to a remote device. 93 * 94 * @param device the remote device 95 * @return true on success, otherwise false. 96 */ connectA2dpSink(BluetoothDevice device)97 public boolean connectA2dpSink(BluetoothDevice device) { 98 return connectA2dpNative(getByteAddress(device)); 99 } 100 101 /** 102 * Disconnects A2DP from a remote device. 103 * 104 * @param device the remote device 105 * @return true on success, otherwise false. 106 */ disconnectA2dpSink(BluetoothDevice device)107 public boolean disconnectA2dpSink(BluetoothDevice device) { 108 return disconnectA2dpNative(getByteAddress(device)); 109 } 110 111 /** 112 * Set a BluetoothDevice as the active device 113 * 114 * <p>The active device is the only one that will receive passthrough commands and the only one 115 * that will have its audio decoded. 116 * 117 * <p>Sending null for the active device will make no device active. 118 * 119 * @return True if the active device request has been scheduled 120 */ setActiveDevice(BluetoothDevice device)121 public boolean setActiveDevice(BluetoothDevice device) { 122 // Translate to byte address for JNI. Use an all 0 MAC for no active device 123 byte[] address = null; 124 if (device != null) { 125 address = getByteAddress(device); 126 } else { 127 address = Utils.getBytesFromAddress("00:00:00:00:00:00"); 128 } 129 return setActiveDeviceNative(address); 130 } 131 132 /** Inform A2DP decoder of the current audio focus */ informAudioFocusState(int focusGranted)133 public void informAudioFocusState(int focusGranted) { 134 informAudioFocusStateNative(focusGranted); 135 } 136 137 /** Inform A2DP decoder the desired audio gain */ informAudioTrackGain(float gain)138 public void informAudioTrackGain(float gain) { 139 informAudioTrackGainNative(gain); 140 } 141 142 /** Send a stack event up to the A2DP Sink Service */ sendMessageToService(StackEvent event)143 private void sendMessageToService(StackEvent event) { 144 A2dpSinkService service = A2dpSinkService.getA2dpSinkService(); 145 if (service != null) { 146 service.messageFromNative(event); 147 } else { 148 Log.e(TAG, "Event ignored, service not available: " + event); 149 } 150 } 151 152 /** For the JNI to send messages about connection state changes */ onConnectionStateChanged(byte[] address, int state)153 public void onConnectionStateChanged(byte[] address, int state) { 154 StackEvent event = StackEvent.connectionStateChanged(getDevice(address), state); 155 Log.d(TAG, "onConnectionStateChanged: " + event); 156 sendMessageToService(event); 157 } 158 159 /** For the JNI to send messages about audio stream state changes */ onAudioStateChanged(byte[] address, int state)160 public void onAudioStateChanged(byte[] address, int state) { 161 StackEvent event = StackEvent.audioStateChanged(getDevice(address), state); 162 Log.d(TAG, "onAudioStateChanged: " + event); 163 sendMessageToService(event); 164 } 165 166 /** For the JNI to send messages about audio configuration changes */ onAudioConfigChanged(byte[] address, int sampleRate, int channelCount)167 public void onAudioConfigChanged(byte[] address, int sampleRate, int channelCount) { 168 StackEvent event = 169 StackEvent.audioConfigChanged(getDevice(address), sampleRate, channelCount); 170 Log.d(TAG, "onAudioConfigChanged: " + event); 171 sendMessageToService(event); 172 } 173 174 // Native methods that call into the JNI interface initNative(int maxConnectedAudioDevices)175 private native void initNative(int maxConnectedAudioDevices); 176 cleanupNative()177 private native void cleanupNative(); 178 connectA2dpNative(byte[] address)179 private native boolean connectA2dpNative(byte[] address); 180 disconnectA2dpNative(byte[] address)181 private native boolean disconnectA2dpNative(byte[] address); 182 setActiveDeviceNative(byte[] address)183 private native boolean setActiveDeviceNative(byte[] address); 184 informAudioFocusStateNative(int focusGranted)185 private native void informAudioFocusStateNative(int focusGranted); 186 informAudioTrackGainNative(float gain)187 private native void informAudioTrackGainNative(float gain); 188 } 189