• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&reg_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