blob: cb742a3d5b1c815f56a928d6f4b618671da87ee3 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2** Copyright 2006, 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 "BluetoothAudioGateway.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#define USE_ACCEPT_DIRECTLY (0)
27#define USE_SELECT (0) /* 1 for select(), 0 for poll(); used only when
28 USE_ACCEPT_DIRECTLY == 0 */
29
30#include <stdio.h>
31#include <string.h>
32#include <stdlib.h>
33#include <errno.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <sys/socket.h>
37#include <sys/time.h>
38#include <sys/types.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <sys/uio.h>
42#include <ctype.h>
43
44#if USE_SELECT
45#include <sys/select.h>
46#else
47#include <sys/poll.h>
48#endif
49
50#ifdef HAVE_BLUETOOTH
51#include <bluetooth/bluetooth.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052#include <bluetooth/rfcomm.h>
53#include <bluetooth/sco.h>
54#endif
55
56namespace android {
57
58#ifdef HAVE_BLUETOOTH
59static jfieldID field_mNativeData;
60 /* in */
61static jfieldID field_mHandsfreeAgRfcommChannel;
62static jfieldID field_mHeadsetAgRfcommChannel;
63 /* out */
64static jfieldID field_mTimeoutRemainingMs; /* out */
65
66static jfieldID field_mConnectingHeadsetAddress;
67static jfieldID field_mConnectingHeadsetRfcommChannel; /* -1 when not connected */
68static jfieldID field_mConnectingHeadsetSocketFd;
69
70static jfieldID field_mConnectingHandsfreeAddress;
71static jfieldID field_mConnectingHandsfreeRfcommChannel; /* -1 when not connected */
72static jfieldID field_mConnectingHandsfreeSocketFd;
73
74
75typedef struct {
76 int hcidev;
77 int hf_ag_rfcomm_channel;
78 int hs_ag_rfcomm_channel;
79 int hf_ag_rfcomm_sock;
80 int hs_ag_rfcomm_sock;
81} native_data_t;
82
83static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
84 return (native_data_t *)(env->GetIntField(object,
85 field_mNativeData));
86}
87
88static int setup_listening_socket(int dev, int channel);
89#endif
90
91static void classInitNative(JNIEnv* env, jclass clazz) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -080092 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093#ifdef HAVE_BLUETOOTH
94
95 /* in */
96 field_mNativeData = get_field(env, clazz, "mNativeData", "I");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -080097 field_mHandsfreeAgRfcommChannel =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 get_field(env, clazz, "mHandsfreeAgRfcommChannel", "I");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -080099 field_mHeadsetAgRfcommChannel =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 get_field(env, clazz, "mHeadsetAgRfcommChannel", "I");
101
102 /* out */
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800103 field_mConnectingHeadsetAddress =
104 get_field(env, clazz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 "mConnectingHeadsetAddress", "Ljava/lang/String;");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800106 field_mConnectingHeadsetRfcommChannel =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 get_field(env, clazz, "mConnectingHeadsetRfcommChannel", "I");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800108 field_mConnectingHeadsetSocketFd =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 get_field(env, clazz, "mConnectingHeadsetSocketFd", "I");
110
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800111 field_mConnectingHandsfreeAddress =
112 get_field(env, clazz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 "mConnectingHandsfreeAddress", "Ljava/lang/String;");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800114 field_mConnectingHandsfreeRfcommChannel =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 get_field(env, clazz, "mConnectingHandsfreeRfcommChannel", "I");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800116 field_mConnectingHandsfreeSocketFd =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 get_field(env, clazz, "mConnectingHandsfreeSocketFd", "I");
118
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800119 field_mTimeoutRemainingMs =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 get_field(env, clazz, "mTimeoutRemainingMs", "I");
121#endif
122}
123
124static void initializeNativeDataNative(JNIEnv* env, jobject object) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800125 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126#ifdef HAVE_BLUETOOTH
127 native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
128 if (NULL == nat) {
129 LOGE("%s: out of memory!", __FUNCTION__);
130 return;
131 }
132
133 nat->hcidev = BLUETOOTH_ADAPTER_HCI_NUM;
134
135 env->SetIntField(object, field_mNativeData, (jint)nat);
136 nat->hf_ag_rfcomm_channel =
137 env->GetIntField(object, field_mHandsfreeAgRfcommChannel);
138 nat->hs_ag_rfcomm_channel =
139 env->GetIntField(object, field_mHeadsetAgRfcommChannel);
140 LOGV("HF RFCOMM channel = %d.", nat->hf_ag_rfcomm_channel);
141 LOGV("HS RFCOMM channel = %d.", nat->hs_ag_rfcomm_channel);
142
143 /* Set the default values of these to -1. */
144 env->SetIntField(object, field_mConnectingHeadsetRfcommChannel, -1);
145 env->SetIntField(object, field_mConnectingHandsfreeRfcommChannel, -1);
146
147 nat->hf_ag_rfcomm_sock = -1;
148 nat->hs_ag_rfcomm_sock = -1;
149#endif
150}
151
152static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800153 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154#ifdef HAVE_BLUETOOTH
155 native_data_t *nat = get_native_data(env, object);
156 if (nat) {
157 free(nat);
158 }
159#endif
160}
161
162#ifdef HAVE_BLUETOOTH
163
164#if USE_ACCEPT_DIRECTLY==0
165static int set_nb(int sk, bool nb) {
166 int flags = fcntl(sk, F_GETFL);
167 if (flags < 0) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800168 LOGE("Can't get socket flags with fcntl(): %s (%d)",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 strerror(errno), errno);
170 close(sk);
171 return -1;
172 }
173 flags &= ~O_NONBLOCK;
174 if (nb) flags |= O_NONBLOCK;
175 int status = fcntl(sk, F_SETFL, flags);
176 if (status < 0) {
177 LOGE("Can't set socket to nonblocking mode with fcntl(): %s (%d)",
178 strerror(errno), errno);
179 close(sk);
180 return -1;
181 }
182 return 0;
183}
184#endif /*USE_ACCEPT_DIRECTLY==0*/
185
186static int do_accept(JNIEnv* env, jobject object, int ag_fd,
187 jfieldID out_fd,
188 jfieldID out_address,
189 jfieldID out_channel) {
190
191#if USE_ACCEPT_DIRECTLY==0
192 if (set_nb(ag_fd, true) < 0)
193 return -1;
194#endif
195
196 struct sockaddr_rc raddr;
197 int alen = sizeof(raddr);
198 int nsk = accept(ag_fd, (struct sockaddr *) &raddr, &alen);
199 if (nsk < 0) {
200 LOGE("Error on accept from socket fd %d: %s (%d).",
201 ag_fd,
202 strerror(errno),
203 errno);
204#if USE_ACCEPT_DIRECTLY==0
205 set_nb(ag_fd, false);
206#endif
207 return -1;
208 }
209
210 env->SetIntField(object, out_fd, nsk);
211 env->SetIntField(object, out_channel, raddr.rc_channel);
212
213 char addr[BTADDR_SIZE];
214 get_bdaddr_as_string(&raddr.rc_bdaddr, addr);
215 env->SetObjectField(object, out_address, env->NewStringUTF(addr));
216
217 LOGI("Successful accept() on AG socket %d: new socket %d, address %s, RFCOMM channel %d",
218 ag_fd,
219 nsk,
220 addr,
221 raddr.rc_channel);
222#if USE_ACCEPT_DIRECTLY==0
223 set_nb(ag_fd, false);
224#endif
225 return 0;
226}
227
228#if USE_SELECT
229static inline int on_accept_set_fields(JNIEnv* env, jobject object,
230 fd_set *rset, int ag_fd,
231 jfieldID out_fd,
232 jfieldID out_address,
233 jfieldID out_channel) {
234
235 env->SetIntField(object, out_channel, -1);
236
237 if (ag_fd >= 0 && FD_ISSET(ag_fd, &rset)) {
238 return do_accept(env, object, ag_fd,
239 out_fd, out_address, out_channel);
240 }
241 else {
242 LOGI("fd = %d, FD_ISSET() = %d",
243 ag_fd,
244 FD_ISSET(ag_fd, &rset));
245 if (ag_fd >= 0 && !FD_ISSET(ag_fd, &rset)) {
246 LOGE("WTF???");
247 return -1;
248 }
249 }
250
251 return 0;
252}
253#endif
254#endif /* HAVE_BLUETOOTH */
255
256static jboolean waitForHandsfreeConnectNative(JNIEnv* env, jobject object,
257 jint timeout_ms) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800258// LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259#ifdef HAVE_BLUETOOTH
260
261 env->SetIntField(object, field_mTimeoutRemainingMs, timeout_ms);
262
263 int n = 0;
264 native_data_t *nat = get_native_data(env, object);
265#if USE_ACCEPT_DIRECTLY
266 if (nat->hf_ag_rfcomm_channel > 0) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800267 LOGI("Setting HF AG server socket to RFCOMM port %d!",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 nat->hf_ag_rfcomm_channel);
269 struct timeval tv;
270 int len = sizeof(tv);
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800271 if (getsockopt(nat->hf_ag_rfcomm_channel,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 SOL_SOCKET, SO_RCVTIMEO, &tv, &len) < 0) {
273 LOGE("getsockopt(%d, SOL_SOCKET, SO_RCVTIMEO): %s (%d)",
274 nat->hf_ag_rfcomm_channel,
275 strerror(errno),
276 errno);
277 return JNI_FALSE;
278 }
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800279 LOGI("Current HF AG server socket RCVTIMEO is (%d(s), %d(us))!",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 (int)tv.tv_sec, (int)tv.tv_usec);
281 if (timeout_ms >= 0) {
282 tv.tv_sec = timeout_ms / 1000;
283 tv.tv_usec = 1000 * (timeout_ms % 1000);
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800284 if (setsockopt(nat->hf_ag_rfcomm_channel,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
286 LOGE("setsockopt(%d, SOL_SOCKET, SO_RCVTIMEO): %s (%d)",
287 nat->hf_ag_rfcomm_channel,
288 strerror(errno),
289 errno);
290 return JNI_FALSE;
291 }
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800292 LOGI("Changed HF AG server socket RCVTIMEO to (%d(s), %d(us))!",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 (int)tv.tv_sec, (int)tv.tv_usec);
294 }
295
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800296 if (!do_accept(env, object, nat->hf_ag_rfcomm_sock,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 field_mConnectingHandsfreeSocketFd,
298 field_mConnectingHandsfreeAddress,
299 field_mConnectingHandsfreeRfcommChannel))
300 {
301 env->SetIntField(object, field_mTimeoutRemainingMs, 0);
302 return JNI_TRUE;
303 }
304 return JNI_FALSE;
305 }
306#else
307#if USE_SELECT
308 fd_set rset;
309 FD_ZERO(&rset);
310 int cnt = 0;
311 if (nat->hf_ag_rfcomm_channel > 0) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800312 LOGI("Setting HF AG server socket to RFCOMM port %d!",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 nat->hf_ag_rfcomm_channel);
314 cnt++;
315 FD_SET(nat->hf_ag_rfcomm_sock, &rset);
316 }
317 if (nat->hs_ag_rfcomm_channel > 0) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800318 LOGI("Setting HS AG server socket to RFCOMM port %d!",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 nat->hs_ag_rfcomm_channel);
320 cnt++;
321 FD_SET(nat->hs_ag_rfcomm_sock, &rset);
322 }
323 if (cnt == 0) {
324 LOGE("Neither HF nor HS listening sockets are open!");
325 return JNI_FALSE;
326 }
327
328 struct timeval to;
329 if (timeout_ms >= 0) {
330 to.tv_sec = timeout_ms / 1000;
331 to.tv_usec = 1000 * (timeout_ms % 1000);
332 }
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800333 n = select(MAX(nat->hf_ag_rfcomm_sock,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 nat->hs_ag_rfcomm_sock) + 1,
335 &rset,
336 NULL,
337 NULL,
338 (timeout_ms < 0 ? NULL : &to));
339 if (timeout_ms > 0) {
340 jint remaining = to.tv_sec*1000 + to.tv_usec/1000;
341 LOGI("Remaining time %ldms", (long)remaining);
342 env->SetIntField(object, field_mTimeoutRemainingMs,
343 remaining);
344 }
345
346 LOGI("listening select() returned %d", n);
347
348 if (n <= 0) {
349 if (n < 0) {
350 LOGE("listening select() on RFCOMM sockets: %s (%d)",
351 strerror(errno),
352 errno);
353 }
354 return JNI_FALSE;
355 }
356
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800357 n = on_accept_set_fields(env, object,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 &rset, nat->hf_ag_rfcomm_sock,
359 field_mConnectingHandsfreeSocketFd,
360 field_mConnectingHandsfreeAddress,
361 field_mConnectingHandsfreeRfcommChannel);
362
363 n += on_accept_set_fields(env, object,
364 &rset, nat->hs_ag_rfcomm_sock,
365 field_mConnectingHeadsetSocketFd,
366 field_mConnectingHeadsetAddress,
367 field_mConnectingHeadsetRfcommChannel);
368
369 return !n ? JNI_TRUE : JNI_FALSE;
370#else
371 struct pollfd fds[2];
372 int cnt = 0;
373 if (nat->hf_ag_rfcomm_channel > 0) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800374// LOGI("Setting HF AG server socket %d to RFCOMM port %d!",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375// nat->hf_ag_rfcomm_sock,
376// nat->hf_ag_rfcomm_channel);
377 fds[cnt].fd = nat->hf_ag_rfcomm_sock;
378 fds[cnt].events = POLLIN | POLLPRI | POLLOUT | POLLERR;
379 cnt++;
380 }
381 if (nat->hs_ag_rfcomm_channel > 0) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800382// LOGI("Setting HS AG server socket %d to RFCOMM port %d!",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383// nat->hs_ag_rfcomm_sock,
384// nat->hs_ag_rfcomm_channel);
385 fds[cnt].fd = nat->hs_ag_rfcomm_sock;
386 fds[cnt].events = POLLIN | POLLPRI | POLLOUT | POLLERR;
387 cnt++;
388 }
389 if (cnt == 0) {
390 LOGE("Neither HF nor HS listening sockets are open!");
391 return JNI_FALSE;
392 }
393 n = poll(fds, cnt, timeout_ms);
394 if (n <= 0) {
395 if (n < 0) {
396 LOGE("listening poll() on RFCOMM sockets: %s (%d)",
397 strerror(errno),
398 errno);
399 }
400 else {
401 env->SetIntField(object, field_mTimeoutRemainingMs, 0);
402// LOGI("listening poll() on RFCOMM socket timed out");
403 }
404 return JNI_FALSE;
405 }
406
407 //LOGI("listening poll() on RFCOMM socket returned %d", n);
408 int err = 0;
409 for (cnt = 0; cnt < (int)(sizeof(fds)/sizeof(fds[0])); cnt++) {
410 //LOGI("Poll on fd %d revent = %d.", fds[cnt].fd, fds[cnt].revents);
411 if (fds[cnt].fd == nat->hf_ag_rfcomm_sock) {
412 if (fds[cnt].revents & (POLLIN | POLLPRI | POLLOUT)) {
413 LOGI("Accepting HF connection.\n");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800414 err += do_accept(env, object, fds[cnt].fd,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 field_mConnectingHandsfreeSocketFd,
416 field_mConnectingHandsfreeAddress,
417 field_mConnectingHandsfreeRfcommChannel);
418 n--;
419 }
420 }
421 else if (fds[cnt].fd == nat->hs_ag_rfcomm_sock) {
422 if (fds[cnt].revents & (POLLIN | POLLPRI | POLLOUT)) {
423 LOGI("Accepting HS connection.\n");
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800424 err += do_accept(env, object, fds[cnt].fd,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 field_mConnectingHeadsetSocketFd,
426 field_mConnectingHeadsetAddress,
427 field_mConnectingHeadsetRfcommChannel);
428 n--;
429 }
430 }
431 } /* for */
432
433 if (n != 0) {
434 LOGI("Bogus poll(): %d fake pollfd entrie(s)!", n);
435 return JNI_FALSE;
436 }
437
438 return !err ? JNI_TRUE : JNI_FALSE;
439#endif /* USE_SELECT */
440#endif /* USE_ACCEPT_DIRECTLY */
441#else
442 return JNI_FALSE;
443#endif /* HAVE_BLUETOOTH */
444}
445
446static jboolean setUpListeningSocketsNative(JNIEnv* env, jobject object) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800447 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448#ifdef HAVE_BLUETOOTH
449 native_data_t *nat = get_native_data(env, object);
450
451 nat->hf_ag_rfcomm_sock =
452 setup_listening_socket(nat->hcidev, nat->hf_ag_rfcomm_channel);
453 if (nat->hf_ag_rfcomm_sock < 0)
454 return JNI_FALSE;
455
456 nat->hs_ag_rfcomm_sock =
457 setup_listening_socket(nat->hcidev, nat->hs_ag_rfcomm_channel);
458 if (nat->hs_ag_rfcomm_sock < 0) {
459 close(nat->hf_ag_rfcomm_sock);
460 nat->hf_ag_rfcomm_sock = -1;
461 return JNI_FALSE;
462 }
463
464 return JNI_TRUE;
465#else
466 return JNI_FALSE;
467#endif /* HAVE_BLUETOOTH */
468}
469
470#ifdef HAVE_BLUETOOTH
471static int setup_listening_socket(int dev, int channel) {
472 struct sockaddr_rc laddr;
473 int sk, lm;
474
475 sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
476 if (sk < 0) {
477 LOGE("Can't create RFCOMM socket");
478 return -1;
479 }
480
481 if (debug_no_encrypt()) {
482 lm = RFCOMM_LM_AUTH;
483 } else {
484 lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
485 }
486
Jaikumar Ganesh14faa3b2011-03-30 14:04:42 -0700487 if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
488 LOGE("Can't set RFCOMM link mode");
489 close(sk);
490 return -1;
491 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492
493 laddr.rc_family = AF_BLUETOOTH;
Jaikumar Ganesh14faa3b2011-03-30 14:04:42 -0700494 memcpy(&laddr.rc_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 laddr.rc_channel = channel;
496
Jaikumar Ganesh14faa3b2011-03-30 14:04:42 -0700497 if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
498 LOGE("Can't bind RFCOMM socket");
499 close(sk);
500 return -1;
501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502
503 listen(sk, 10);
504 return sk;
505}
506#endif /* HAVE_BLUETOOTH */
507
508/*
509 private native void tearDownListeningSocketsNative();
510*/
511static void tearDownListeningSocketsNative(JNIEnv *env, jobject object) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800512 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513#ifdef HAVE_BLUETOOTH
514 native_data_t *nat = get_native_data(env, object);
515
516 if (nat->hf_ag_rfcomm_sock > 0) {
517 if (close(nat->hf_ag_rfcomm_sock) < 0) {
518 LOGE("Could not close HF server socket: %s (%d)\n",
519 strerror(errno), errno);
520 }
521 nat->hf_ag_rfcomm_sock = -1;
522 }
523 if (nat->hs_ag_rfcomm_sock > 0) {
524 if (close(nat->hs_ag_rfcomm_sock) < 0) {
525 LOGE("Could not close HS server socket: %s (%d)\n",
526 strerror(errno), errno);
527 }
528 nat->hs_ag_rfcomm_sock = -1;
529 }
530#endif /* HAVE_BLUETOOTH */
531}
532
533static JNINativeMethod sMethods[] = {
534 /* name, signature, funcPtr */
535
536 {"classInitNative", "()V", (void*)classInitNative},
537 {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
538 {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
539
540 {"setUpListeningSocketsNative", "()Z", (void *)setUpListeningSocketsNative},
541 {"tearDownListeningSocketsNative", "()V", (void *)tearDownListeningSocketsNative},
542 {"waitForHandsfreeConnectNative", "(I)Z", (void *)waitForHandsfreeConnectNative},
543};
544
545int register_android_bluetooth_BluetoothAudioGateway(JNIEnv *env) {
546 return AndroidRuntime::registerNativeMethods(env,
547 "android/bluetooth/BluetoothAudioGateway", sMethods,
548 NELEM(sMethods));
549}
550
551} /* namespace android */