1 /* 2 * Copyright 2017 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 /* 18 * Defines the native inteface that is used by state machine/service to 19 * send or receive messages from the native stack. This file is registered 20 * for the native methods in the corresponding JNI C++ file. 21 */ 22 package com.android.bluetooth.a2dp; 23 24 import android.bluetooth.BluetoothA2dp; 25 import android.bluetooth.BluetoothAdapter; 26 import android.bluetooth.BluetoothCodecConfig; 27 import android.bluetooth.BluetoothCodecStatus; 28 import android.bluetooth.BluetoothDevice; 29 import android.util.Log; 30 31 import com.android.bluetooth.Utils; 32 import com.android.internal.annotations.GuardedBy; 33 import com.android.internal.annotations.VisibleForTesting; 34 35 /** 36 * A2DP Native Interface to/from JNI. 37 */ 38 public class A2dpNativeInterface { 39 private static final String TAG = "A2dpNativeInterface"; 40 private static final boolean DBG = true; 41 private BluetoothAdapter mAdapter; 42 43 @GuardedBy("INSTANCE_LOCK") 44 private static A2dpNativeInterface sInstance; 45 private static final Object INSTANCE_LOCK = new Object(); 46 47 static { classInitNative()48 classInitNative(); 49 } 50 51 @VisibleForTesting A2dpNativeInterface()52 private A2dpNativeInterface() { 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 A2dpNativeInterface getInstance() { 63 synchronized (INSTANCE_LOCK) { 64 if (sInstance == null) { 65 sInstance = new A2dpNativeInterface(); 66 } 67 return sInstance; 68 } 69 } 70 71 /** 72 * Initializes the native interface. 73 * 74 * @param maxConnectedAudioDevices maximum number of A2DP Sink devices that can be connected 75 * simultaneously 76 * @param codecConfigPriorities an array with the codec configuration 77 * priorities to configure. 78 */ init(int maxConnectedAudioDevices, BluetoothCodecConfig[] codecConfigPriorities, BluetoothCodecConfig[] codecConfigOffloading)79 public void init(int maxConnectedAudioDevices, BluetoothCodecConfig[] codecConfigPriorities, 80 BluetoothCodecConfig[] codecConfigOffloading) { 81 initNative(maxConnectedAudioDevices, codecConfigPriorities, codecConfigOffloading); 82 } 83 84 /** 85 * Cleanup the native interface. 86 */ cleanup()87 public void cleanup() { 88 cleanupNative(); 89 } 90 91 /** 92 * Initiates A2DP connection to a remote device. 93 * 94 * @param device the remote device 95 * @return true on success, otherwise false. 96 */ connectA2dp(BluetoothDevice device)97 public boolean connectA2dp(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 */ disconnectA2dp(BluetoothDevice device)107 public boolean disconnectA2dp(BluetoothDevice device) { 108 return disconnectA2dpNative(getByteAddress(device)); 109 } 110 111 /** 112 * Sets a connected A2DP remote device to silence mode. 113 * 114 * @param device the remote device 115 * @return true on success, otherwise false. 116 */ setSilenceDevice(BluetoothDevice device, boolean silence)117 public boolean setSilenceDevice(BluetoothDevice device, boolean silence) { 118 return setSilenceDeviceNative(getByteAddress(device), silence); 119 } 120 121 /** 122 * Sets a connected A2DP remote device as active. 123 * 124 * @param device the remote device 125 * @return true on success, otherwise false. 126 */ setActiveDevice(BluetoothDevice device)127 public boolean setActiveDevice(BluetoothDevice device) { 128 return setActiveDeviceNative(getByteAddress(device)); 129 } 130 131 /** 132 * Sets the codec configuration preferences. 133 * 134 * @param device the remote Bluetooth device 135 * @param codecConfigArray an array with the codec configurations to 136 * configure. 137 * @return true on success, otherwise false. 138 */ setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig[] codecConfigArray)139 public boolean setCodecConfigPreference(BluetoothDevice device, 140 BluetoothCodecConfig[] codecConfigArray) { 141 return setCodecConfigPreferenceNative(getByteAddress(device), 142 codecConfigArray); 143 } 144 getDevice(byte[] address)145 private BluetoothDevice getDevice(byte[] address) { 146 return mAdapter.getRemoteDevice(address); 147 } 148 getByteAddress(BluetoothDevice device)149 private byte[] getByteAddress(BluetoothDevice device) { 150 if (device == null) { 151 return Utils.getBytesFromAddress("00:00:00:00:00:00"); 152 } 153 return Utils.getBytesFromAddress(device.getAddress()); 154 } 155 sendMessageToService(A2dpStackEvent event)156 private void sendMessageToService(A2dpStackEvent event) { 157 A2dpService service = A2dpService.getA2dpService(); 158 if (service != null) { 159 service.messageFromNative(event); 160 } else { 161 Log.w(TAG, "Event ignored, service not available: " + event); 162 } 163 } 164 165 // Callbacks from the native stack back into the Java framework. 166 // All callbacks are routed via the Service which will disambiguate which 167 // state machine the message should be routed to. 168 onConnectionStateChanged(byte[] address, int state)169 private void onConnectionStateChanged(byte[] address, int state) { 170 A2dpStackEvent event = 171 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 172 event.device = getDevice(address); 173 event.valueInt = state; 174 175 if (DBG) { 176 Log.d(TAG, "onConnectionStateChanged: " + event); 177 } 178 sendMessageToService(event); 179 } 180 onAudioStateChanged(byte[] address, int state)181 private void onAudioStateChanged(byte[] address, int state) { 182 A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 183 event.device = getDevice(address); 184 event.valueInt = state; 185 186 if (DBG) { 187 Log.d(TAG, "onAudioStateChanged: " + event); 188 } 189 sendMessageToService(event); 190 } 191 onCodecConfigChanged(byte[] address, BluetoothCodecConfig newCodecConfig, BluetoothCodecConfig[] codecsLocalCapabilities, BluetoothCodecConfig[] codecsSelectableCapabilities)192 private void onCodecConfigChanged(byte[] address, 193 BluetoothCodecConfig newCodecConfig, 194 BluetoothCodecConfig[] codecsLocalCapabilities, 195 BluetoothCodecConfig[] codecsSelectableCapabilities) { 196 A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED); 197 event.device = getDevice(address); 198 event.codecStatus = new BluetoothCodecStatus(newCodecConfig, 199 codecsLocalCapabilities, 200 codecsSelectableCapabilities); 201 if (DBG) { 202 Log.d(TAG, "onCodecConfigChanged: " + event); 203 } 204 sendMessageToService(event); 205 } 206 isMandatoryCodecPreferred(byte[] address)207 private boolean isMandatoryCodecPreferred(byte[] address) { 208 A2dpService service = A2dpService.getA2dpService(); 209 if (service != null) { 210 int enabled = service.getOptionalCodecsEnabled(getDevice(address)); 211 if (DBG) { 212 Log.d(TAG, "isMandatoryCodecPreferred: optional preference " + enabled); 213 } 214 // Optional codecs are more preferred if possible 215 return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; 216 } else { 217 Log.w(TAG, "isMandatoryCodecPreferred: service not available"); 218 return false; 219 } 220 } 221 222 // Native methods that call into the JNI interface classInitNative()223 private static native void classInitNative(); initNative(int maxConnectedAudioDevices, BluetoothCodecConfig[] codecConfigPriorities, BluetoothCodecConfig[] codecConfigOffloading)224 private native void initNative(int maxConnectedAudioDevices, 225 BluetoothCodecConfig[] codecConfigPriorities, 226 BluetoothCodecConfig[] codecConfigOffloading); cleanupNative()227 private native void cleanupNative(); connectA2dpNative(byte[] address)228 private native boolean connectA2dpNative(byte[] address); disconnectA2dpNative(byte[] address)229 private native boolean disconnectA2dpNative(byte[] address); setSilenceDeviceNative(byte[] address, boolean silence)230 private native boolean setSilenceDeviceNative(byte[] address, boolean silence); setActiveDeviceNative(byte[] address)231 private native boolean setActiveDeviceNative(byte[] address); setCodecConfigPreferenceNative(byte[] address, BluetoothCodecConfig[] codecConfigArray)232 private native boolean setCodecConfigPreferenceNative(byte[] address, 233 BluetoothCodecConfig[] codecConfigArray); 234 } 235