1 /*
2 * Copyright (C) 2012 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 #define LOG_TAG "BluetoothHealthServiceJni"
18
19 #define LOG_NDEBUG 0
20
21 #include "android_runtime/AndroidRuntime.h"
22 #include "com_android_bluetooth.h"
23 #include "hardware/bt_hl.h"
24 #include "utils/Log.h"
25
26 #include <string.h>
27
28 namespace android {
29
30 static jmethodID method_onAppRegistrationState;
31 static jmethodID method_onChannelStateChanged;
32
33 static const bthl_interface_t* sBluetoothHdpInterface = NULL;
34 static jobject mCallbacksObj = NULL;
35
36 // Define callback functions
app_registration_state_callback(int app_id,bthl_app_reg_state_t state)37 static void app_registration_state_callback(int app_id,
38 bthl_app_reg_state_t state) {
39 CallbackEnv sCallbackEnv(__func__);
40 if (!sCallbackEnv.valid()) return;
41 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAppRegistrationState,
42 app_id, (jint)state);
43 }
44
channel_state_callback(int app_id,RawAddress * bd_addr,int mdep_cfg_index,int channel_id,bthl_channel_state_t state,int fd)45 static void channel_state_callback(int app_id, RawAddress* bd_addr,
46 int mdep_cfg_index, int channel_id,
47 bthl_channel_state_t state, int fd) {
48 CallbackEnv sCallbackEnv(__func__);
49 if (!sCallbackEnv.valid()) return;
50 ScopedLocalRef<jbyteArray> addr(
51 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
52 if (!addr.get()) {
53 ALOGE("Fail to new jbyteArray bd addr for channel state");
54 return;
55 }
56
57 // TODO(BT) check if fd is only valid for BTHH_CONN_STATE_CONNECTED state
58 jobject fileDescriptor = NULL;
59 if (state == BTHL_CONN_STATE_CONNECTED) {
60 fileDescriptor = jniCreateFileDescriptor(sCallbackEnv.get(), fd);
61 if (!fileDescriptor) {
62 ALOGE("Failed to convert file descriptor, fd: %d", fd);
63 return;
64 }
65 }
66
67 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
68 (jbyte*)bd_addr);
69 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onChannelStateChanged,
70 app_id, addr.get(), mdep_cfg_index, channel_id,
71 (jint)state, fileDescriptor);
72 }
73
74 static bthl_callbacks_t sBluetoothHdpCallbacks = {
75 sizeof(sBluetoothHdpCallbacks), app_registration_state_callback,
76 channel_state_callback};
77
78 // Define native functions
79
classInitNative(JNIEnv * env,jclass clazz)80 static void classInitNative(JNIEnv* env, jclass clazz) {
81 method_onAppRegistrationState =
82 env->GetMethodID(clazz, "onAppRegistrationState", "(II)V");
83 method_onChannelStateChanged = env->GetMethodID(
84 clazz, "onChannelStateChanged", "(I[BIIILjava/io/FileDescriptor;)V");
85 ALOGI("%s: succeeds", __func__);
86 }
87
initializeNative(JNIEnv * env,jobject object)88 static void initializeNative(JNIEnv* env, jobject object) {
89 const bt_interface_t* btInf = getBluetoothInterface();
90 if (btInf == NULL) {
91 ALOGE("Bluetooth module is not loaded");
92 return;
93 }
94
95 if (sBluetoothHdpInterface != NULL) {
96 ALOGW("Cleaning up Bluetooth Health Interface before initializing...");
97 sBluetoothHdpInterface->cleanup();
98 sBluetoothHdpInterface = NULL;
99 }
100
101 if (mCallbacksObj != NULL) {
102 ALOGW("Cleaning up Bluetooth Health callback object");
103 env->DeleteGlobalRef(mCallbacksObj);
104 mCallbacksObj = NULL;
105 }
106
107 sBluetoothHdpInterface =
108 (bthl_interface_t*)btInf->get_profile_interface(BT_PROFILE_HEALTH_ID);
109 if (sBluetoothHdpInterface == NULL) {
110 ALOGE("Failed to get Bluetooth Health Interface");
111 return;
112 }
113
114 bt_status_t status = sBluetoothHdpInterface->init(&sBluetoothHdpCallbacks);
115 if (status != BT_STATUS_SUCCESS) {
116 ALOGE("Failed to initialize Bluetooth HDP, status: %d", status);
117 sBluetoothHdpInterface = NULL;
118 return;
119 }
120
121 mCallbacksObj = env->NewGlobalRef(object);
122 }
123
cleanupNative(JNIEnv * env,jobject object)124 static void cleanupNative(JNIEnv* env, jobject object) {
125 const bt_interface_t* btInf = getBluetoothInterface();
126
127 if (btInf == NULL) {
128 ALOGE("Bluetooth module is not loaded");
129 return;
130 }
131
132 if (sBluetoothHdpInterface != NULL) {
133 ALOGW("Cleaning up Bluetooth Health Interface...");
134 sBluetoothHdpInterface->cleanup();
135 sBluetoothHdpInterface = NULL;
136 }
137
138 if (mCallbacksObj != NULL) {
139 ALOGW("Cleaning up Bluetooth Health object");
140 env->DeleteGlobalRef(mCallbacksObj);
141 mCallbacksObj = NULL;
142 }
143 }
144
registerHealthAppNative(JNIEnv * env,jobject object,jint data_type,jint role,jstring name,jint channel_type)145 static jint registerHealthAppNative(JNIEnv* env, jobject object, jint data_type,
146 jint role, jstring name,
147 jint channel_type) {
148 if (!sBluetoothHdpInterface) {
149 ALOGE(
150 "Failed to register health app. No Bluetooth Health Interface "
151 "available");
152 return -1;
153 }
154
155 bthl_mdep_cfg_t mdep_cfg;
156 mdep_cfg.mdep_role = (bthl_mdep_role_t)role;
157 mdep_cfg.data_type = data_type;
158 mdep_cfg.channel_type = (bthl_channel_type_t)channel_type;
159 // TODO(BT) pass all the followings in from java instead of reuse name
160 mdep_cfg.mdep_description = env->GetStringUTFChars(name, NULL);
161
162 bthl_reg_param_t reg_param;
163 reg_param.application_name = env->GetStringUTFChars(name, NULL);
164 reg_param.provider_name = NULL;
165 reg_param.srv_name = NULL;
166 reg_param.srv_desp = NULL;
167 reg_param.number_of_mdeps = 1;
168 reg_param.mdep_cfg = &mdep_cfg;
169
170 int app_id;
171 bt_status_t status =
172 sBluetoothHdpInterface->register_application(®_param, &app_id);
173 if (status != BT_STATUS_SUCCESS) {
174 ALOGE("Failed register health app, status: %d", status);
175 return -1;
176 }
177
178 env->ReleaseStringUTFChars(name, mdep_cfg.mdep_description);
179 env->ReleaseStringUTFChars(name, reg_param.application_name);
180 return app_id;
181 }
182
unregisterHealthAppNative(JNIEnv * env,jobject object,int app_id)183 static jboolean unregisterHealthAppNative(JNIEnv* env, jobject object,
184 int app_id) {
185 if (!sBluetoothHdpInterface) return JNI_FALSE;
186
187 bt_status_t status = sBluetoothHdpInterface->unregister_application(app_id);
188 if (status != BT_STATUS_SUCCESS) {
189 ALOGE("Failed to unregister app %d, status: %d", app_id, status);
190 }
191 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
192 }
193
connectChannelNative(JNIEnv * env,jobject object,jbyteArray address,jint app_id)194 static jint connectChannelNative(JNIEnv* env, jobject object,
195 jbyteArray address, jint app_id) {
196 if (!sBluetoothHdpInterface) return -1;
197
198 jbyte* addr = env->GetByteArrayElements(address, NULL);
199 if (!addr) {
200 ALOGE("Bluetooth device address null");
201 return -1;
202 }
203
204 jint chan_id;
205 bt_status_t status = sBluetoothHdpInterface->connect_channel(
206 app_id, (RawAddress*)addr, 0, &chan_id);
207 if (status != BT_STATUS_SUCCESS) {
208 ALOGE("Failed HDP channel connection, status: %d", status);
209 chan_id = -1;
210 }
211 env->ReleaseByteArrayElements(address, addr, 0);
212
213 return chan_id;
214 }
215
disconnectChannelNative(JNIEnv * env,jobject object,jint channel_id)216 static jboolean disconnectChannelNative(JNIEnv* env, jobject object,
217 jint channel_id) {
218 if (!sBluetoothHdpInterface) return JNI_FALSE;
219
220 bt_status_t status = sBluetoothHdpInterface->destroy_channel(channel_id);
221 if (status != BT_STATUS_SUCCESS) {
222 ALOGE("Failed disconnect health channel, status: %d", status);
223 return JNI_FALSE;
224 }
225 return JNI_TRUE;
226 }
227
228 static JNINativeMethod sMethods[] = {
229 {"classInitNative", "()V", (void*)classInitNative},
230 {"initializeNative", "()V", (void*)initializeNative},
231 {"cleanupNative", "()V", (void*)cleanupNative},
232 {"registerHealthAppNative", "(IILjava/lang/String;I)I",
233 (void*)registerHealthAppNative},
234 {"unregisterHealthAppNative", "(I)Z", (void*)unregisterHealthAppNative},
235 {"connectChannelNative", "([BI)I", (void*)connectChannelNative},
236 {"disconnectChannelNative", "(I)Z", (void*)disconnectChannelNative},
237 };
238
register_com_android_bluetooth_hdp(JNIEnv * env)239 int register_com_android_bluetooth_hdp(JNIEnv* env) {
240 return jniRegisterNativeMethods(env,
241 "com/android/bluetooth/hdp/HealthService",
242 sMethods, NELEM(sMethods));
243 }
244 }
245