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