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