blob: 5b21c56d431e72a48481d38eda1b323a3cbf86cf [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
Matthew Xiefe900592011-03-21 12:07:04 -070099static void mask_eighth_bit(char *line)
100{
101 for (;;line++) {
102 if (0 == *line) return;
103 *line &= 0x7F;
104 }
Nick Pelly8457b0f2010-03-24 18:41:13 -0700105}
106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107static const char* get_line(int fd, char *buf, int len, int timeout_ms,
108 int *err) {
109 char *bufit=buf;
110 int fd_flags = fcntl(fd, F_GETFL, 0);
111 struct pollfd pfd;
112
113again:
114 *bufit = 0;
115 pfd.fd = fd;
116 pfd.events = POLLIN;
117 *err = errno = 0;
118 int ret = poll(&pfd, 1, timeout_ms);
119 if (ret < 0) {
120 LOGE("poll() error\n");
121 *err = errno;
122 return NULL;
123 }
124 if (ret == 0) {
125 return NULL;
126 }
127
128 if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
129 LOGW("RFCOMM poll() returned success (%d), "
130 "but with an unexpected revents bitmask: %#x\n", ret, pfd.revents);
131 errno = EIO;
132 *err = errno;
133 return NULL;
134 }
135
Nick Pelly8457b0f2010-03-24 18:41:13 -0700136 while ((int)(bufit - buf) < (len - 1))
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 {
138 errno = 0;
139 int rc = read(fd, bufit, 1);
140
141 if (!rc)
142 break;
143
144 if (rc < 0) {
145 if (errno == EBUSY) {
146 LOGI("read() error %s (%d): repeating read()...",
147 strerror(errno), errno);
148 goto again;
149 }
150 *err = errno;
151 LOGE("read() error %s (%d)", strerror(errno), errno);
152 return NULL;
153 }
154
155
156 if (*bufit=='\xd') {
157 break;
158 }
159
160 if (*bufit=='\xa')
161 bufit = buf;
162 else
163 bufit++;
164 }
165
Nick Pelly8457b0f2010-03-24 18:41:13 -0700166 *bufit = NULL;
167
Matthew Xiefe900592011-03-21 12:07:04 -0700168 // According to ITU V.250 section 5.1, IA5 7 bit chars are used,
169 // the eighth bit or higher bits are ignored if they exists
170 // We mask out only eighth bit, no higher bit, since we do char
171 // string here, not wide char.
172 // We added this processing due to 2 real world problems.
173 // 1 BMW 2005 E46 which sends binary junk
174 // 2 Audi 2010 A3, dial command use 0xAD (soft-hyphen) as number
175 // formater, which was rejected by the AT handler
176 mask_eighth_bit(buf);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
178 return buf;
179}
180#endif
181
182static void classInitNative(JNIEnv* env, jclass clazz) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800183 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184#ifdef HAVE_BLUETOOTH
185 field_mNativeData = get_field(env, clazz, "mNativeData", "I");
186 field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;");
187 field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I");
188 field_mRfcommChannel = get_field(env, clazz, "mRfcommChannel", "I");
189#endif
190}
191
192static void initializeNativeDataNative(JNIEnv* env, jobject object,
193 jint socketFd) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800194 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195#ifdef HAVE_BLUETOOTH
196 native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
197 if (NULL == nat) {
198 LOGE("%s: out of memory!", __FUNCTION__);
199 return;
200 }
201
202 env->SetIntField(object, field_mNativeData, (jint)nat);
203 nat->address =
204 (jstring)env->NewGlobalRef(env->GetObjectField(object,
205 field_mAddress));
206 nat->c_address = env->GetStringUTFChars(nat->address, NULL);
207 nat->rfcomm_channel = env->GetIntField(object, field_mRfcommChannel);
208 nat->rfcomm_sock = socketFd;
209 nat->rfcomm_connected = socketFd >= 0;
210 if (nat->rfcomm_connected)
211 LOGI("%s: ALREADY CONNECTED!", __FUNCTION__);
212#endif
213}
214
215static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800216 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217#ifdef HAVE_BLUETOOTH
218 native_data_t *nat =
219 (native_data_t *)env->GetIntField(object, field_mNativeData);
220 env->ReleaseStringUTFChars(nat->address, nat->c_address);
221 env->DeleteGlobalRef(nat->address);
222 if (nat)
223 free(nat);
224#endif
225}
226
227static jboolean connectNative(JNIEnv *env, jobject obj)
228{
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800229 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230#ifdef HAVE_BLUETOOTH
231 int lm;
232 struct sockaddr_rc addr;
233 native_data_t *nat = get_native_data(env, obj);
234
235 nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
236
237 if (nat->rfcomm_sock < 0) {
238 LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__,
239 strerror(errno));
240 return JNI_FALSE;
241 }
242
243 if (debug_no_encrypt()) {
244 lm = RFCOMM_LM_AUTH;
245 } else {
246 lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
247 }
248
249 if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm,
250 sizeof(lm)) < 0) {
251 LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__);
252 close(nat->rfcomm_sock);
253 return JNI_FALSE;
254 }
255
256 memset(&addr, 0, sizeof(struct sockaddr_rc));
257 get_bdaddr(nat->c_address, &addr.rc_bdaddr);
258 addr.rc_channel = nat->rfcomm_channel;
259 addr.rc_family = AF_BLUETOOTH;
260 nat->rfcomm_connected = 0;
261 while (nat->rfcomm_connected == 0) {
262 if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr,
263 sizeof(addr)) < 0) {
264 if (errno == EINTR) continue;
265 LOGE("%s: connect() failed: %s\n", __FUNCTION__, strerror(errno));
266 close(nat->rfcomm_sock);
267 nat->rfcomm_sock = -1;
268 return JNI_FALSE;
269 } else {
270 nat->rfcomm_connected = 1;
271 }
272 }
273
274 return JNI_TRUE;
275#else
276 return JNI_FALSE;
277#endif
278}
279
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700280static jint connectAsyncNative(JNIEnv *env, jobject obj) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800281 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282#ifdef HAVE_BLUETOOTH
283 struct sockaddr_rc addr;
284 native_data_t *nat = get_native_data(env, obj);
285
286 if (nat->rfcomm_connected) {
287 LOGV("RFCOMM socket is already connected or connection is in progress.");
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700288 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
290
291 if (nat->rfcomm_sock < 0) {
292 int lm;
293
294 nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
295 if (nat->rfcomm_sock < 0) {
296 LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__,
297 strerror(errno));
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700298 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 }
300
301 if (debug_no_encrypt()) {
302 lm = RFCOMM_LM_AUTH;
303 } else {
304 lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
305 }
306
307 if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm,
308 sizeof(lm)) < 0) {
309 LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__);
310 close(nat->rfcomm_sock);
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700311 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313 LOGI("Created RFCOMM socket fd %d.", nat->rfcomm_sock);
314 }
315
316 memset(&addr, 0, sizeof(struct sockaddr_rc));
317 get_bdaddr(nat->c_address, &addr.rc_bdaddr);
318 addr.rc_channel = nat->rfcomm_channel;
319 addr.rc_family = AF_BLUETOOTH;
320 if (nat->rfcomm_sock_flags >= 0) {
321 nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0);
322 if (fcntl(nat->rfcomm_sock,
323 F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) {
324 int rc;
325 nat->rfcomm_connected = 0;
326 errno = 0;
327 rc = connect(nat->rfcomm_sock,
328 (struct sockaddr *)&addr,
329 sizeof(addr));
330
331 if (rc >= 0) {
332 nat->rfcomm_connected = 1;
333 LOGI("async connect successful");
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700334 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 }
336 else if (rc < 0) {
337 if (errno == EINPROGRESS || errno == EAGAIN)
338 {
339 LOGI("async connect is in progress (%s)",
340 strerror(errno));
341 nat->rfcomm_connected = -1;
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700342 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 }
344 else
345 {
346 LOGE("async connect error: %s (%d)", strerror(errno), errno);
347 close(nat->rfcomm_sock);
348 nat->rfcomm_sock = -1;
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700349 return -errno;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
351 }
352 } // fcntl(nat->rfcomm_sock ...)
353 } // if (nat->rfcomm_sock_flags >= 0)
354#endif
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700355 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356}
357
358static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj,
359 jint timeout_ms) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800360 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361#ifdef HAVE_BLUETOOTH
362 struct sockaddr_rc addr;
363 native_data_t *nat = get_native_data(env, obj);
364
365 env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms);
366
367 if (nat->rfcomm_connected > 0) {
368 LOGI("RFCOMM is already connected!");
369 return 1;
370 }
371
372 if (nat->rfcomm_sock >= 0 && nat->rfcomm_connected == 0) {
373 LOGI("Re-opening RFCOMM socket.");
374 close(nat->rfcomm_sock);
375 nat->rfcomm_sock = -1;
376 }
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700377 int ret = connectAsyncNative(env, obj);
378
379 if (ret < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 LOGI("Failed to re-open RFCOMM socket!");
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700381 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 }
383
384 if (nat->rfcomm_sock >= 0) {
385 /* Do an asynchronous select() */
386 int n;
387 fd_set rset, wset;
388 struct timeval to;
389
390 FD_ZERO(&rset);
391 FD_ZERO(&wset);
392 FD_SET(nat->rfcomm_sock, &rset);
393 FD_SET(nat->rfcomm_sock, &wset);
394 if (timeout_ms >= 0) {
395 to.tv_sec = timeout_ms / 1000;
396 to.tv_usec = 1000 * (timeout_ms % 1000);
397 }
398 n = select(nat->rfcomm_sock + 1,
399 &rset,
400 &wset,
401 NULL,
402 (timeout_ms < 0 ? NULL : &to));
403
404 if (timeout_ms > 0) {
405 jint remaining = to.tv_sec*1000 + to.tv_usec/1000;
406 LOGV("Remaining time %ldms", (long)remaining);
407 env->SetIntField(obj, field_mTimeoutRemainingMs,
408 remaining);
409 }
410
411 if (n <= 0) {
412 if (n < 0) {
413 LOGE("select() on RFCOMM socket: %s (%d)",
414 strerror(errno),
415 errno);
Jaikumar Ganesha01a4472009-10-01 19:10:43 -0700416 return -errno;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 }
418 return 0;
419 }
420 /* n must be equal to 1 and either rset or wset must have the
421 file descriptor set. */
422 LOGV("select() returned %d.", n);
423 if (FD_ISSET(nat->rfcomm_sock, &rset) ||
424 FD_ISSET(nat->rfcomm_sock, &wset))
425 {
426 /* A trial async read() will tell us if everything is OK. */
427 {
428 char ch;
429 errno = 0;
430 int nr = read(nat->rfcomm_sock, &ch, 1);
431 /* It should be that nr != 1 because we just opened a socket
432 and we haven't sent anything over it for the other side to
433 respond... but one can't be paranoid enough.
434 */
435 if (nr >= 0 || errno != EAGAIN) {
436 LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n",
437 strerror(errno),
438 errno,
439 nr);
440 /* Clear the rfcomm_connected flag to cause this function
441 to re-create the socket and re-attempt the connect()
442 the next time it is called.
443 */
444 nat->rfcomm_connected = 0;
445 /* Restore the blocking properties of the socket. */
446 fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
447 close(nat->rfcomm_sock);
448 nat->rfcomm_sock = -1;
Jaikumar Ganesha01a4472009-10-01 19:10:43 -0700449 return -errno;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 }
451 }
452 /* Restore the blocking properties of the socket. */
453 fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
454 LOGI("Successful RFCOMM socket connect.");
455 nat->rfcomm_connected = 1;
456 return 1;
457 }
458 }
459 else LOGE("RFCOMM socket file descriptor %d is bad!",
460 nat->rfcomm_sock);
461#endif
462 return -1;
463}
464
465static void disconnectNative(JNIEnv *env, jobject obj) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800466 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467#ifdef HAVE_BLUETOOTH
468 native_data_t *nat = get_native_data(env, obj);
469 if (nat->rfcomm_sock >= 0) {
470 close(nat->rfcomm_sock);
471 nat->rfcomm_sock = -1;
472 nat->rfcomm_connected = 0;
473 }
474#endif
475}
476
477static void pretty_log_urc(const char *urc) {
478 size_t i;
479 bool in_line_break = false;
480 char *buf = (char *)calloc(strlen(urc) + 1, sizeof(char));
481
482 strcpy(buf, urc);
483 for (i = 0; i < strlen(buf); i++) {
484 switch(buf[i]) {
485 case '\r':
486 case '\n':
487 in_line_break = true;
488 buf[i] = ' ';
489 break;
490 default:
491 if (in_line_break) {
492 in_line_break = false;
493 buf[i-1] = '\n';
494 }
495 }
496 }
Nick Pelly9bf39ef2010-06-24 09:38:27 -0700497 IF_LOGV() LOG(LOG_VERBOSE, "Bluetooth AT sent", "%s", buf);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498
499 free(buf);
500}
501
502static jboolean sendURCNative(JNIEnv *env, jobject obj, jstring urc) {
503#ifdef HAVE_BLUETOOTH
504 native_data_t *nat = get_native_data(env, obj);
505 if (nat->rfcomm_connected) {
506 const char *c_urc = env->GetStringUTFChars(urc, NULL);
507 jboolean ret = send_line(nat->rfcomm_sock, c_urc) == 0 ? JNI_TRUE : JNI_FALSE;
508 if (ret == JNI_TRUE) pretty_log_urc(c_urc);
509 env->ReleaseStringUTFChars(urc, c_urc);
510 return ret;
511 }
512#endif
513 return JNI_FALSE;
514}
515
516static jstring readNative(JNIEnv *env, jobject obj, jint timeout_ms) {
517#ifdef HAVE_BLUETOOTH
518 {
519 native_data_t *nat = get_native_data(env, obj);
520 if (nat->rfcomm_connected) {
Nick Pelly8457b0f2010-03-24 18:41:13 -0700521 char buf[256];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 const char *ret = get_line(nat->rfcomm_sock,
523 buf, sizeof(buf),
524 timeout_ms,
525 &nat->last_read_err);
526 return ret ? env->NewStringUTF(ret) : NULL;
527 }
528 return NULL;
529 }
530#else
531 return NULL;
532#endif
533}
534
535static jint getLastReadStatusNative(JNIEnv *env, jobject obj) {
536#ifdef HAVE_BLUETOOTH
537 {
538 native_data_t *nat = get_native_data(env, obj);
539 if (nat->rfcomm_connected)
540 return (jint)nat->last_read_err;
541 return 0;
542 }
543#else
544 return 0;
545#endif
546}
547
548static JNINativeMethod sMethods[] = {
549 /* name, signature, funcPtr */
550 {"classInitNative", "()V", (void*)classInitNative},
551 {"initializeNativeDataNative", "(I)V", (void *)initializeNativeDataNative},
552 {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
553 {"connectNative", "()Z", (void *)connectNative},
Jaikumar Ganesh54172d92009-09-29 13:00:30 -0700554 {"connectAsyncNative", "()I", (void *)connectAsyncNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative},
556 {"disconnectNative", "()V", (void *)disconnectNative},
557 {"sendURCNative", "(Ljava/lang/String;)Z", (void *)sendURCNative},
558 {"readNative", "(I)Ljava/lang/String;", (void *)readNative},
559 {"getLastReadStatusNative", "()I", (void *)getLastReadStatusNative},
560};
561
562int register_android_bluetooth_HeadsetBase(JNIEnv *env) {
563 return AndroidRuntime::registerNativeMethods(env,
564 "android/bluetooth/HeadsetBase", sMethods, NELEM(sMethods));
565}
566
567} /* namespace android */