1 /*
2 ** Copyright 2008, 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 "bluetooth_ScoSocket.cpp"
18
19 #include "android_bluetooth_common.h"
20 #include "android_runtime/AndroidRuntime.h"
21 #include "JNIHelp.h"
22 #include "jni.h"
23 #include "utils/Log.h"
24 #include "utils/misc.h"
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <pthread.h>
32 #include <sys/socket.h>
33 #include <sys/types.h>
34 #include <sys/uio.h>
35 #include <sys/poll.h>
36
37 #ifdef HAVE_BLUETOOTH
38 #include <bluetooth/bluetooth.h>
39 #include <bluetooth/sco.h>
40 #endif
41
42 /* Ideally, blocking I/O on a SCO socket would return when another thread
43 * calls close(). However it does not right now, in fact close() on a SCO
44 * socket has strange behavior (returns a bogus value) when other threads
45 * are performing blocking I/O on that socket. So, to workaround, we always
46 * call close() from the same thread that does blocking I/O. This requires the
47 * use of a socketpair to signal the blocking I/O to abort.
48 *
49 * Unfortunately I don't know a way to abort connect() yet, but at least this
50 * times out after the BT page timeout (10 seconds currently), so the thread
51 * will die eventually. The fact that the thread can outlive
52 * the Java object forces us to use a mutex in destoryNative().
53 *
54 * The JNI API is entirely async.
55 *
56 * Also note this class deals only with SCO connections, not with data
57 * transmission.
58 */
59 namespace android {
60 #ifdef HAVE_BLUETOOTH
61
62 static JavaVM *jvm;
63 static jfieldID field_mNativeData;
64 static jmethodID method_onAccepted;
65 static jmethodID method_onConnected;
66 static jmethodID method_onClosed;
67
68 struct thread_data_t;
69 static void *work_thread(void *arg);
70 static int connect_work(const char *address);
71 static int accept_work(int signal_sk);
72 static void wait_for_close(int sk, int signal_sk);
73 static void closeNative(JNIEnv *env, jobject object);
74
75 /* shared native data - protected by mutex */
76 typedef struct {
77 pthread_mutex_t mutex;
78 int signal_sk; // socket to signal blocked I/O to unblock
79 jobject object; // JNI global ref to the Java object
80 thread_data_t *thread_data; // pointer to thread local data
81 // max 1 thread per sco socket
82 } native_data_t;
83
84 /* thread local data */
85 struct thread_data_t {
86 native_data_t *nat;
87 bool is_accept; // accept (listening) or connect (outgoing) thread
88 int signal_sk; // socket for thread to listen for unblock signal
89 char address[BTADDR_SIZE]; // BT addres as string
90 };
91
get_native_data(JNIEnv * env,jobject object)92 static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
93 return (native_data_t *)(env->GetIntField(object, field_mNativeData));
94 }
95 #endif
96
classInitNative(JNIEnv * env,jclass clazz)97 static void classInitNative(JNIEnv* env, jclass clazz) {
98 LOGV(__FUNCTION__);
99 #ifdef HAVE_BLUETOOTH
100 if (env->GetJavaVM(&jvm) < 0) {
101 LOGE("Could not get handle to the VM");
102 }
103 field_mNativeData = get_field(env, clazz, "mNativeData", "I");
104 method_onAccepted = env->GetMethodID(clazz, "onAccepted", "(I)V");
105 method_onConnected = env->GetMethodID(clazz, "onConnected", "(I)V");
106 method_onClosed = env->GetMethodID(clazz, "onClosed", "()V");
107 #endif
108 }
109
110 /* Returns false if a serious error occured */
initNative(JNIEnv * env,jobject object)111 static jboolean initNative(JNIEnv* env, jobject object) {
112 LOGV(__FUNCTION__);
113 #ifdef HAVE_BLUETOOTH
114
115 native_data_t *nat = (native_data_t *) calloc(1, sizeof(native_data_t));
116 if (nat == NULL) {
117 LOGE("%s: out of memory!", __FUNCTION__);
118 return JNI_FALSE;
119 }
120
121 pthread_mutex_init(&nat->mutex, NULL);
122 env->SetIntField(object, field_mNativeData, (jint)nat);
123 nat->signal_sk = -1;
124 nat->object = NULL;
125 nat->thread_data = NULL;
126
127 #endif
128 return JNI_TRUE;
129 }
130
destroyNative(JNIEnv * env,jobject object)131 static void destroyNative(JNIEnv* env, jobject object) {
132 LOGV(__FUNCTION__);
133 #ifdef HAVE_BLUETOOTH
134 native_data_t *nat = get_native_data(env, object);
135
136 closeNative(env, object);
137
138 pthread_mutex_lock(&nat->mutex);
139 if (nat->thread_data != NULL) {
140 nat->thread_data->nat = NULL;
141 }
142 pthread_mutex_unlock(&nat->mutex);
143 pthread_mutex_destroy(&nat->mutex);
144
145 free(nat);
146 #endif
147 }
148
acceptNative(JNIEnv * env,jobject object)149 static jboolean acceptNative(JNIEnv *env, jobject object) {
150 LOGV(__FUNCTION__);
151 #ifdef HAVE_BLUETOOTH
152 native_data_t *nat = get_native_data(env, object);
153 int signal_sks[2];
154 pthread_t thread;
155 struct thread_data_t *data = NULL;
156
157 pthread_mutex_lock(&nat->mutex);
158 if (nat->signal_sk != -1) {
159 pthread_mutex_unlock(&nat->mutex);
160 return JNI_FALSE;
161 }
162
163 // setup socketpair to pass messages between threads
164 if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) {
165 LOGE("%s: socketpair() failed: %s", __FUNCTION__, strerror(errno));
166 pthread_mutex_unlock(&nat->mutex);
167 return JNI_FALSE;
168 }
169 nat->signal_sk = signal_sks[0];
170 nat->object = env->NewGlobalRef(object);
171
172 data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
173 if (data == NULL) {
174 LOGE("%s: out of memory", __FUNCTION__);
175 pthread_mutex_unlock(&nat->mutex);
176 return JNI_FALSE;
177 }
178 nat->thread_data = data;
179 pthread_mutex_unlock(&nat->mutex);
180
181 data->signal_sk = signal_sks[1];
182 data->nat = nat;
183 data->is_accept = true;
184
185 if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
186 LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
187 return JNI_FALSE;
188 }
189 return JNI_TRUE;
190
191 #endif
192 return JNI_FALSE;
193 }
194
connectNative(JNIEnv * env,jobject object,jstring address)195 static jboolean connectNative(JNIEnv *env, jobject object, jstring address) {
196 LOGV(__FUNCTION__);
197 #ifdef HAVE_BLUETOOTH
198 native_data_t *nat = get_native_data(env, object);
199 int signal_sks[2];
200 pthread_t thread;
201 struct thread_data_t *data;
202 const char *c_address;
203
204 pthread_mutex_lock(&nat->mutex);
205 if (nat->signal_sk != -1) {
206 pthread_mutex_unlock(&nat->mutex);
207 return JNI_FALSE;
208 }
209
210 // setup socketpair to pass messages between threads
211 if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) {
212 LOGE("%s: socketpair() failed: %s\n", __FUNCTION__, strerror(errno));
213 pthread_mutex_unlock(&nat->mutex);
214 return JNI_FALSE;
215 }
216 nat->signal_sk = signal_sks[0];
217 nat->object = env->NewGlobalRef(object);
218
219 data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
220 if (data == NULL) {
221 LOGE("%s: out of memory", __FUNCTION__);
222 pthread_mutex_unlock(&nat->mutex);
223 return JNI_FALSE;
224 }
225 pthread_mutex_unlock(&nat->mutex);
226
227 data->signal_sk = signal_sks[1];
228 data->nat = nat;
229 c_address = env->GetStringUTFChars(address, NULL);
230 strlcpy(data->address, c_address, BTADDR_SIZE);
231 env->ReleaseStringUTFChars(address, c_address);
232 data->is_accept = false;
233
234 if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
235 LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
236 return JNI_FALSE;
237 }
238 return JNI_TRUE;
239
240 #endif
241 return JNI_FALSE;
242 }
243
closeNative(JNIEnv * env,jobject object)244 static void closeNative(JNIEnv *env, jobject object) {
245 LOGV(__FUNCTION__);
246 #ifdef HAVE_BLUETOOTH
247 native_data_t *nat = get_native_data(env, object);
248 int signal_sk;
249
250 pthread_mutex_lock(&nat->mutex);
251 signal_sk = nat->signal_sk;
252 nat->signal_sk = -1;
253 env->DeleteGlobalRef(nat->object);
254 nat->object = NULL;
255 pthread_mutex_unlock(&nat->mutex);
256
257 if (signal_sk >= 0) {
258 LOGV("%s: signal_sk = %d", __FUNCTION__, signal_sk);
259 unsigned char dummy;
260 write(signal_sk, &dummy, sizeof(dummy));
261 close(signal_sk);
262 }
263 #endif
264 }
265
266 #ifdef HAVE_BLUETOOTH
267 /* thread entry point */
work_thread(void * arg)268 static void *work_thread(void *arg) {
269 JNIEnv* env;
270 thread_data_t *data = (thread_data_t *)arg;
271 int sk;
272
273 LOGV(__FUNCTION__);
274 if (jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
275 LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
276 return NULL;
277 }
278
279 /* connect the SCO socket */
280 if (data->is_accept) {
281 LOGV("SCO OBJECT %p ACCEPT #####", data->nat->object);
282 sk = accept_work(data->signal_sk);
283 LOGV("SCO OBJECT %p END ACCEPT *****", data->nat->object);
284 } else {
285 sk = connect_work(data->address);
286 }
287
288 /* callback with connection result */
289 if (data->nat == NULL) {
290 LOGV("%s: object destroyed!", __FUNCTION__);
291 goto done;
292 }
293 pthread_mutex_lock(&data->nat->mutex);
294 if (data->nat->object == NULL) {
295 pthread_mutex_unlock(&data->nat->mutex);
296 LOGV("%s: callback cancelled", __FUNCTION__);
297 goto done;
298 }
299 if (data->is_accept) {
300 env->CallVoidMethod(data->nat->object, method_onAccepted, sk);
301 } else {
302 env->CallVoidMethod(data->nat->object, method_onConnected, sk);
303 }
304 pthread_mutex_unlock(&data->nat->mutex);
305
306 if (sk < 0) {
307 goto done;
308 }
309
310 LOGV("SCO OBJECT %p %d CONNECTED +++ (%s)", data->nat->object, sk,
311 data->is_accept ? "in" : "out");
312
313 /* wait for the socket to close */
314 LOGV("wait_for_close()...");
315 wait_for_close(sk, data->signal_sk);
316 LOGV("wait_for_close() returned");
317
318 /* callback with close result */
319 if (data->nat == NULL) {
320 LOGV("%s: object destroyed!", __FUNCTION__);
321 goto done;
322 }
323 pthread_mutex_lock(&data->nat->mutex);
324 if (data->nat->object == NULL) {
325 LOGV("%s: callback cancelled", __FUNCTION__);
326 } else {
327 env->CallVoidMethod(data->nat->object, method_onClosed);
328 }
329 pthread_mutex_unlock(&data->nat->mutex);
330
331 done:
332 if (sk >= 0) {
333 close(sk);
334 LOGV("SCO OBJECT %p %d CLOSED --- (%s)", data->nat->object, sk, data->is_accept ? "in" : "out");
335 }
336 if (data->signal_sk >= 0) {
337 close(data->signal_sk);
338 }
339 LOGV("SCO socket closed");
340
341 if (data->nat != NULL) {
342 pthread_mutex_lock(&data->nat->mutex);
343 env->DeleteGlobalRef(data->nat->object);
344 data->nat->object = NULL;
345 data->nat->thread_data = NULL;
346 pthread_mutex_unlock(&data->nat->mutex);
347 }
348
349 free(data);
350 if (jvm->DetachCurrentThread() != JNI_OK) {
351 LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
352 }
353
354 LOGV("work_thread() done");
355 return NULL;
356 }
357
accept_work(int signal_sk)358 static int accept_work(int signal_sk) {
359 LOGV(__FUNCTION__);
360 int sk;
361 int nsk;
362 int addr_sz;
363 int max_fd;
364 fd_set fds;
365 struct sockaddr_sco addr;
366
367 sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
368 if (sk < 0) {
369 LOGE("%s socket() failed: %s", __FUNCTION__, strerror(errno));
370 return -1;
371 }
372
373 memset(&addr, 0, sizeof(addr));
374 addr.sco_family = AF_BLUETOOTH;
375 memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
376 if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
377 LOGE("%s bind() failed: %s", __FUNCTION__, strerror(errno));
378 goto error;
379 }
380
381 if (listen(sk, 1)) {
382 LOGE("%s: listen() failed: %s", __FUNCTION__, strerror(errno));
383 goto error;
384 }
385
386 memset(&addr, 0, sizeof(addr));
387 addr_sz = sizeof(addr);
388
389 FD_ZERO(&fds);
390 FD_SET(sk, &fds);
391 FD_SET(signal_sk, &fds);
392
393 max_fd = (sk > signal_sk) ? sk : signal_sk;
394 LOGI("Listening SCO socket...");
395 while (select(max_fd + 1, &fds, NULL, NULL, NULL) < 0) {
396 if (errno != EINTR) {
397 LOGE("%s: select() failed: %s", __FUNCTION__, strerror(errno));
398 goto error;
399 }
400 LOGV("%s: select() EINTR, retrying", __FUNCTION__);
401 }
402 LOGV("select() returned");
403 if (FD_ISSET(signal_sk, &fds)) {
404 // signal to cancel listening
405 LOGV("cancelled listening socket, closing");
406 goto error;
407 }
408 if (!FD_ISSET(sk, &fds)) {
409 LOGE("error: select() returned >= 0 with no fds set");
410 goto error;
411 }
412
413 nsk = accept(sk, (struct sockaddr *)&addr, &addr_sz);
414 if (nsk < 0) {
415 LOGE("%s: accept() failed: %s", __FUNCTION__, strerror(errno));
416 goto error;
417 }
418 LOGI("Connected SCO socket (incoming)");
419 close(sk); // The listening socket
420
421 return nsk;
422
423 error:
424 close(sk);
425
426 return -1;
427 }
428
connect_work(const char * address)429 static int connect_work(const char *address) {
430 LOGV(__FUNCTION__);
431 struct sockaddr_sco addr;
432 int sk = -1;
433
434 sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
435 if (sk < 0) {
436 LOGE("%s: socket() failed: %s", __FUNCTION__, strerror(errno));
437 return -1;
438 }
439
440 /* Bind to local address */
441 memset(&addr, 0, sizeof(addr));
442 addr.sco_family = AF_BLUETOOTH;
443 memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
444 if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
445 LOGE("%s: bind() failed: %s", __FUNCTION__, strerror(errno));
446 goto error;
447 }
448
449 memset(&addr, 0, sizeof(addr));
450 addr.sco_family = AF_BLUETOOTH;
451 get_bdaddr(address, &addr.sco_bdaddr);
452 LOGI("Connecting to socket");
453 while (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
454 if (errno != EINTR) {
455 LOGE("%s: connect() failed: %s", __FUNCTION__, strerror(errno));
456 goto error;
457 }
458 LOGV("%s: connect() EINTR, retrying", __FUNCTION__);
459 }
460 LOGI("SCO socket connected (outgoing)");
461
462 return sk;
463
464 error:
465 if (sk >= 0) close(sk);
466 return -1;
467 }
468
wait_for_close(int sk,int signal_sk)469 static void wait_for_close(int sk, int signal_sk) {
470 LOGV(__FUNCTION__);
471 pollfd p[2];
472
473 memset(p, 0, 2 * sizeof(pollfd));
474 p[0].fd = sk;
475 p[1].fd = signal_sk;
476 p[1].events = POLLIN | POLLPRI;
477
478 LOGV("poll...");
479
480 while (poll(p, 2, -1) < 0) { // blocks
481 if (errno != EINTR) {
482 LOGE("%s: poll() failed: %s", __FUNCTION__, strerror(errno));
483 break;
484 }
485 LOGV("%s: poll() EINTR, retrying", __FUNCTION__);
486 }
487
488 LOGV("poll() returned");
489 }
490 #endif
491
492 static JNINativeMethod sMethods[] = {
493 {"classInitNative", "()V", (void*)classInitNative},
494 {"initNative", "()V", (void *)initNative},
495 {"destroyNative", "()V", (void *)destroyNative},
496 {"connectNative", "(Ljava/lang/String;)Z", (void *)connectNative},
497 {"acceptNative", "()Z", (void *)acceptNative},
498 {"closeNative", "()V", (void *)closeNative},
499 };
500
register_android_bluetooth_ScoSocket(JNIEnv * env)501 int register_android_bluetooth_ScoSocket(JNIEnv *env) {
502 return AndroidRuntime::registerNativeMethods(env,
503 "android/bluetooth/ScoSocket", sMethods, NELEM(sMethods));
504 }
505
506 } /* namespace android */
507