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