1 /* 2 * Copyright 2021 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.bluetooth.hap; 19 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothHapPresetInfo; 23 import android.util.Log; 24 25 import com.android.bluetooth.Utils; 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 31 /** Hearing Access Profile Client Native Interface to/from JNI. */ 32 public class HapClientNativeInterface { 33 private static final String TAG = HapClientNativeInterface.class.getSimpleName(); 34 35 private final BluetoothAdapter mAdapter; 36 HapClientNativeInterface()37 public HapClientNativeInterface() { 38 mAdapter = BluetoothAdapter.getDefaultAdapter(); 39 if (mAdapter == null) { 40 Log.wtf(TAG, "No Bluetooth Adapter Available"); 41 } 42 } 43 44 /** 45 * Initiates HapClientService connection to a remote device. 46 * 47 * @param device the remote device 48 * @return true on success, otherwise false. 49 */ 50 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) connectHapClient(BluetoothDevice device)51 public boolean connectHapClient(BluetoothDevice device) { 52 return connectHapClientNative(getByteAddress(device)); 53 } 54 55 /** 56 * Disconnects HapClientService from a remote device. 57 * 58 * @param device the remote device 59 * @return true on success, otherwise false. 60 */ 61 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) disconnectHapClient(BluetoothDevice device)62 public boolean disconnectHapClient(BluetoothDevice device) { 63 return disconnectHapClientNative(getByteAddress(device)); 64 } 65 66 /** 67 * Gets a HapClientService device 68 * 69 * @param address the remote device address 70 * @return Bluetooth Device. 71 */ 72 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) getDevice(byte[] address)73 public BluetoothDevice getDevice(byte[] address) { 74 return mAdapter.getRemoteDevice(address); 75 } 76 getByteAddress(BluetoothDevice device)77 private byte[] getByteAddress(BluetoothDevice device) { 78 if (device == null) { 79 return Utils.getBytesFromAddress("00:00:00:00:00:00"); 80 } 81 return Utils.getBytesFromAddress(device.getAddress()); 82 } 83 84 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) sendMessageToService(HapClientStackEvent event)85 void sendMessageToService(HapClientStackEvent event) { 86 HapClientService service = HapClientService.getHapClientService(); 87 if (service != null) { 88 service.messageFromNative(event); 89 } else { 90 Log.e(TAG, "Event ignored, service not available: " + event); 91 } 92 } 93 94 /** Initializes the native interface. */ 95 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) init()96 public void init() { 97 initNative(); 98 } 99 100 /** Cleanup the native interface. */ 101 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) cleanup()102 public void cleanup() { 103 cleanupNative(); 104 } 105 106 /** 107 * Selects the currently active preset for a HA device 108 * 109 * @param device is the device for which we want to set the active preset 110 * @param presetIndex is an index of one of the available presets 111 */ 112 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) selectActivePreset(BluetoothDevice device, int presetIndex)113 public void selectActivePreset(BluetoothDevice device, int presetIndex) { 114 selectActivePresetNative(getByteAddress(device), presetIndex); 115 } 116 117 /** 118 * Selects the currently active preset for a HA device group. 119 * 120 * @param groupId is the device group identifier for which want to set the active preset 121 * @param presetIndex is an index of one of the available presets 122 */ 123 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) groupSelectActivePreset(int groupId, int presetIndex)124 public void groupSelectActivePreset(int groupId, int presetIndex) { 125 groupSelectActivePresetNative(groupId, presetIndex); 126 } 127 128 /** 129 * Sets the next preset as a currently active preset for a HA device 130 * 131 * @param device is the device for which we want to set the active preset 132 */ 133 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) nextActivePreset(BluetoothDevice device)134 public void nextActivePreset(BluetoothDevice device) { 135 nextActivePresetNative(getByteAddress(device)); 136 } 137 138 /** 139 * Sets the next preset as a currently active preset for a HA device group 140 * 141 * @param groupId is the device group identifier for which want to set the active preset 142 */ 143 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) groupNextActivePreset(int groupId)144 public void groupNextActivePreset(int groupId) { 145 groupNextActivePresetNative(groupId); 146 } 147 148 /** 149 * Sets the previous preset as a currently active preset for a HA device 150 * 151 * @param device is the device for which we want to set the active preset 152 */ 153 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) previousActivePreset(BluetoothDevice device)154 public void previousActivePreset(BluetoothDevice device) { 155 previousActivePresetNative(getByteAddress(device)); 156 } 157 158 /** 159 * Sets the previous preset as a currently active preset for a HA device group 160 * 161 * @param groupId is the device group identifier for which want to set the active preset 162 */ 163 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) groupPreviousActivePreset(int groupId)164 public void groupPreviousActivePreset(int groupId) { 165 groupPreviousActivePresetNative(groupId); 166 } 167 168 /** 169 * Requests the preset name 170 * 171 * @param device is the device for which we want to get the preset name 172 * @param presetIndex is an index of one of the available presets 173 */ 174 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) getPresetInfo(BluetoothDevice device, int presetIndex)175 public void getPresetInfo(BluetoothDevice device, int presetIndex) { 176 getPresetInfoNative(getByteAddress(device), presetIndex); 177 } 178 179 /** 180 * Sets the preset name 181 * 182 * @param device is the device for which we want to get the preset name 183 * @param presetIndex is an index of one of the available presets 184 * @param name is a new name for a preset 185 */ 186 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setPresetName(BluetoothDevice device, int presetIndex, String name)187 public void setPresetName(BluetoothDevice device, int presetIndex, String name) { 188 setPresetNameNative(getByteAddress(device), presetIndex, name); 189 } 190 191 /** 192 * Sets the preset name 193 * 194 * @param groupId is the device group 195 * @param presetIndex is an index of one of the available presets 196 * @param name is a new name for a preset 197 */ 198 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) groupSetPresetName(int groupId, int presetIndex, String name)199 public void groupSetPresetName(int groupId, int presetIndex, String name) { 200 groupSetPresetNameNative(groupId, presetIndex, name); 201 } 202 203 // Callbacks from the native stack back into the Java framework. 204 // All callbacks are routed via the Service which will disambiguate which 205 // state machine the message should be routed to. 206 207 @VisibleForTesting onConnectionStateChanged(int state, byte[] address)208 void onConnectionStateChanged(int state, byte[] address) { 209 HapClientStackEvent event = 210 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 211 event.device = getDevice(address); 212 event.valueInt1 = state; 213 214 Log.d(TAG, "onConnectionStateChanged: " + event); 215 sendMessageToService(event); 216 } 217 218 @VisibleForTesting onDeviceAvailable(byte[] address, int features)219 void onDeviceAvailable(byte[] address, int features) { 220 HapClientStackEvent event = 221 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_DEVICE_AVAILABLE); 222 event.device = getDevice(address); 223 event.valueInt1 = features; 224 225 Log.d(TAG, "onDeviceAvailable: " + event); 226 sendMessageToService(event); 227 } 228 229 @VisibleForTesting onFeaturesUpdate(byte[] address, int features)230 void onFeaturesUpdate(byte[] address, int features) { 231 HapClientStackEvent event = 232 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_DEVICE_FEATURES); 233 event.device = getDevice(address); 234 event.valueInt1 = features; 235 236 Log.d(TAG, "onFeaturesUpdate: " + event); 237 sendMessageToService(event); 238 } 239 240 @VisibleForTesting onActivePresetSelected(byte[] address, int presetIndex)241 void onActivePresetSelected(byte[] address, int presetIndex) { 242 HapClientStackEvent event = 243 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECTED); 244 event.device = getDevice(address); 245 event.valueInt1 = presetIndex; 246 247 Log.d(TAG, "onActivePresetSelected: " + event); 248 sendMessageToService(event); 249 } 250 251 @VisibleForTesting onActivePresetGroupSelected(int groupId, int presetIndex)252 void onActivePresetGroupSelected(int groupId, int presetIndex) { 253 HapClientStackEvent event = 254 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECTED); 255 event.valueInt1 = presetIndex; 256 event.valueInt2 = groupId; 257 258 Log.d(TAG, "onActivePresetGroupSelected: " + event); 259 sendMessageToService(event); 260 } 261 262 @VisibleForTesting onActivePresetSelectError(byte[] address, int resultCode)263 void onActivePresetSelectError(byte[] address, int resultCode) { 264 HapClientStackEvent event = 265 new HapClientStackEvent( 266 HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECT_ERROR); 267 event.device = getDevice(address); 268 event.valueInt1 = resultCode; 269 270 Log.d(TAG, "onActivePresetSelectError: " + event); 271 sendMessageToService(event); 272 } 273 274 @VisibleForTesting onActivePresetGroupSelectError(int groupId, int resultCode)275 void onActivePresetGroupSelectError(int groupId, int resultCode) { 276 HapClientStackEvent event = 277 new HapClientStackEvent( 278 HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECT_ERROR); 279 event.valueInt1 = resultCode; 280 event.valueInt2 = groupId; 281 282 Log.d(TAG, "onActivePresetGroupSelectError: " + event); 283 sendMessageToService(event); 284 } 285 286 @VisibleForTesting onPresetInfo(byte[] address, int infoReason, BluetoothHapPresetInfo[] presets)287 void onPresetInfo(byte[] address, int infoReason, BluetoothHapPresetInfo[] presets) { 288 HapClientStackEvent event = 289 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO); 290 event.device = getDevice(address); 291 event.valueInt2 = infoReason; 292 event.valueList = new ArrayList<>(Arrays.asList(presets)); 293 294 Log.d(TAG, "onPresetInfo: " + event); 295 sendMessageToService(event); 296 } 297 298 @VisibleForTesting onGroupPresetInfo(int groupId, int infoReason, BluetoothHapPresetInfo[] presets)299 void onGroupPresetInfo(int groupId, int infoReason, BluetoothHapPresetInfo[] presets) { 300 HapClientStackEvent event = 301 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO); 302 event.valueInt2 = infoReason; 303 event.valueInt3 = groupId; 304 event.valueList = new ArrayList<>(Arrays.asList(presets)); 305 306 Log.d(TAG, "onGroupPresetInfo: " + event); 307 sendMessageToService(event); 308 } 309 310 @VisibleForTesting onPresetNameSetError(byte[] address, int presetIndex, int resultCode)311 void onPresetNameSetError(byte[] address, int presetIndex, int resultCode) { 312 HapClientStackEvent event = 313 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_PRESET_NAME_SET_ERROR); 314 event.device = getDevice(address); 315 event.valueInt1 = resultCode; 316 event.valueInt2 = presetIndex; 317 318 Log.d(TAG, "onPresetNameSetError: " + event); 319 sendMessageToService(event); 320 } 321 322 @VisibleForTesting onGroupPresetNameSetError(int groupId, int presetIndex, int resultCode)323 void onGroupPresetNameSetError(int groupId, int presetIndex, int resultCode) { 324 HapClientStackEvent event = 325 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_PRESET_NAME_SET_ERROR); 326 event.valueInt1 = resultCode; 327 event.valueInt2 = presetIndex; 328 event.valueInt3 = groupId; 329 330 Log.d(TAG, "onGroupPresetNameSetError: " + event); 331 sendMessageToService(event); 332 } 333 334 @VisibleForTesting onPresetInfoError(byte[] address, int presetIndex, int resultCode)335 void onPresetInfoError(byte[] address, int presetIndex, int resultCode) { 336 HapClientStackEvent event = 337 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO_ERROR); 338 event.device = getDevice(address); 339 event.valueInt1 = resultCode; 340 event.valueInt2 = presetIndex; 341 342 Log.d(TAG, "onPresetInfoError: " + event); 343 sendMessageToService(event); 344 } 345 346 @VisibleForTesting onGroupPresetInfoError(int groupId, int presetIndex, int resultCode)347 void onGroupPresetInfoError(int groupId, int presetIndex, int resultCode) { 348 HapClientStackEvent event = 349 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO_ERROR); 350 event.valueInt1 = resultCode; 351 event.valueInt2 = presetIndex; 352 event.valueInt3 = groupId; 353 354 Log.d(TAG, "onGroupPresetInfoError: " + event); 355 sendMessageToService(event); 356 } 357 358 // Native methods that call into the JNI interface initNative()359 private native void initNative(); 360 cleanupNative()361 private native void cleanupNative(); 362 connectHapClientNative(byte[] address)363 private native boolean connectHapClientNative(byte[] address); 364 disconnectHapClientNative(byte[] address)365 private native boolean disconnectHapClientNative(byte[] address); 366 selectActivePresetNative(byte[] byteAddress, int presetIndex)367 private native void selectActivePresetNative(byte[] byteAddress, int presetIndex); 368 groupSelectActivePresetNative(int groupId, int presetIndex)369 private native void groupSelectActivePresetNative(int groupId, int presetIndex); 370 nextActivePresetNative(byte[] byteAddress)371 private native void nextActivePresetNative(byte[] byteAddress); 372 groupNextActivePresetNative(int groupId)373 private native void groupNextActivePresetNative(int groupId); 374 previousActivePresetNative(byte[] byteAddress)375 private native void previousActivePresetNative(byte[] byteAddress); 376 groupPreviousActivePresetNative(int groupId)377 private native void groupPreviousActivePresetNative(int groupId); 378 getPresetInfoNative(byte[] byteAddress, int presetIndex)379 private native void getPresetInfoNative(byte[] byteAddress, int presetIndex); 380 setPresetNameNative(byte[] byteAddress, int presetIndex, String name)381 private native void setPresetNameNative(byte[] byteAddress, int presetIndex, String name); 382 groupSetPresetNameNative(int groupId, int presetIndex, String name)383 private native void groupSetPresetNameNative(int groupId, int presetIndex, String name); 384 } 385