OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / jni / android_bluetooth_ScoSocket.cpp
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 #include <bluetooth/hci.h>
41
42 #define MAX_LINE 255
43
44 /*
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.
48  */
49 #define SCO_BLACKLIST_MODULE_NAME "scoSocket"
50
51
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"
56
57 #endif
58
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.
65  *
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().
70  *
71  * The JNI API is entirely async.
72  *
73  * Also note this class deals only with SCO connections, not with data
74  * transmission.
75  */
76 namespace android {
77 #ifdef HAVE_BLUETOOTH
78
79 static JavaVM *jvm;
80 static jfieldID field_mNativeData;
81 static jmethodID method_onAccepted;
82 static jmethodID method_onConnected;
83 static jmethodID method_onClosed;
84
85 struct thread_data_t;
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);
91
92 static void parseBlacklist(void);
93 static uint16_t getScoType(char *address, const char *name);
94
95 #define COMPARE_STRING(key, s) (!strncmp(key, s, strlen(s)))
96
97 /* Blacklist data */
98 typedef struct scoBlacklist {
99     int fieldType;
100     char *value;
101     uint16_t scoType;
102     struct scoBlacklist *next;
103 } scoBlacklist_t;
104
105 #define BL_TYPE_NAME 1   // Field type is name string
106
107 static scoBlacklist_t *blacklist = NULL;
108
109 /* shared native data - protected by mutex */
110 typedef struct {
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
116 } native_data_t;
117
118 /* thread local data */
119 struct thread_data_t {
120     native_data_t *nat;
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
125 };
126
127 static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
128     return (native_data_t *)(env->GetIntField(object, field_mNativeData));
129 }
130
131 static uint16_t str2scoType (char *key) {
132     LOGV("%s: key = %s", __FUNCTION__, key);
133     if (COMPARE_STRING(key, "ESCO_HV1"))
134         return ESCO_HV1;
135     if (COMPARE_STRING(key, "ESCO_HV2"))
136         return ESCO_HV2;
137     if (COMPARE_STRING(key, "ESCO_HV3"))
138         return ESCO_HV3;
139     if (COMPARE_STRING(key, "ESCO_EV3"))
140         return ESCO_EV3;
141     if (COMPARE_STRING(key, "ESCO_EV4"))
142         return ESCO_EV4;
143     if (COMPARE_STRING(key, "ESCO_EV5"))
144         return ESCO_EV5;
145     if (COMPARE_STRING(key, "ESCO_2EV3"))
146         return ESCO_2EV3;
147     if (COMPARE_STRING(key, "ESCO_3EV3"))
148         return ESCO_3EV3;
149     if (COMPARE_STRING(key, "ESCO_2EV5"))
150         return ESCO_2EV5;
151     if (COMPARE_STRING(key, "ESCO_3EV5"))
152         return 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);
160     return 0;
161 }
162
163 static void parseBlacklist(void) {
164     const char *filename = "/etc/bluetooth/blacklist.conf";
165     char line[MAX_LINE];
166     scoBlacklist_t *list = NULL;
167     scoBlacklist_t *newelem;
168
169     LOGV(__FUNCTION__);
170
171     /* Open file */
172     FILE *fp = fopen(filename, "r");
173     if(!fp) {
174         LOGE("Error(%s)opening blacklist file", strerror(errno));
175         return;
176     }
177
178     while (fgets(line, MAX_LINE, fp) != NULL) {
179         if ((COMPARE_STRING(line, "//")) || (!strcmp(line, "")))
180             continue;
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__);
186                 return;
187             }
188             // parse line
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__);
198                     continue;
199                 }
200                 valueList++;  // Skip open quote
201                 strncpy(newelem->value, valueList, strlen(valueList) - 1);
202
203                 // Get Sco Settings from Parameters
204                 char *param = strtok(paramList, ";");
205                 uint16_t scoTypes = 0;
206                 while (param != NULL) {
207                     uint16_t sco;
208                     if (param[0] == '-') {
209                         param++;
210                         sco = str2scoType(param);
211                         if (sco != 0)
212                             scoTypes &= ~sco;
213                     } else if (param[0] == '+') {
214                         param++;
215                         sco = str2scoType(param);
216                         if (sco != 0)
217                             scoTypes |= sco;
218                     } else if (param[0] == '=') {
219                         param++;
220                         sco = str2scoType(param);
221                         if (sco != 0)
222                             scoTypes = sco;
223                     } else {
224                         LOGE("Invalid SCO type must be =, + or -");
225                     }
226                     param = strtok(NULL, ";");
227                 }
228                 newelem->scoType = scoTypes;
229             } else {
230                 LOGE("Unknown SCO type entry in Blacklist file");
231                 continue;
232             }
233             if (list) {
234                 list->next = newelem;
235                 list = newelem;
236             } else {
237                 blacklist = list = newelem;
238             }
239             LOGI("Entry name = %s ScoTypes = 0x%x", newelem->value,
240                  newelem->scoType);
241         }
242     }
243     fclose(fp);
244     return;
245 }
246 static uint16_t getScoType(char *address, const char *name) {
247     uint16_t ret = 0;
248     scoBlacklist_t *list = blacklist;
249
250     while (list != NULL) {
251         if (list->fieldType == BL_TYPE_NAME) {
252             if (COMPARE_STRING(name, list->value)) {
253                 ret = list->scoType;
254                 break;
255             }
256         }
257         list = list->next;
258     }
259     LOGI("%s %s - 0x%x",  __FUNCTION__, name, ret);
260     return ret;
261 }
262 #endif
263
264 static void classInitNative(JNIEnv* env, jclass clazz) {
265     LOGV(__FUNCTION__);
266 #ifdef HAVE_BLUETOOTH
267     if (env->GetJavaVM(&jvm) < 0) {
268         LOGE("Could not get handle to the VM");
269     }
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");
274
275     /* Read the blacklist file in here */
276     parseBlacklist();
277 #endif
278 }
279
280 /* Returns false if a serious error occured */
281 static jboolean initNative(JNIEnv* env, jobject object) {
282     LOGV(__FUNCTION__);
283 #ifdef HAVE_BLUETOOTH
284
285     native_data_t *nat = (native_data_t *) calloc(1, sizeof(native_data_t));
286     if (nat == NULL) {
287         LOGE("%s: out of memory!", __FUNCTION__);
288         return JNI_FALSE;
289     }
290
291     pthread_mutex_init(&nat->mutex, NULL);
292     env->SetIntField(object, field_mNativeData, (jint)nat);
293     nat->signal_sk = -1;
294     nat->object = NULL;
295     nat->thread_data = NULL;
296
297 #endif
298     return JNI_TRUE;
299 }
300
301 static void destroyNative(JNIEnv* env, jobject object) {
302     LOGV(__FUNCTION__);
303 #ifdef HAVE_BLUETOOTH
304     native_data_t *nat = get_native_data(env, object);
305     
306     closeNative(env, object);
307     
308     pthread_mutex_lock(&nat->mutex);
309     if (nat->thread_data != NULL) {
310         nat->thread_data->nat = NULL;
311     }
312     pthread_mutex_unlock(&nat->mutex);
313     pthread_mutex_destroy(&nat->mutex);
314
315     free(nat);
316 #endif
317 }
318
319 static jboolean acceptNative(JNIEnv *env, jobject object) {
320     LOGV(__FUNCTION__);
321 #ifdef HAVE_BLUETOOTH
322     native_data_t *nat = get_native_data(env, object);
323     int signal_sks[2];
324     pthread_t thread;
325     struct thread_data_t *data = NULL;
326
327     pthread_mutex_lock(&nat->mutex);
328     if (nat->signal_sk != -1) {
329         pthread_mutex_unlock(&nat->mutex);
330         return JNI_FALSE;
331     }
332
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);
337         return JNI_FALSE;
338     }
339     nat->signal_sk = signal_sks[0];
340     nat->object = env->NewGlobalRef(object);
341
342     data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
343     if (data == NULL) {
344         LOGE("%s: out of memory", __FUNCTION__);
345         pthread_mutex_unlock(&nat->mutex);
346         return JNI_FALSE;
347     }
348     nat->thread_data = data;
349     pthread_mutex_unlock(&nat->mutex);
350
351     data->signal_sk = signal_sks[1];
352     data->nat = nat;
353     data->is_accept = true;
354
355     if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
356         LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
357         return JNI_FALSE;
358     }
359     return JNI_TRUE;
360
361 #endif
362     return JNI_FALSE;
363 }
364
365 static jboolean connectNative(JNIEnv *env, jobject object, jstring address,
366         jstring name) {
367
368     LOGV(__FUNCTION__);
369 #ifdef HAVE_BLUETOOTH
370     native_data_t *nat = get_native_data(env, object);
371     int signal_sks[2];
372     pthread_t thread;
373     struct thread_data_t *data;
374     const char *c_address;
375     const char *c_name;
376
377     pthread_mutex_lock(&nat->mutex);
378     if (nat->signal_sk != -1) {
379         pthread_mutex_unlock(&nat->mutex);
380         return JNI_FALSE;
381     }
382
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);
387         return JNI_FALSE;
388     }
389     nat->signal_sk = signal_sks[0];
390     nat->object = env->NewGlobalRef(object);
391
392     data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
393     if (data == NULL) {
394         LOGE("%s: out of memory", __FUNCTION__);
395         pthread_mutex_unlock(&nat->mutex);
396         return JNI_FALSE;
397     }
398     pthread_mutex_unlock(&nat->mutex);
399
400     data->signal_sk = signal_sks[1];
401     data->nat = nat;
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;
406
407     if (name == NULL) {
408         LOGE("%s: Null pointer passed in for device name", __FUNCTION__);
409         data->sco_pkt_type = 0;
410     } else {
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);
415     }
416     if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
417         LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
418         return JNI_FALSE;
419     }
420     return JNI_TRUE;
421
422 #endif
423     return JNI_FALSE;
424 }
425
426 static void closeNative(JNIEnv *env, jobject object) {
427     LOGV(__FUNCTION__);
428 #ifdef HAVE_BLUETOOTH
429     native_data_t *nat = get_native_data(env, object);
430     int signal_sk;
431
432     pthread_mutex_lock(&nat->mutex);
433     signal_sk = nat->signal_sk;
434     nat->signal_sk = -1;
435     env->DeleteGlobalRef(nat->object);
436     nat->object = NULL;
437     pthread_mutex_unlock(&nat->mutex);
438
439     if (signal_sk >= 0) {
440         LOGV("%s: signal_sk = %d", __FUNCTION__, signal_sk);
441         unsigned char dummy;
442         write(signal_sk, &dummy, sizeof(dummy));
443         close(signal_sk);
444     }
445 #endif
446 }
447
448 #ifdef HAVE_BLUETOOTH
449 /* thread entry point */
450 static void *work_thread(void *arg) {
451     JNIEnv* env;
452     thread_data_t *data = (thread_data_t *)arg;
453     int sk;
454
455     LOGV(__FUNCTION__);
456     if (jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
457         LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
458         return NULL;
459     }
460
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);
466     } else {
467         sk = connect_work(data->address, data->sco_pkt_type);
468     }
469
470     /* callback with connection result */
471     if (data->nat == NULL) {
472         LOGV("%s: object destroyed!", __FUNCTION__);
473         goto done;
474     }
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__);
479         goto done;
480     }
481     if (data->is_accept) {
482         env->CallVoidMethod(data->nat->object, method_onAccepted, sk);
483     } else {
484         env->CallVoidMethod(data->nat->object, method_onConnected, sk);
485     }
486     pthread_mutex_unlock(&data->nat->mutex);
487
488     if (sk < 0) {
489         goto done;
490     }
491
492     LOGV("SCO OBJECT %p %d CONNECTED +++ (%s)", data->nat->object, sk,
493          data->is_accept ? "in" : "out");
494
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");
499
500     /* callback with close result */
501     if (data->nat == NULL) {
502         LOGV("%s: object destroyed!", __FUNCTION__);
503         goto done;
504     }
505     pthread_mutex_lock(&data->nat->mutex);
506     if (data->nat->object == NULL) {
507         LOGV("%s: callback cancelled", __FUNCTION__);
508     } else {
509         env->CallVoidMethod(data->nat->object, method_onClosed);
510     }
511     pthread_mutex_unlock(&data->nat->mutex);
512
513 done:
514     if (sk >= 0) {
515         close(sk);
516         LOGV("SCO OBJECT %p %d CLOSED --- (%s)", data->nat->object, sk, data->is_accept ? "in" : "out");
517     }
518     if (data->signal_sk >= 0) {
519         close(data->signal_sk);
520     }
521     LOGV("SCO socket closed");
522
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);
529     }
530
531     free(data);
532     if (jvm->DetachCurrentThread() != JNI_OK) {
533         LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
534     }
535
536     LOGV("work_thread() done");
537     return NULL;
538 }
539
540 static int accept_work(int signal_sk) {
541     LOGV(__FUNCTION__);
542     int sk;
543     int nsk;
544     int addr_sz;
545     int max_fd;
546     fd_set fds;
547     struct sockaddr_sco addr;
548
549     sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
550     if (sk < 0) {
551         LOGE("%s socket() failed: %s", __FUNCTION__, strerror(errno));
552         return -1;
553     }
554
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));
560         goto error;
561     }
562
563     if (listen(sk, 1)) {
564         LOGE("%s: listen() failed: %s", __FUNCTION__, strerror(errno));
565         goto error;
566     }
567
568     memset(&addr, 0, sizeof(addr));
569     addr_sz = sizeof(addr);
570
571     FD_ZERO(&fds);
572     FD_SET(sk, &fds);
573     FD_SET(signal_sk, &fds);
574
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));
580             goto error;
581         }
582         LOGV("%s: select() EINTR, retrying", __FUNCTION__);
583     }
584     LOGV("select() returned");
585     if (FD_ISSET(signal_sk, &fds)) {
586         // signal to cancel listening
587         LOGV("cancelled listening socket, closing");
588         goto error;
589     }
590     if (!FD_ISSET(sk, &fds)) {
591         LOGE("error: select() returned >= 0 with no fds set");
592         goto error;
593     }
594
595     nsk = accept(sk, (struct sockaddr *)&addr, &addr_sz);
596     if (nsk < 0) {
597         LOGE("%s: accept() failed: %s", __FUNCTION__, strerror(errno));
598         goto error;
599     }
600     LOGI("Connected SCO socket (incoming)");
601     close(sk);  // The listening socket
602
603     return nsk;
604
605 error:
606     close(sk);
607
608     return -1;
609 }
610
611 static int connect_work(const char *address, uint16_t sco_pkt_type) {
612     LOGV(__FUNCTION__);
613     struct sockaddr_sco addr;
614     int sk = -1;
615
616     sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
617     if (sk < 0) {
618         LOGE("%s: socket() failed: %s", __FUNCTION__, strerror(errno));
619         return -1;
620     }
621
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));
628         goto error;
629     }
630
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));
639             goto error;
640         }
641         LOGV("%s: connect() EINTR, retrying", __FUNCTION__);
642     }
643     LOGI("SCO socket connected (outgoing)");
644
645     return sk;
646
647 error:
648     if (sk >= 0) close(sk);
649     return -1;
650 }
651
652 static void wait_for_close(int sk, int signal_sk) {
653     LOGV(__FUNCTION__);
654     pollfd p[2];
655
656     memset(p, 0, 2 * sizeof(pollfd));
657     p[0].fd = sk;
658     p[1].fd = signal_sk;
659     p[1].events = POLLIN | POLLPRI;
660
661     LOGV("poll...");
662
663     while (poll(p, 2, -1) < 0) {  // blocks
664         if (errno != EINTR) {
665             LOGE("%s: poll() failed: %s", __FUNCTION__, strerror(errno));
666             break;
667         }
668         LOGV("%s: poll() EINTR, retrying", __FUNCTION__);
669     }
670
671     LOGV("poll() returned");
672 }
673 #endif
674
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},
682 };
683
684 int register_android_bluetooth_ScoSocket(JNIEnv *env) {
685     return AndroidRuntime::registerNativeMethods(env,
686             "android/bluetooth/ScoSocket", sMethods, NELEM(sMethods));
687 }
688
689 } /* namespace android */