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