2 ** Copyright 2008, The Android Open Source Project
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
8 ** http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "bluetooth_ScoSocket.cpp"
19 #include "android_bluetooth_common.h"
20 #include "android_runtime/AndroidRuntime.h"
23 #include "utils/Log.h"
24 #include "utils/misc.h"
32 #include <sys/socket.h>
33 #include <sys/types.h>
38 #include <bluetooth/bluetooth.h>
39 #include <bluetooth/sco.h>
40 #include <bluetooth/hci.h>
45 * Defines the module strings used in the blacklist file.
46 * These are used by consumers of the blacklist file to see if the line is
47 * used by that module.
49 #define SCO_BLACKLIST_MODULE_NAME "scoSocket"
52 /* Define the type strings used in the blacklist file. */
53 #define BLACKLIST_BY_NAME "name"
54 #define BLACKLIST_BY_PARTIAL_NAME "partial_name"
55 #define BLACKLIST_BY_OUI "vendor_oui"
59 /* Ideally, blocking I/O on a SCO socket would return when another thread
60 * calls close(). However it does not right now, in fact close() on a SCO
61 * socket has strange behavior (returns a bogus value) when other threads
62 * are performing blocking I/O on that socket. So, to workaround, we always
63 * call close() from the same thread that does blocking I/O. This requires the
64 * use of a socketpair to signal the blocking I/O to abort.
66 * Unfortunately I don't know a way to abort connect() yet, but at least this
67 * times out after the BT page timeout (10 seconds currently), so the thread
68 * will die eventually. The fact that the thread can outlive
69 * the Java object forces us to use a mutex in destoryNative().
71 * The JNI API is entirely async.
73 * Also note this class deals only with SCO connections, not with data
80 static jfieldID field_mNativeData;
81 static jmethodID method_onAccepted;
82 static jmethodID method_onConnected;
83 static jmethodID method_onClosed;
86 static void *work_thread(void *arg);
87 static int connect_work(const char *address, uint16_t sco_pkt_type);
88 static int accept_work(int signal_sk);
89 static void wait_for_close(int sk, int signal_sk);
90 static void closeNative(JNIEnv *env, jobject object);
92 static void parseBlacklist(void);
93 static uint16_t getScoType(char *address, const char *name);
95 #define COMPARE_STRING(key, s) (!strncmp(key, s, strlen(s)))
98 typedef struct scoBlacklist {
102 struct scoBlacklist *next;
105 #define BL_TYPE_NAME 1 // Field type is name string
107 static scoBlacklist_t *blacklist = NULL;
109 /* shared native data - protected by mutex */
111 pthread_mutex_t mutex;
112 int signal_sk; // socket to signal blocked I/O to unblock
113 jobject object; // JNI global ref to the Java object
114 thread_data_t *thread_data; // pointer to thread local data
115 // max 1 thread per sco socket
118 /* thread local data */
119 struct thread_data_t {
121 bool is_accept; // accept (listening) or connect (outgoing) thread
122 int signal_sk; // socket for thread to listen for unblock signal
123 char address[BTADDR_SIZE]; // BT addres as string
124 uint16_t sco_pkt_type; // SCO packet types supported
127 static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
128 return (native_data_t *)(env->GetIntField(object, field_mNativeData));
131 static uint16_t str2scoType (char *key) {
132 LOGV("%s: key = %s", __FUNCTION__, key);
133 if (COMPARE_STRING(key, "ESCO_HV1"))
135 if (COMPARE_STRING(key, "ESCO_HV2"))
137 if (COMPARE_STRING(key, "ESCO_HV3"))
139 if (COMPARE_STRING(key, "ESCO_EV3"))
141 if (COMPARE_STRING(key, "ESCO_EV4"))
143 if (COMPARE_STRING(key, "ESCO_EV5"))
145 if (COMPARE_STRING(key, "ESCO_2EV3"))
147 if (COMPARE_STRING(key, "ESCO_3EV3"))
149 if (COMPARE_STRING(key, "ESCO_2EV5"))
151 if (COMPARE_STRING(key, "ESCO_3EV5"))
153 if (COMPARE_STRING(key, "SCO_ESCO_MASK"))
154 return SCO_ESCO_MASK;
155 if (COMPARE_STRING(key, "EDR_ESCO_MASK"))
156 return EDR_ESCO_MASK;
157 if (COMPARE_STRING(key, "ALL_ESCO_MASK"))
158 return ALL_ESCO_MASK;
159 LOGE("Unknown SCO Type (%s) skipping",key);
163 static void parseBlacklist(void) {
164 const char *filename = "/etc/bluetooth/blacklist.conf";
166 scoBlacklist_t *list = NULL;
167 scoBlacklist_t *newelem;
172 FILE *fp = fopen(filename, "r");
174 LOGE("Error(%s)opening blacklist file", strerror(errno));
178 while (fgets(line, MAX_LINE, fp) != NULL) {
179 if ((COMPARE_STRING(line, "//")) || (!strcmp(line, "")))
181 char *module = strtok(line,":");
182 if (COMPARE_STRING(module, SCO_BLACKLIST_MODULE_NAME)) {
183 newelem = (scoBlacklist_t *)calloc(1, sizeof(scoBlacklist_t));
184 if (newelem == NULL) {
185 LOGE("%s: out of memory!", __FUNCTION__);
189 char *type = strtok(NULL, ",");
190 char *valueList = strtok(NULL, ",");
191 char *paramList = strtok(NULL, ",");
192 if (COMPARE_STRING(type, BLACKLIST_BY_NAME)) {
193 // Extract Name from Value list
194 newelem->fieldType = BL_TYPE_NAME;
195 newelem->value = (char *)calloc(1, strlen(valueList));
196 if (newelem->value == NULL) {
197 LOGE("%s: out of memory!", __FUNCTION__);
200 valueList++; // Skip open quote
201 strncpy(newelem->value, valueList, strlen(valueList) - 1);
203 // Get Sco Settings from Parameters
204 char *param = strtok(paramList, ";");
205 uint16_t scoTypes = 0;
206 while (param != NULL) {
208 if (param[0] == '-') {
210 sco = str2scoType(param);
213 } else if (param[0] == '+') {
215 sco = str2scoType(param);
218 } else if (param[0] == '=') {
220 sco = str2scoType(param);
224 LOGE("Invalid SCO type must be =, + or -");
226 param = strtok(NULL, ";");
228 newelem->scoType = scoTypes;
230 LOGE("Unknown SCO type entry in Blacklist file");
234 list->next = newelem;
237 blacklist = list = newelem;
239 LOGI("Entry name = %s ScoTypes = 0x%x", newelem->value,
246 static uint16_t getScoType(char *address, const char *name) {
248 scoBlacklist_t *list = blacklist;
250 while (list != NULL) {
251 if (list->fieldType == BL_TYPE_NAME) {
252 if (COMPARE_STRING(name, list->value)) {
259 LOGI("%s %s - 0x%x", __FUNCTION__, name, ret);
264 static void classInitNative(JNIEnv* env, jclass clazz) {
266 #ifdef HAVE_BLUETOOTH
267 if (env->GetJavaVM(&jvm) < 0) {
268 LOGE("Could not get handle to the VM");
270 field_mNativeData = get_field(env, clazz, "mNativeData", "I");
271 method_onAccepted = env->GetMethodID(clazz, "onAccepted", "(I)V");
272 method_onConnected = env->GetMethodID(clazz, "onConnected", "(I)V");
273 method_onClosed = env->GetMethodID(clazz, "onClosed", "()V");
275 /* Read the blacklist file in here */
280 /* Returns false if a serious error occured */
281 static jboolean initNative(JNIEnv* env, jobject object) {
283 #ifdef HAVE_BLUETOOTH
285 native_data_t *nat = (native_data_t *) calloc(1, sizeof(native_data_t));
287 LOGE("%s: out of memory!", __FUNCTION__);
291 pthread_mutex_init(&nat->mutex, NULL);
292 env->SetIntField(object, field_mNativeData, (jint)nat);
295 nat->thread_data = NULL;
301 static void destroyNative(JNIEnv* env, jobject object) {
303 #ifdef HAVE_BLUETOOTH
304 native_data_t *nat = get_native_data(env, object);
306 closeNative(env, object);
308 pthread_mutex_lock(&nat->mutex);
309 if (nat->thread_data != NULL) {
310 nat->thread_data->nat = NULL;
312 pthread_mutex_unlock(&nat->mutex);
313 pthread_mutex_destroy(&nat->mutex);
319 static jboolean acceptNative(JNIEnv *env, jobject object) {
321 #ifdef HAVE_BLUETOOTH
322 native_data_t *nat = get_native_data(env, object);
325 struct thread_data_t *data = NULL;
327 pthread_mutex_lock(&nat->mutex);
328 if (nat->signal_sk != -1) {
329 pthread_mutex_unlock(&nat->mutex);
333 // setup socketpair to pass messages between threads
334 if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) {
335 LOGE("%s: socketpair() failed: %s", __FUNCTION__, strerror(errno));
336 pthread_mutex_unlock(&nat->mutex);
339 nat->signal_sk = signal_sks[0];
340 nat->object = env->NewGlobalRef(object);
342 data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
344 LOGE("%s: out of memory", __FUNCTION__);
345 pthread_mutex_unlock(&nat->mutex);
348 nat->thread_data = data;
349 pthread_mutex_unlock(&nat->mutex);
351 data->signal_sk = signal_sks[1];
353 data->is_accept = true;
355 if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
356 LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
365 static jboolean connectNative(JNIEnv *env, jobject object, jstring address,
369 #ifdef HAVE_BLUETOOTH
370 native_data_t *nat = get_native_data(env, object);
373 struct thread_data_t *data;
374 const char *c_address;
377 pthread_mutex_lock(&nat->mutex);
378 if (nat->signal_sk != -1) {
379 pthread_mutex_unlock(&nat->mutex);
383 // setup socketpair to pass messages between threads
384 if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) {
385 LOGE("%s: socketpair() failed: %s\n", __FUNCTION__, strerror(errno));
386 pthread_mutex_unlock(&nat->mutex);
389 nat->signal_sk = signal_sks[0];
390 nat->object = env->NewGlobalRef(object);
392 data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
394 LOGE("%s: out of memory", __FUNCTION__);
395 pthread_mutex_unlock(&nat->mutex);
398 pthread_mutex_unlock(&nat->mutex);
400 data->signal_sk = signal_sks[1];
402 c_address = env->GetStringUTFChars(address, NULL);
403 strlcpy(data->address, c_address, BTADDR_SIZE);
404 env->ReleaseStringUTFChars(address, c_address);
405 data->is_accept = false;
408 LOGE("%s: Null pointer passed in for device name", __FUNCTION__);
409 data->sco_pkt_type = 0;
411 c_name = env->GetStringUTFChars(name, NULL);
412 /* See if this device is in the black list */
413 data->sco_pkt_type = getScoType(data->address, c_name);
414 env->ReleaseStringUTFChars(name, c_name);
416 if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
417 LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
426 static void closeNative(JNIEnv *env, jobject object) {
428 #ifdef HAVE_BLUETOOTH
429 native_data_t *nat = get_native_data(env, object);
432 pthread_mutex_lock(&nat->mutex);
433 signal_sk = nat->signal_sk;
435 env->DeleteGlobalRef(nat->object);
437 pthread_mutex_unlock(&nat->mutex);
439 if (signal_sk >= 0) {
440 LOGV("%s: signal_sk = %d", __FUNCTION__, signal_sk);
442 write(signal_sk, &dummy, sizeof(dummy));
448 #ifdef HAVE_BLUETOOTH
449 /* thread entry point */
450 static void *work_thread(void *arg) {
452 thread_data_t *data = (thread_data_t *)arg;
456 if (jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
457 LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
461 /* connect the SCO socket */
462 if (data->is_accept) {
463 LOGV("SCO OBJECT %p ACCEPT #####", data->nat->object);
464 sk = accept_work(data->signal_sk);
465 LOGV("SCO OBJECT %p END ACCEPT *****", data->nat->object);
467 sk = connect_work(data->address, data->sco_pkt_type);
470 /* callback with connection result */
471 if (data->nat == NULL) {
472 LOGV("%s: object destroyed!", __FUNCTION__);
475 pthread_mutex_lock(&data->nat->mutex);
476 if (data->nat->object == NULL) {
477 pthread_mutex_unlock(&data->nat->mutex);
478 LOGV("%s: callback cancelled", __FUNCTION__);
481 if (data->is_accept) {
482 env->CallVoidMethod(data->nat->object, method_onAccepted, sk);
484 env->CallVoidMethod(data->nat->object, method_onConnected, sk);
486 pthread_mutex_unlock(&data->nat->mutex);
492 LOGV("SCO OBJECT %p %d CONNECTED +++ (%s)", data->nat->object, sk,
493 data->is_accept ? "in" : "out");
495 /* wait for the socket to close */
496 LOGV("wait_for_close()...");
497 wait_for_close(sk, data->signal_sk);
498 LOGV("wait_for_close() returned");
500 /* callback with close result */
501 if (data->nat == NULL) {
502 LOGV("%s: object destroyed!", __FUNCTION__);
505 pthread_mutex_lock(&data->nat->mutex);
506 if (data->nat->object == NULL) {
507 LOGV("%s: callback cancelled", __FUNCTION__);
509 env->CallVoidMethod(data->nat->object, method_onClosed);
511 pthread_mutex_unlock(&data->nat->mutex);
516 LOGV("SCO OBJECT %p %d CLOSED --- (%s)", data->nat->object, sk, data->is_accept ? "in" : "out");
518 if (data->signal_sk >= 0) {
519 close(data->signal_sk);
521 LOGV("SCO socket closed");
523 if (data->nat != NULL) {
524 pthread_mutex_lock(&data->nat->mutex);
525 env->DeleteGlobalRef(data->nat->object);
526 data->nat->object = NULL;
527 data->nat->thread_data = NULL;
528 pthread_mutex_unlock(&data->nat->mutex);
532 if (jvm->DetachCurrentThread() != JNI_OK) {
533 LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
536 LOGV("work_thread() done");
540 static int accept_work(int signal_sk) {
547 struct sockaddr_sco addr;
549 sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
551 LOGE("%s socket() failed: %s", __FUNCTION__, strerror(errno));
555 memset(&addr, 0, sizeof(addr));
556 addr.sco_family = AF_BLUETOOTH;
557 memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
558 if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
559 LOGE("%s bind() failed: %s", __FUNCTION__, strerror(errno));
564 LOGE("%s: listen() failed: %s", __FUNCTION__, strerror(errno));
568 memset(&addr, 0, sizeof(addr));
569 addr_sz = sizeof(addr);
573 FD_SET(signal_sk, &fds);
575 max_fd = (sk > signal_sk) ? sk : signal_sk;
576 LOGI("Listening SCO socket...");
577 while (select(max_fd + 1, &fds, NULL, NULL, NULL) < 0) {
578 if (errno != EINTR) {
579 LOGE("%s: select() failed: %s", __FUNCTION__, strerror(errno));
582 LOGV("%s: select() EINTR, retrying", __FUNCTION__);
584 LOGV("select() returned");
585 if (FD_ISSET(signal_sk, &fds)) {
586 // signal to cancel listening
587 LOGV("cancelled listening socket, closing");
590 if (!FD_ISSET(sk, &fds)) {
591 LOGE("error: select() returned >= 0 with no fds set");
595 nsk = accept(sk, (struct sockaddr *)&addr, &addr_sz);
597 LOGE("%s: accept() failed: %s", __FUNCTION__, strerror(errno));
600 LOGI("Connected SCO socket (incoming)");
601 close(sk); // The listening socket
611 static int connect_work(const char *address, uint16_t sco_pkt_type) {
613 struct sockaddr_sco addr;
616 sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
618 LOGE("%s: socket() failed: %s", __FUNCTION__, strerror(errno));
622 /* Bind to local address */
623 memset(&addr, 0, sizeof(addr));
624 addr.sco_family = AF_BLUETOOTH;
625 memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
626 if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
627 LOGE("%s: bind() failed: %s", __FUNCTION__, strerror(errno));
631 memset(&addr, 0, sizeof(addr));
632 addr.sco_family = AF_BLUETOOTH;
633 get_bdaddr(address, &addr.sco_bdaddr);
634 addr.sco_pkt_type = sco_pkt_type;
635 LOGI("Connecting to socket");
636 while (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
637 if (errno != EINTR) {
638 LOGE("%s: connect() failed: %s", __FUNCTION__, strerror(errno));
641 LOGV("%s: connect() EINTR, retrying", __FUNCTION__);
643 LOGI("SCO socket connected (outgoing)");
648 if (sk >= 0) close(sk);
652 static void wait_for_close(int sk, int signal_sk) {
656 memset(p, 0, 2 * sizeof(pollfd));
659 p[1].events = POLLIN | POLLPRI;
663 while (poll(p, 2, -1) < 0) { // blocks
664 if (errno != EINTR) {
665 LOGE("%s: poll() failed: %s", __FUNCTION__, strerror(errno));
668 LOGV("%s: poll() EINTR, retrying", __FUNCTION__);
671 LOGV("poll() returned");
675 static JNINativeMethod sMethods[] = {
676 {"classInitNative", "()V", (void*)classInitNative},
677 {"initNative", "()V", (void *)initNative},
678 {"destroyNative", "()V", (void *)destroyNative},
679 {"connectNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)connectNative},
680 {"acceptNative", "()Z", (void *)acceptNative},
681 {"closeNative", "()V", (void *)closeNative},
684 int register_android_bluetooth_ScoSocket(JNIEnv *env) {
685 return AndroidRuntime::registerNativeMethods(env,
686 "android/bluetooth/ScoSocket", sMethods, NELEM(sMethods));
689 } /* namespace android */