The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 42 | namespace android { |
| 43 | |
| 44 | #ifdef HAVE_BLUETOOTH |
| 45 | static jfieldID field_mNativeData; |
| 46 | static jfieldID field_mAddress; |
| 47 | static jfieldID field_mRfcommChannel; |
| 48 | static jfieldID field_mTimeoutRemainingMs; |
| 49 | |
| 50 | typedef 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 | |
| 60 | static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { |
| 61 | return (native_data_t *)(env->GetIntField(object, field_mNativeData)); |
| 62 | } |
| 63 | |
| 64 | static const char CRLF[] = "\xd\xa"; |
| 65 | static const int CRLF_LEN = 2; |
| 66 | |
| 67 | static 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 | |
| 83 | static 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 Xie | fe90059 | 2011-03-21 12:07:04 -0700 | [diff] [blame^] | 99 | static void mask_eighth_bit(char *line) |
| 100 | { |
| 101 | for (;;line++) { |
| 102 | if (0 == *line) return; |
| 103 | *line &= 0x7F; |
| 104 | } |
Nick Pelly | 8457b0f | 2010-03-24 18:41:13 -0700 | [diff] [blame] | 105 | } |
| 106 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 107 | static 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 | |
| 113 | again: |
| 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 Pelly | 8457b0f | 2010-03-24 18:41:13 -0700 | [diff] [blame] | 136 | while ((int)(bufit - buf) < (len - 1)) |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 137 | { |
| 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 Pelly | 8457b0f | 2010-03-24 18:41:13 -0700 | [diff] [blame] | 166 | *bufit = NULL; |
| 167 | |
Matthew Xie | fe90059 | 2011-03-21 12:07:04 -0700 | [diff] [blame^] | 168 | // 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 Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 177 | |
| 178 | return buf; |
| 179 | } |
| 180 | #endif |
| 181 | |
| 182 | static void classInitNative(JNIEnv* env, jclass clazz) { |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 183 | LOGV("%s", __FUNCTION__); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 184 | #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 | |
| 192 | static void initializeNativeDataNative(JNIEnv* env, jobject object, |
| 193 | jint socketFd) { |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 194 | LOGV("%s", __FUNCTION__); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 195 | #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 | |
| 215 | static void cleanupNativeDataNative(JNIEnv* env, jobject object) { |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 216 | LOGV("%s", __FUNCTION__); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 217 | #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 | |
| 227 | static jboolean connectNative(JNIEnv *env, jobject obj) |
| 228 | { |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 229 | LOGV("%s", __FUNCTION__); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 230 | #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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 280 | static jint connectAsyncNative(JNIEnv *env, jobject obj) { |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 281 | LOGV("%s", __FUNCTION__); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 282 | #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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 288 | return 0; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 289 | } |
| 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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 298 | return -1; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 299 | } |
| 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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 311 | return -1; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 312 | } |
| 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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 334 | return 0; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 335 | } |
| 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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 342 | return 0; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 343 | } |
| 344 | else |
| 345 | { |
| 346 | LOGE("async connect error: %s (%d)", strerror(errno), errno); |
| 347 | close(nat->rfcomm_sock); |
| 348 | nat->rfcomm_sock = -1; |
Jaikumar Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 349 | return -errno; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 350 | } |
| 351 | } |
| 352 | } // fcntl(nat->rfcomm_sock ...) |
| 353 | } // if (nat->rfcomm_sock_flags >= 0) |
| 354 | #endif |
Jaikumar Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 355 | return -1; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, |
| 359 | jint timeout_ms) { |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 360 | LOGV("%s", __FUNCTION__); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 361 | #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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 377 | int ret = connectAsyncNative(env, obj); |
| 378 | |
| 379 | if (ret < 0) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 380 | LOGI("Failed to re-open RFCOMM socket!"); |
Jaikumar Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 381 | return ret; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 382 | } |
| 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 Ganesh | a01a447 | 2009-10-01 19:10:43 -0700 | [diff] [blame] | 416 | return -errno; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 417 | } |
| 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 Ganesh | a01a447 | 2009-10-01 19:10:43 -0700 | [diff] [blame] | 449 | return -errno; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 450 | } |
| 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 | |
| 465 | static void disconnectNative(JNIEnv *env, jobject obj) { |
Jaikumar Ganesh | 2653a1e | 2011-02-22 10:53:29 -0800 | [diff] [blame] | 466 | LOGV("%s", __FUNCTION__); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 467 | #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 | |
| 477 | static 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 Pelly | 9bf39ef | 2010-06-24 09:38:27 -0700 | [diff] [blame] | 497 | IF_LOGV() LOG(LOG_VERBOSE, "Bluetooth AT sent", "%s", buf); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 498 | |
| 499 | free(buf); |
| 500 | } |
| 501 | |
| 502 | static 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 | |
| 516 | static 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 Pelly | 8457b0f | 2010-03-24 18:41:13 -0700 | [diff] [blame] | 521 | char buf[256]; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 522 | 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 | |
| 535 | static 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 | |
| 548 | static 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 Ganesh | 54172d9 | 2009-09-29 13:00:30 -0700 | [diff] [blame] | 554 | {"connectAsyncNative", "()I", (void *)connectAsyncNative}, |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 555 | {"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 | |
| 562 | int register_android_bluetooth_HeadsetBase(JNIEnv *env) { |
| 563 | return AndroidRuntime::registerNativeMethods(env, |
| 564 | "android/bluetooth/HeadsetBase", sMethods, NELEM(sMethods)); |
| 565 | } |
| 566 | |
| 567 | } /* namespace android */ |