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