• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.GuardedBy;
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 
32 /**
33  * Hearing Access Profile Client Native Interface to/from JNI.
34  */
35 public class HapClientNativeInterface {
36     private static final String TAG = "HapClientNativeInterface";
37     private static final boolean DBG = true;
38     private final BluetoothAdapter mAdapter;
39 
40     @GuardedBy("INSTANCE_LOCK")
41     private static HapClientNativeInterface sInstance;
42     private static final Object INSTANCE_LOCK = new Object();
43 
44     static {
classInitNative()45         classInitNative();
46     }
47 
HapClientNativeInterface()48     private HapClientNativeInterface() {
49         mAdapter = BluetoothAdapter.getDefaultAdapter();
50         if (mAdapter == null) {
51             Log.wtf(TAG, "No Bluetooth Adapter Available");
52         }
53     }
54 
55     /**
56      * Get singleton instance.
57      */
getInstance()58     public static HapClientNativeInterface getInstance() {
59         synchronized (INSTANCE_LOCK) {
60             if (sInstance == null) {
61                 sInstance = new HapClientNativeInterface();
62             }
63             return sInstance;
64         }
65     }
66 
67     /**
68      * Initiates HapClientService connection to a remote device.
69      *
70      * @param device the remote device
71      * @return true on success, otherwise false.
72      */
73     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
connectHapClient(BluetoothDevice device)74     public boolean connectHapClient(BluetoothDevice device) {
75         return connectHapClientNative(getByteAddress(device));
76     }
77 
78     /**
79      * Disconnects HapClientService from a remote device.
80      *
81      * @param device the remote device
82      * @return true on success, otherwise false.
83      */
84     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
disconnectHapClient(BluetoothDevice device)85     public boolean disconnectHapClient(BluetoothDevice device) {
86         return disconnectHapClientNative(getByteAddress(device));
87     }
88 
89     /**
90      * Gets a HapClientService device
91      *
92      * @param address the remote device address
93      * @return Bluetooth Device.
94      */
95     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getDevice(byte[] address)96     public BluetoothDevice getDevice(byte[] address) {
97         return mAdapter.getRemoteDevice(address);
98     }
99 
getByteAddress(BluetoothDevice device)100     private byte[] getByteAddress(BluetoothDevice device) {
101         if (device == null) {
102             return Utils.getBytesFromAddress("00:00:00:00:00:00");
103         }
104         return Utils.getBytesFromAddress(device.getAddress());
105     }
106 
107     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
sendMessageToService(HapClientStackEvent event)108     void sendMessageToService(HapClientStackEvent event) {
109         HapClientService service = HapClientService.getHapClientService();
110         if (service != null) {
111             service.messageFromNative(event);
112         } else {
113             Log.e(TAG, "Event ignored, service not available: " + event);
114         }
115     }
116 
117     /**
118      * Initializes the native interface.
119      */
120     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
init()121     public void init() {
122         initNative();
123     }
124 
125     /**
126      * Cleanup the native interface.
127      */
128     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
cleanup()129     public void cleanup() {
130         cleanupNative();
131     }
132 
133     /**
134      * Selects the currently active preset for a HA device
135      *
136      * @param device is the device for which we want to set the active preset
137      * @param presetIndex is an index of one of the available presets
138      */
139     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
selectActivePreset(BluetoothDevice device, int presetIndex)140     public void selectActivePreset(BluetoothDevice device, int presetIndex) {
141         selectActivePresetNative(getByteAddress(device), presetIndex);
142     }
143 
144     /**
145      * Selects the currently active preset for a HA device group.
146      *
147      * @param groupId is the device group identifier for which want to set the active preset
148      * @param presetIndex is an index of one of the available presets
149      */
150     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
groupSelectActivePreset(int groupId, int presetIndex)151     public void groupSelectActivePreset(int groupId, int presetIndex) {
152         groupSelectActivePresetNative(groupId, presetIndex);
153     }
154 
155     /**
156      * Sets the next preset as a currently active preset for a HA device
157      *
158      * @param device is the device for which we want to set the active preset
159      */
160     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
nextActivePreset(BluetoothDevice device)161     public void nextActivePreset(BluetoothDevice device) {
162         nextActivePresetNative(getByteAddress(device));
163     }
164 
165     /**
166      * Sets the next preset as a currently active preset for a HA device group
167      *
168      * @param groupId is the device group identifier for which want to set the active preset
169      */
170     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
groupNextActivePreset(int groupId)171     public void groupNextActivePreset(int groupId) {
172         groupNextActivePresetNative(groupId);
173     }
174 
175     /**
176      * Sets the previous preset as a currently active preset for a HA device
177      *
178      * @param device is the device for which we want to set the active preset
179      */
180     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
previousActivePreset(BluetoothDevice device)181     public void previousActivePreset(BluetoothDevice device) {
182         previousActivePresetNative(getByteAddress(device));
183     }
184 
185     /**
186      * Sets the previous preset as a currently active preset for a HA device group
187      *
188      * @param groupId is the device group identifier for which want to set the active preset
189      */
190     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
groupPreviousActivePreset(int groupId)191     public void groupPreviousActivePreset(int groupId) {
192         groupPreviousActivePresetNative(groupId);
193     }
194 
195     /**
196      * Requests the preset name
197      *
198      * @param device is the device for which we want to get the preset name
199      * @param presetIndex is an index of one of the available presets
200      */
201     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getPresetInfo(BluetoothDevice device, int presetIndex)202     public void getPresetInfo(BluetoothDevice device, int presetIndex) {
203         getPresetInfoNative(getByteAddress(device), presetIndex);
204     }
205 
206      /**
207      * Sets the preset name
208      *
209      * @param device is the device for which we want to get the preset name
210      * @param presetIndex is an index of one of the available presets
211      * @param name is a new name for a preset
212      */
213     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setPresetName(BluetoothDevice device, int presetIndex, String name)214     public void setPresetName(BluetoothDevice device, int presetIndex, String name) {
215         setPresetNameNative(getByteAddress(device), presetIndex, name);
216     }
217 
218     /**
219      * Sets the preset name
220      *
221      * @param groupId is the device group
222      * @param presetIndex is an index of one of the available presets
223      * @param name is a new name for a preset
224      */
225     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
groupSetPresetName(int groupId, int presetIndex, String name)226     public void groupSetPresetName(int groupId, int presetIndex, String name) {
227         groupSetPresetNameNative(groupId, presetIndex, name);
228     }
229 
230     // Callbacks from the native stack back into the Java framework.
231     // All callbacks are routed via the Service which will disambiguate which
232     // state machine the message should be routed to.
233 
234     @VisibleForTesting
onConnectionStateChanged(int state, byte[] address)235     void onConnectionStateChanged(int state, byte[] address) {
236         HapClientStackEvent event =
237                 new HapClientStackEvent(
238                         HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
239         event.device = getDevice(address);
240         event.valueInt1 = state;
241 
242         if (DBG) {
243             Log.d(TAG, "onConnectionStateChanged: " + event);
244         }
245         sendMessageToService(event);
246     }
247 
248     @VisibleForTesting
onDeviceAvailable(byte[] address, int features)249     void onDeviceAvailable(byte[] address, int features) {
250         HapClientStackEvent event = new HapClientStackEvent(
251                 HapClientStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
252         event.device = getDevice(address);
253         event.valueInt1 = features;
254 
255         if (DBG) {
256             Log.d(TAG, "onDeviceAvailable: " + event);
257         }
258         sendMessageToService(event);
259     }
260 
261     @VisibleForTesting
onFeaturesUpdate(byte[] address, int features)262     void onFeaturesUpdate(byte[] address, int features) {
263         HapClientStackEvent event = new HapClientStackEvent(
264                 HapClientStackEvent.EVENT_TYPE_DEVICE_FEATURES);
265         event.device = getDevice(address);
266         event.valueInt1 = features;
267 
268         if (DBG) {
269             Log.d(TAG, "onFeaturesUpdate: " + event);
270         }
271         sendMessageToService(event);
272     }
273 
274     @VisibleForTesting
onActivePresetSelected(byte[] address, int presetIndex)275     void onActivePresetSelected(byte[] address, int presetIndex) {
276         HapClientStackEvent event = new HapClientStackEvent(
277                 HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECTED);
278         event.device = getDevice(address);
279         event.valueInt1 = presetIndex;
280 
281         if (DBG) {
282             Log.d(TAG, "onActivePresetSelected: " + event);
283         }
284         sendMessageToService(event);
285     }
286 
287     @VisibleForTesting
onActivePresetGroupSelected(int groupId, int presetIndex)288     void onActivePresetGroupSelected(int groupId, int presetIndex) {
289         HapClientStackEvent event = new HapClientStackEvent(
290                 HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECTED);
291         event.valueInt1 = presetIndex;
292         event.valueInt2 = groupId;
293 
294         if (DBG) {
295             Log.d(TAG, "onActivePresetGroupSelected: " + event);
296         }
297         sendMessageToService(event);
298     }
299 
300     @VisibleForTesting
onActivePresetSelectError(byte[] address, int resultCode)301     void onActivePresetSelectError(byte[] address, int resultCode) {
302         HapClientStackEvent event = new HapClientStackEvent(
303                 HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECT_ERROR);
304         event.device = getDevice(address);
305         event.valueInt1 = resultCode;
306 
307         if (DBG) {
308             Log.d(TAG, "onActivePresetSelectError: " + event);
309         }
310         sendMessageToService(event);
311     }
312 
313     @VisibleForTesting
onActivePresetGroupSelectError(int groupId, int resultCode)314     void onActivePresetGroupSelectError(int groupId, int resultCode) {
315         HapClientStackEvent event = new HapClientStackEvent(
316                 HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECT_ERROR);
317         event.valueInt1 = resultCode;
318         event.valueInt2 = groupId;
319 
320         if (DBG) {
321             Log.d(TAG, "onActivePresetGroupSelectError: " + event);
322         }
323         sendMessageToService(event);
324     }
325 
326     @VisibleForTesting
onPresetInfo(byte[] address, int infoReason, BluetoothHapPresetInfo[] presets)327     void onPresetInfo(byte[] address, int infoReason, BluetoothHapPresetInfo[] presets) {
328         HapClientStackEvent event = new HapClientStackEvent(
329                 HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO);
330         event.device = getDevice(address);
331         event.valueInt2 = infoReason;
332         event.valueList = new ArrayList<>(Arrays.asList(presets));
333 
334         if (DBG) {
335             Log.d(TAG, "onPresetInfo: " + event);
336         }
337         sendMessageToService(event);
338     }
339 
340     @VisibleForTesting
onGroupPresetInfo(int groupId, int infoReason, BluetoothHapPresetInfo[] presets)341     void onGroupPresetInfo(int groupId, int infoReason, BluetoothHapPresetInfo[] presets) {
342         HapClientStackEvent event = new HapClientStackEvent(
343                 HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO);
344         event.valueInt2 = infoReason;
345         event.valueInt3 = groupId;
346         event.valueList = new ArrayList<>(Arrays.asList(presets));
347 
348         if (DBG) {
349             Log.d(TAG, "onPresetInfo: " + event);
350         }
351         sendMessageToService(event);
352     }
353 
354     @VisibleForTesting
onPresetNameSetError(byte[] address, int presetIndex, int resultCode)355     void onPresetNameSetError(byte[] address, int presetIndex, int resultCode) {
356         HapClientStackEvent event = new HapClientStackEvent(
357                 HapClientStackEvent.EVENT_TYPE_ON_PRESET_NAME_SET_ERROR);
358         event.device = getDevice(address);
359         event.valueInt1 = resultCode;
360         event.valueInt2 = presetIndex;
361 
362         if (DBG) {
363             Log.d(TAG, "OnPresetNameSetError: " + event);
364         }
365         sendMessageToService(event);
366     }
367 
368     @VisibleForTesting
onGroupPresetNameSetError(int groupId, int presetIndex, int resultCode)369     void onGroupPresetNameSetError(int groupId, int presetIndex, int resultCode) {
370         HapClientStackEvent event = new HapClientStackEvent(
371                 HapClientStackEvent.EVENT_TYPE_ON_PRESET_NAME_SET_ERROR);
372         event.valueInt1 = resultCode;
373         event.valueInt2 = presetIndex;
374         event.valueInt3 = groupId;
375 
376         if (DBG) {
377             Log.d(TAG, "OnPresetNameSetError: " + event);
378         }
379         sendMessageToService(event);
380     }
381 
382     @VisibleForTesting
onPresetInfoError(byte[] address, int presetIndex, int resultCode)383     void onPresetInfoError(byte[] address, int presetIndex, int resultCode) {
384         HapClientStackEvent event = new HapClientStackEvent(
385                 HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO_ERROR);
386         event.device = getDevice(address);
387         event.valueInt1 = resultCode;
388         event.valueInt2 = presetIndex;
389 
390         if (DBG) {
391             Log.d(TAG, "onPresetInfoError: " + event);
392         }
393         sendMessageToService(event);
394     }
395 
396     @VisibleForTesting
onGroupPresetInfoError(int groupId, int presetIndex, int resultCode)397     void onGroupPresetInfoError(int groupId, int presetIndex, int resultCode) {
398         HapClientStackEvent event = new HapClientStackEvent(
399                 HapClientStackEvent.EVENT_TYPE_ON_PRESET_INFO_ERROR);
400         event.valueInt1 = resultCode;
401         event.valueInt2 = presetIndex;
402         event.valueInt3 = groupId;
403 
404         if (DBG) {
405             Log.d(TAG, "onPresetInfoError: " + event);
406         }
407         sendMessageToService(event);
408     }
409 
410     // Native methods that call into the JNI interface
classInitNative()411     private static native void classInitNative();
initNative()412     private native void initNative();
cleanupNative()413     private native void cleanupNative();
connectHapClientNative(byte[] address)414     private native boolean connectHapClientNative(byte[] address);
disconnectHapClientNative(byte[] address)415     private native boolean disconnectHapClientNative(byte[] address);
selectActivePresetNative(byte[] byteAddress, int presetIndex)416     private native void selectActivePresetNative(byte[] byteAddress, int presetIndex);
groupSelectActivePresetNative(int groupId, int presetIndex)417     private native void groupSelectActivePresetNative(int groupId, int presetIndex);
nextActivePresetNative(byte[] byteAddress)418     private native void nextActivePresetNative(byte[] byteAddress);
groupNextActivePresetNative(int groupId)419     private native void groupNextActivePresetNative(int groupId);
previousActivePresetNative(byte[] byteAddress)420     private native void previousActivePresetNative(byte[] byteAddress);
groupPreviousActivePresetNative(int groupId)421     private native void groupPreviousActivePresetNative(int groupId);
getPresetInfoNative(byte[] byteAddress, int presetIndex)422     private native void getPresetInfoNative(byte[] byteAddress, int presetIndex);
setPresetNameNative(byte[] byteAddress, int presetIndex, String name)423     private native void setPresetNameNative(byte[] byteAddress, int presetIndex, String name);
groupSetPresetNameNative(int groupId, int presetIndex, String name)424     private native void groupSetPresetNameNative(int groupId, int presetIndex, String name);
425 }
426