1 /*
2  * Copyright 2018 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 interface 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.hearingaid;
23 
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothDevice;
26 import android.util.Log;
27 
28 import com.android.bluetooth.Utils;
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 /**
33  * HearingAid Native Interface to/from JNI.
34  */
35 public class HearingAidNativeInterface {
36     private static final String TAG = "HearingAidNativeInterface";
37     private static final boolean DBG = true;
38     private BluetoothAdapter mAdapter;
39 
40     @GuardedBy("INSTANCE_LOCK")
41     private static HearingAidNativeInterface sInstance;
42     private static final Object INSTANCE_LOCK = new Object();
43 
44     static {
classInitNative()45         classInitNative();
46     }
47 
HearingAidNativeInterface()48     private HearingAidNativeInterface() {
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 HearingAidNativeInterface getInstance() {
59         synchronized (INSTANCE_LOCK) {
60             if (sInstance == null) {
61                 sInstance = new HearingAidNativeInterface();
62             }
63             return sInstance;
64         }
65     }
66 
67     /**
68      * Initializes the native interface.
69      *
70      * priorities to configure.
71      */
72     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
init()73     public void init() {
74         initNative();
75     }
76 
77     /**
78      * Cleanup the native interface.
79      */
80     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
cleanup()81     public void cleanup() {
82         cleanupNative();
83     }
84 
85     /**
86      * Initiates HearingAid connection to a remote device.
87      *
88      * @param device the remote device
89      * @return true on success, otherwise false.
90      */
91     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
connectHearingAid(BluetoothDevice device)92     public boolean connectHearingAid(BluetoothDevice device) {
93         return connectHearingAidNative(getByteAddress(device));
94     }
95 
96     /**
97      * Disconnects HearingAid from a remote device.
98      *
99      * @param device the remote device
100      * @return true on success, otherwise false.
101      */
102     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
disconnectHearingAid(BluetoothDevice device)103     public boolean disconnectHearingAid(BluetoothDevice device) {
104         return disconnectHearingAidNative(getByteAddress(device));
105     }
106 
107     /**
108      * Add a hearing aid device to white list.
109      *
110      * @param device the remote device
111      * @return true on success, otherwise false.
112      */
113     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
addToWhiteList(BluetoothDevice device)114     public boolean addToWhiteList(BluetoothDevice device) {
115         return addToWhiteListNative(getByteAddress(device));
116     }
117 
118     /**
119      * Sets the HearingAid volume
120      * @param volume
121      */
122     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setVolume(int volume)123     public void setVolume(int volume) {
124         setVolumeNative(volume);
125     }
126 
getDevice(byte[] address)127     private BluetoothDevice getDevice(byte[] address) {
128         return mAdapter.getRemoteDevice(address);
129     }
130 
getByteAddress(BluetoothDevice device)131     private byte[] getByteAddress(BluetoothDevice device) {
132         if (device == null) {
133             return Utils.getBytesFromAddress("00:00:00:00:00:00");
134         }
135         return Utils.getBytesFromAddress(device.getAddress());
136     }
137 
sendMessageToService(HearingAidStackEvent event)138     private void sendMessageToService(HearingAidStackEvent event) {
139         HearingAidService service = HearingAidService.getHearingAidService();
140         if (service != null) {
141             service.messageFromNative(event);
142         } else {
143             Log.e(TAG, "Event ignored, service not available: " + event);
144         }
145     }
146 
147     // Callbacks from the native stack back into the Java framework.
148     // All callbacks are routed via the Service which will disambiguate which
149     // state machine the message should be routed to.
150 
onConnectionStateChanged(int state, byte[] address)151     private void onConnectionStateChanged(int state, byte[] address) {
152         HearingAidStackEvent event =
153                 new HearingAidStackEvent(HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
154         event.device = getDevice(address);
155         event.valueInt1 = state;
156 
157         if (DBG) {
158             Log.d(TAG, "onConnectionStateChanged: " + event);
159         }
160         sendMessageToService(event);
161     }
162 
onDeviceAvailable(byte capabilities, long hiSyncId, byte[] address)163     private void onDeviceAvailable(byte capabilities, long hiSyncId, byte[] address) {
164         HearingAidStackEvent event = new HearingAidStackEvent(
165                 HearingAidStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
166         event.device = getDevice(address);
167         event.valueInt1 = capabilities;
168         event.valueLong2 = hiSyncId;
169 
170         if (DBG) {
171             Log.d(TAG, "onDeviceAvailable: " + event);
172         }
173         sendMessageToService(event);
174     }
175 
176     // Native methods that call into the JNI interface
classInitNative()177     private static native void classInitNative();
initNative()178     private native void initNative();
cleanupNative()179     private native void cleanupNative();
connectHearingAidNative(byte[] address)180     private native boolean connectHearingAidNative(byte[] address);
disconnectHearingAidNative(byte[] address)181     private native boolean disconnectHearingAidNative(byte[] address);
addToWhiteListNative(byte[] address)182     private native boolean addToWhiteListNative(byte[] address);
setVolumeNative(int volume)183     private native void setVolumeNative(int volume);
184 }
185