blob: 71279b26263c4ba070e6029deea3de0fc3c6485d [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 "BT HSHFP"
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 <fcntl.h>
32#include <sys/socket.h>
33#include <sys/uio.h>
34#include <sys/poll.h>
35
36#ifdef HAVE_BLUETOOTH
37#include <bluetooth/bluetooth.h>
38#include <bluetooth/rfcomm.h>
39#include <bluetooth/sco.h>
40#endif
41
42namespace android {
43
44#ifdef HAVE_BLUETOOTH
45static jfieldID field_mNativeData;
46static jfieldID field_mAddress;
47static jfieldID field_mRfcommChannel;
48static jfieldID field_mTimeoutRemainingMs;
49
50typedef struct {
51 jstring address;
52 const char *c_address;
53 int rfcomm_channel;
54 int last_read_err;
55 int rfcomm_sock;
56 int rfcomm_connected; // -1 in progress, 0 not connected, 1 connected
57 int rfcomm_sock_flags;
58} native_data_t;
59
60static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
61 return (native_data_t *)(env->GetIntField(object, field_mNativeData));
62}
63
64static const char CRLF[] = "\xd\xa";
65static const int CRLF_LEN = 2;
66
67static inline int write_error_check(int fd, const char* line, int len) {
68 int ret;
69 errno = 0;
70 ret = write(fd, line, len);
71 if (ret < 0) {
72 LOGE("%s: write() failed: %s (%d)", __FUNCTION__, strerror(errno),
73 errno);
74 return -1;
75 }
76 if (ret != len) {
77 LOGE("%s: write() only wrote %d of %d bytes", __FUNCTION__, ret, len);
78 return -1;
79 }
80 return 0;
81}
82
83static int send_line(int fd, const char* line) {
84 int nw;
85 int len = strlen(line);
86 int llen = len + CRLF_LEN * 2 + 1;
87 char *buffer = (char *)calloc(llen, sizeof(char));
88
89 snprintf(buffer, llen, "%s%s%s", CRLF, line, CRLF);
90
91 if (write_error_check(fd, buffer, llen - 1)) {
92 free(buffer);
93 return -1;
94 }
95 free(buffer);
96 return 0;
97}
98
99static const char* get_line(int fd, char *buf, int len, int timeout_ms,
100 int *err) {
101 char *bufit=buf;
102 int fd_flags = fcntl(fd, F_GETFL, 0);
103 struct pollfd pfd;
104
105again:
106 *bufit = 0;
107 pfd.fd = fd;
108 pfd.events = POLLIN;
109 *err = errno = 0;
110 int ret = poll(&pfd, 1, timeout_ms);
111 if (ret < 0) {
112 LOGE("poll() error\n");
113 *err = errno;
114 return NULL;
115 }
116 if (ret == 0) {
117 return NULL;
118 }
119
120 if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
121 LOGW("RFCOMM poll() returned success (%d), "
122 "but with an unexpected revents bitmask: %#x\n", ret, pfd.revents);
123 errno = EIO;
124 *err = errno;
125 return NULL;
126 }
127
128 while ((int)(bufit - buf) < len)
129 {
130 errno = 0;
131 int rc = read(fd, bufit, 1);
132
133 if (!rc)
134 break;
135
136 if (rc < 0) {
137 if (errno == EBUSY) {
138 LOGI("read() error %s (%d): repeating read()...",
139 strerror(errno), errno);
140 goto again;
141 }
142 *err = errno;
143 LOGE("read() error %s (%d)", strerror(errno), errno);
144 return NULL;
145 }
146
147
148 if (*bufit=='\xd') {
149 break;
150 }
151
152 if (*bufit=='\xa')
153 bufit = buf;
154 else
155 bufit++;
156 }
157
158 *bufit = '\x0';
159 LOG(LOG_INFO, "Bluetooth AT recv", buf);
160
161 return buf;
162}
163#endif
164
165static void classInitNative(JNIEnv* env, jclass clazz) {
166 LOGV(__FUNCTION__);
167#ifdef HAVE_BLUETOOTH
168 field_mNativeData = get_field(env, clazz, "mNativeData", "I");
169 field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;");
170 field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I");
171 field_mRfcommChannel = get_field(env, clazz, "mRfcommChannel", "I");
172#endif
173}
174
175static void initializeNativeDataNative(JNIEnv* env, jobject object,
176 jint socketFd) {
177 LOGV(__FUNCTION__);
178#ifdef HAVE_BLUETOOTH
179 native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
180 if (NULL == nat) {
181 LOGE("%s: out of memory!", __FUNCTION__);
182 return;
183 }
184
185 env->SetIntField(object, field_mNativeData, (jint)nat);
186 nat->address =
187 (jstring)env->NewGlobalRef(env->GetObjectField(object,
188 field_mAddress));
189 nat->c_address = env->GetStringUTFChars(nat->address, NULL);
190 nat->rfcomm_channel = env->GetIntField(object, field_mRfcommChannel);
191 nat->rfcomm_sock = socketFd;
192 nat->rfcomm_connected = socketFd >= 0;
193 if (nat->rfcomm_connected)
194 LOGI("%s: ALREADY CONNECTED!", __FUNCTION__);
195#endif
196}
197
198static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
199 LOGV(__FUNCTION__);
200#ifdef HAVE_BLUETOOTH
201 native_data_t *nat =
202 (native_data_t *)env->GetIntField(object, field_mNativeData);
203 env->ReleaseStringUTFChars(nat->address, nat->c_address);
204 env->DeleteGlobalRef(nat->address);
205 if (nat)
206 free(nat);
207#endif
208}
209
210static jboolean connectNative(JNIEnv *env, jobject obj)
211{
212 LOGV(__FUNCTION__);
213#ifdef HAVE_BLUETOOTH
214 int lm;
215 struct sockaddr_rc addr;
216 native_data_t *nat = get_native_data(env, obj);
217
218 nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
219
220 if (nat->rfcomm_sock < 0) {
221 LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__,
222 strerror(errno));
223 return JNI_FALSE;
224 }
225
226 if (debug_no_encrypt()) {
227 lm = RFCOMM_LM_AUTH;
228 } else {
229 lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
230 }
231
232 if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm,
233 sizeof(lm)) < 0) {
234 LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__);
235 close(nat->rfcomm_sock);
236 return JNI_FALSE;
237 }
238
239 memset(&addr, 0, sizeof(struct sockaddr_rc));
240 get_bdaddr(nat->c_address, &addr.rc_bdaddr);
241 addr.rc_channel = nat->rfcomm_channel;
242 addr.rc_family = AF_BLUETOOTH;
243 nat->rfcomm_connected = 0;
244 while (nat->rfcomm_connected == 0) {
245 if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr,
246 sizeof(addr)) < 0) {
247 if (errno == EINTR) continue;
248 LOGE("%s: connect() failed: %s\n", __FUNCTION__, strerror(errno));
249 close(nat->rfcomm_sock);
250 nat->rfcomm_sock = -1;
251 return JNI_FALSE;
252 } else {
253 nat->rfcomm_connected = 1;
254 }
255 }
256
257 return JNI_TRUE;
258#else
259 return JNI_FALSE;
260#endif
261}
262
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700263static jint connectAsyncNative(JNIEnv *env, jobject obj) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 LOGV(__FUNCTION__);
265#ifdef HAVE_BLUETOOTH
266 struct sockaddr_rc addr;
267 native_data_t *nat = get_native_data(env, obj);
268
269 if (nat->rfcomm_connected) {
270 LOGV("RFCOMM socket is already connected or connection is in progress.");
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700271 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 }
273
274 if (nat->rfcomm_sock < 0) {
275 int lm;
276
277 nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
278 if (nat->rfcomm_sock < 0) {
279 LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__,
280 strerror(errno));
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700281 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 }
283
284 if (debug_no_encrypt()) {
285 lm = RFCOMM_LM_AUTH;
286 } else {
287 lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
288 }
289
290 if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm,
291 sizeof(lm)) < 0) {
292 LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__);
293 close(nat->rfcomm_sock);
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700294 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
296 LOGI("Created RFCOMM socket fd %d.", nat->rfcomm_sock);
297 }
298
299 memset(&addr, 0, sizeof(struct sockaddr_rc));
300 get_bdaddr(nat->c_address, &addr.rc_bdaddr);
301 addr.rc_channel = nat->rfcomm_channel;
302 addr.rc_family = AF_BLUETOOTH;
303 if (nat->rfcomm_sock_flags >= 0) {
304 nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0);
305 if (fcntl(nat->rfcomm_sock,
306 F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) {
307 int rc;
308 nat->rfcomm_connected = 0;
309 errno = 0;
310 rc = connect(nat->rfcomm_sock,
311 (struct sockaddr *)&addr,
312 sizeof(addr));
313
314 if (rc >= 0) {
315 nat->rfcomm_connected = 1;
316 LOGI("async connect successful");
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700317 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 }
319 else if (rc < 0) {
320 if (errno == EINPROGRESS || errno == EAGAIN)
321 {
322 LOGI("async connect is in progress (%s)",
323 strerror(errno));
324 nat->rfcomm_connected = -1;
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700325 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 }
327 else
328 {
329 LOGE("async connect error: %s (%d)", strerror(errno), errno);
330 close(nat->rfcomm_sock);
331 nat->rfcomm_sock = -1;
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700332 return -errno;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 }
334 }
335 } // fcntl(nat->rfcomm_sock ...)
336 } // if (nat->rfcomm_sock_flags >= 0)
337#endif
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700338 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339}
340
341static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj,
342 jint timeout_ms) {
343 LOGV(__FUNCTION__);
344#ifdef HAVE_BLUETOOTH
345 struct sockaddr_rc addr;
346 native_data_t *nat = get_native_data(env, obj);
347
348 env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms);
349
350 if (nat->rfcomm_connected > 0) {
351 LOGI("RFCOMM is already connected!");
352 return 1;
353 }
354
355 if (nat->rfcomm_sock >= 0 && nat->rfcomm_connected == 0) {
356 LOGI("Re-opening RFCOMM socket.");
357 close(nat->rfcomm_sock);
358 nat->rfcomm_sock = -1;
359 }
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700360 int ret = connectAsyncNative(env, obj);
361
362 if (ret < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 LOGI("Failed to re-open RFCOMM socket!");
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700364 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 }
366
367 if (nat->rfcomm_sock >= 0) {
368 /* Do an asynchronous select() */
369 int n;
370 fd_set rset, wset;
371 struct timeval to;
372
373 FD_ZERO(&rset);
374 FD_ZERO(&wset);
375 FD_SET(nat->rfcomm_sock, &rset);
376 FD_SET(nat->rfcomm_sock, &wset);
377 if (timeout_ms >= 0) {
378 to.tv_sec = timeout_ms / 1000;
379 to.tv_usec = 1000 * (timeout_ms % 1000);
380 }
381 n = select(nat->rfcomm_sock + 1,
382 &rset,
383 &wset,
384 NULL,
385 (timeout_ms < 0 ? NULL : &to));
386
387 if (timeout_ms > 0) {
388 jint remaining = to.tv_sec*1000 + to.tv_usec/1000;
389 LOGV("Remaining time %ldms", (long)remaining);
390 env->SetIntField(obj, field_mTimeoutRemainingMs,
391 remaining);
392 }
393
394 if (n <= 0) {
395 if (n < 0) {
396 LOGE("select() on RFCOMM socket: %s (%d)",
397 strerror(errno),
398 errno);
Jaikumar Ganesha01a4472009-10-01 19:10:43 -0700399 return -errno;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 }
401 return 0;
402 }
403 /* n must be equal to 1 and either rset or wset must have the
404 file descriptor set. */
405 LOGV("select() returned %d.", n);
406 if (FD_ISSET(nat->rfcomm_sock, &rset) ||
407 FD_ISSET(nat->rfcomm_sock, &wset))
408 {
409 /* A trial async read() will tell us if everything is OK. */
410 {
411 char ch;
412 errno = 0;
413 int nr = read(nat->rfcomm_sock, &ch, 1);
414 /* It should be that nr != 1 because we just opened a socket
415 and we haven't sent anything over it for the other side to
416 respond... but one can't be paranoid enough.
417 */
418 if (nr >= 0 || errno != EAGAIN) {
419 LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n",
420 strerror(errno),
421 errno,
422 nr);
423 /* Clear the rfcomm_connected flag to cause this function
424 to re-create the socket and re-attempt the connect()
425 the next time it is called.
426 */
427 nat->rfcomm_connected = 0;
428 /* Restore the blocking properties of the socket. */
429 fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
430 close(nat->rfcomm_sock);
431 nat->rfcomm_sock = -1;
Jaikumar Ganesha01a4472009-10-01 19:10:43 -0700432 return -errno;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 }
434 }
435 /* Restore the blocking properties of the socket. */
436 fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
437 LOGI("Successful RFCOMM socket connect.");
438 nat->rfcomm_connected = 1;
439 return 1;
440 }
441 }
442 else LOGE("RFCOMM socket file descriptor %d is bad!",
443 nat->rfcomm_sock);
444#endif
445 return -1;
446}
447
448static void disconnectNative(JNIEnv *env, jobject obj) {
449 LOGV(__FUNCTION__);
450#ifdef HAVE_BLUETOOTH
451 native_data_t *nat = get_native_data(env, obj);
452 if (nat->rfcomm_sock >= 0) {
453 close(nat->rfcomm_sock);
454 nat->rfcomm_sock = -1;
455 nat->rfcomm_connected = 0;
456 }
457#endif
458}
459
460static void pretty_log_urc(const char *urc) {
461 size_t i;
462 bool in_line_break = false;
463 char *buf = (char *)calloc(strlen(urc) + 1, sizeof(char));
464
465 strcpy(buf, urc);
466 for (i = 0; i < strlen(buf); i++) {
467 switch(buf[i]) {
468 case '\r':
469 case '\n':
470 in_line_break = true;
471 buf[i] = ' ';
472 break;
473 default:
474 if (in_line_break) {
475 in_line_break = false;
476 buf[i-1] = '\n';
477 }
478 }
479 }
480 LOG(LOG_INFO, "Bluetooth AT sent", buf);
481
482 free(buf);
483}
484
485static jboolean sendURCNative(JNIEnv *env, jobject obj, jstring urc) {
486#ifdef HAVE_BLUETOOTH
487 native_data_t *nat = get_native_data(env, obj);
488 if (nat->rfcomm_connected) {
489 const char *c_urc = env->GetStringUTFChars(urc, NULL);
490 jboolean ret = send_line(nat->rfcomm_sock, c_urc) == 0 ? JNI_TRUE : JNI_FALSE;
491 if (ret == JNI_TRUE) pretty_log_urc(c_urc);
492 env->ReleaseStringUTFChars(urc, c_urc);
493 return ret;
494 }
495#endif
496 return JNI_FALSE;
497}
498
499static jstring readNative(JNIEnv *env, jobject obj, jint timeout_ms) {
500#ifdef HAVE_BLUETOOTH
501 {
502 native_data_t *nat = get_native_data(env, obj);
503 if (nat->rfcomm_connected) {
504 char buf[128];
505 const char *ret = get_line(nat->rfcomm_sock,
506 buf, sizeof(buf),
507 timeout_ms,
508 &nat->last_read_err);
509 return ret ? env->NewStringUTF(ret) : NULL;
510 }
511 return NULL;
512 }
513#else
514 return NULL;
515#endif
516}
517
518static jint getLastReadStatusNative(JNIEnv *env, jobject obj) {
519#ifdef HAVE_BLUETOOTH
520 {
521 native_data_t *nat = get_native_data(env, obj);
522 if (nat->rfcomm_connected)
523 return (jint)nat->last_read_err;
524 return 0;
525 }
526#else
527 return 0;
528#endif
529}
530
531static JNINativeMethod sMethods[] = {
532 /* name, signature, funcPtr */
533 {"classInitNative", "()V", (void*)classInitNative},
534 {"initializeNativeDataNative", "(I)V", (void *)initializeNativeDataNative},
535 {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
536 {"connectNative", "()Z", (void *)connectNative},
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700537 {"connectAsyncNative", "()I", (void *)connectAsyncNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative},
539 {"disconnectNative", "()V", (void *)disconnectNative},
540 {"sendURCNative", "(Ljava/lang/String;)Z", (void *)sendURCNative},
541 {"readNative", "(I)Ljava/lang/String;", (void *)readNative},
542 {"getLastReadStatusNative", "()I", (void *)getLastReadStatusNative},
543};
544
545int register_android_bluetooth_HeadsetBase(JNIEnv *env) {
546 return AndroidRuntime::registerNativeMethods(env,
547 "android/bluetooth/HeadsetBase", sMethods, NELEM(sMethods));
548}
549
550} /* namespace android */