1 /*
2  * Copyright 2020 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 /*
19  * Defines the native interface that is used by state machine/service to
20  * send or receive messages from the native stack. This file is registered
21  * for the native methods in the corresponding JNI C++ file.
22  */
23 package com.android.bluetooth.le_audio;
24 
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.BluetoothDevice;
27 import android.util.Log;
28 
29 import com.android.bluetooth.Utils;
30 import com.android.internal.annotations.GuardedBy;
31 
32 /**
33  * LeAudio Native Interface to/from JNI.
34  */
35 public class LeAudioNativeInterface {
36     private static final String TAG = "LeAudioNativeInterface";
37     private static final boolean DBG = true;
38     private BluetoothAdapter mAdapter;
39 
40     @GuardedBy("INSTANCE_LOCK")
41     private static LeAudioNativeInterface sInstance;
42     private static final Object INSTANCE_LOCK = new Object();
43 
44     static {
classInitNative()45         classInitNative();
46     }
47 
LeAudioNativeInterface()48     private LeAudioNativeInterface() {
49         mAdapter = BluetoothAdapter.getDefaultAdapter();
50         if (mAdapter == null) {
51             Log.wtfStack(TAG, "No Bluetooth Adapter Available");
52         }
53     }
54 
55     /**
56      * Get singleton instance.
57      */
getInstance()58     public static LeAudioNativeInterface getInstance() {
59         synchronized (INSTANCE_LOCK) {
60             if (sInstance == null) {
61                 sInstance = new LeAudioNativeInterface();
62             }
63             return sInstance;
64         }
65     }
66 
getByteAddress(BluetoothDevice device)67     private byte[] getByteAddress(BluetoothDevice device) {
68         if (device == null) {
69             return Utils.getBytesFromAddress("00:00:00:00:00:00");
70         }
71         return Utils.getBytesFromAddress(device.getAddress());
72     }
73 
sendMessageToService(LeAudioStackEvent event)74     private void sendMessageToService(LeAudioStackEvent event) {
75         LeAudioService service = LeAudioService.getLeAudioService();
76         if (service != null) {
77             service.messageFromNative(event);
78         } else {
79             Log.e(TAG, "Event ignored, service not available: " + event);
80         }
81     }
82 
getDevice(byte[] address)83     private BluetoothDevice getDevice(byte[] address) {
84         return mAdapter.getRemoteDevice(address);
85     }
86 
87     // Callbacks from the native stack back into the Java framework.
88     // All callbacks are routed via the Service which will disambiguate which
89     // state machine the message should be routed to.
onConnectionStateChanged(int state, byte[] address)90     private void onConnectionStateChanged(int state, byte[] address) {
91         LeAudioStackEvent event =
92                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
93         event.device = getDevice(address);
94         event.valueInt1 = state;
95 
96         if (DBG) {
97             Log.d(TAG, "onConnectionStateChanged: " + event);
98         }
99         sendMessageToService(event);
100     }
101 
102     // Callbacks from the native stack back into the Java framework.
103     // All callbacks are routed via the Service which will disambiguate which
104     // state machine the message should be routed to.
onSetMemberAvailable(byte[] address, int groupId)105     private void onSetMemberAvailable(byte[] address, int groupId) {
106         LeAudioStackEvent event =
107                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_SET_MEMBER_AVAILABLE);
108         event.device = getDevice(address);
109         event.valueInt1 = groupId;
110         if (DBG) {
111             Log.d(TAG, "onSetMemberAvailable: " + event);
112         }
113         sendMessageToService(event);
114     }
115 
onGroupStatus(int groupId, int groupStatus, int groupFlags)116     private void onGroupStatus(int groupId, int groupStatus, int groupFlags) {
117         LeAudioStackEvent event =
118                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
119         event.valueInt1 = groupId;
120         event.valueInt2 = groupStatus;
121         event.valueInt3 = groupFlags;
122         event.device = null;
123 
124         if (DBG) {
125             Log.d(TAG, "onGroupStatus: " + event);
126         }
127         sendMessageToService(event);
128     }
129 
onAudioConf(int direction, int groupId, int sinkAudioLocation, int sourceAudioLocation, byte[] address)130     private void onAudioConf(int direction, int groupId, int sinkAudioLocation,
131                              int sourceAudioLocation, byte[] address) {
132         LeAudioStackEvent event =
133                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
134         event.valueInt1 = direction;
135         event.valueInt2 = groupId;
136         event.valueInt3 = sinkAudioLocation;
137         event.valueInt4 = sourceAudioLocation;
138         event.device = getDevice(address);
139 
140         if (DBG) {
141             Log.d(TAG, "onAudioConf: " + event);
142         }
143         sendMessageToService(event);
144     }
145 
146     /**
147      * Initializes the native interface.
148      *
149      * priorities to configure.
150      */
init()151     public void init() {
152         initNative();
153     }
154 
155     /**
156      * Cleanup the native interface.
157      */
cleanup()158     public void cleanup() {
159         cleanupNative();
160     }
161 
162     /**
163      * Initiates LeAudio connection to a remote device.
164      *
165      * @param device the remote device
166      * @return true on success, otherwise false.
167      */
connectLeAudio(BluetoothDevice device)168     public boolean connectLeAudio(BluetoothDevice device) {
169         return connectLeAudioNative(getByteAddress(device));
170     }
171 
172     /**
173      * Disconnects LeAudio from a remote device.
174      *
175      * @param device the remote device
176      * @return true on success, otherwise false.
177      */
disconnectLeAudio(BluetoothDevice device)178     public boolean disconnectLeAudio(BluetoothDevice device) {
179         return disconnectLeAudioNative(getByteAddress(device));
180     }
181 
182     /**
183      * Enable content streaming.
184      * @param groupId group identifier
185      * @param contentType type of content to stream
186      */
groupStream(int groupId, int contentType)187     public void groupStream(int groupId, int contentType) {
188         groupStreamNative(groupId, contentType);
189     }
190 
191     /**
192      * Suspend content streaming.
193      * @param groupId  group identifier
194      */
groupSuspend(int groupId)195     public void groupSuspend(int groupId) {
196         groupSuspendNative(groupId);
197     }
198 
199     /**
200      * Stop all content streaming.
201      * @param groupId  group identifier
202      * TODO: Maybe we should use also pass the content type argument
203      */
groupStop(int groupId)204     public void groupStop(int groupId) {
205         groupStopNative(groupId);
206     }
207 
208     // Native methods that call into the JNI interface
classInitNative()209     private static native void classInitNative();
initNative()210     private native void initNative();
cleanupNative()211     private native void cleanupNative();
connectLeAudioNative(byte[] address)212     private native boolean connectLeAudioNative(byte[] address);
disconnectLeAudioNative(byte[] address)213     private native boolean disconnectLeAudioNative(byte[] address);
groupStreamNative(int groupId, int contentType)214     private native void groupStreamNative(int groupId, int contentType);
groupSuspendNative(int groupId)215     private native void groupSuspendNative(int groupId);
groupStopNative(int groupId)216     private native void groupStopNative(int groupId);
217 }
218