/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #define LOG_TAG "BT HSHFP" #include "android_bluetooth_common.h" #include "android_runtime/AndroidRuntime.h" #include "JNIHelp.h" #include "jni.h" #include "utils/Log.h" #include "utils/misc.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_BLUETOOTH #include #include #include #endif namespace android { #ifdef HAVE_BLUETOOTH static jfieldID field_mNativeData; static jfieldID field_mAddress; static jfieldID field_mRfcommChannel; static jfieldID field_mTimeoutRemainingMs; typedef struct { jstring address; const char *c_address; int rfcomm_channel; int last_read_err; int rfcomm_sock; int rfcomm_connected; // -1 in progress, 0 not connected, 1 connected int rfcomm_sock_flags; } native_data_t; static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { return (native_data_t *)(env->GetIntField(object, field_mNativeData)); } static const char CRLF[] = "\xd\xa"; static const int CRLF_LEN = 2; static inline int write_error_check(int fd, const char* line, int len) { int ret; errno = 0; ret = write(fd, line, len); if (ret < 0) { LOGE("%s: write() failed: %s (%d)", __FUNCTION__, strerror(errno), errno); return -1; } if (ret != len) { LOGE("%s: write() only wrote %d of %d bytes", __FUNCTION__, ret, len); return -1; } return 0; } static int send_line(int fd, const char* line) { int nw; int len = strlen(line); int llen = len + CRLF_LEN * 2 + 1; char *buffer = (char *)calloc(llen, sizeof(char)); snprintf(buffer, llen, "%s%s%s", CRLF, line, CRLF); if (write_error_check(fd, buffer, llen - 1)) { free(buffer); return -1; } free(buffer); return 0; } static int is_ascii(char *line) { for (;;line++) { if (*line == 0) return 1; if (*line >> 7) return 0; } } static const char* get_line(int fd, char *buf, int len, int timeout_ms, int *err) { char *bufit=buf; int fd_flags = fcntl(fd, F_GETFL, 0); struct pollfd pfd; again: *bufit = 0; pfd.fd = fd; pfd.events = POLLIN; *err = errno = 0; int ret = poll(&pfd, 1, timeout_ms); if (ret < 0) { LOGE("poll() error\n"); *err = errno; return NULL; } if (ret == 0) { return NULL; } if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) { LOGW("RFCOMM poll() returned success (%d), " "but with an unexpected revents bitmask: %#x\n", ret, pfd.revents); errno = EIO; *err = errno; return NULL; } while ((int)(bufit - buf) < (len - 1)) { errno = 0; int rc = read(fd, bufit, 1); if (!rc) break; if (rc < 0) { if (errno == EBUSY) { LOGI("read() error %s (%d): repeating read()...", strerror(errno), errno); goto again; } *err = errno; LOGE("read() error %s (%d)", strerror(errno), errno); return NULL; } if (*bufit=='\xd') { break; } if (*bufit=='\xa') bufit = buf; else bufit++; } *bufit = NULL; // Simple validation. Must be all ASCII. // (we sometimes send non-ASCII UTF-8 in address book, but should // never receive non-ASCII UTF-8). // This was added because of the BMW 2005 E46 which sends binary junk. if (is_ascii(buf)) { IF_LOGV() LOG(LOG_VERBOSE, "Bluetooth AT recv", buf); } else { LOGW("Ignoring invalid AT command: %s", buf); buf[0] = NULL; } return buf; } #endif static void classInitNative(JNIEnv* env, jclass clazz) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH field_mNativeData = get_field(env, clazz, "mNativeData", "I"); field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;"); field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I"); field_mRfcommChannel = get_field(env, clazz, "mRfcommChannel", "I"); #endif } static void initializeNativeDataNative(JNIEnv* env, jobject object, jint socketFd) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); if (NULL == nat) { LOGE("%s: out of memory!", __FUNCTION__); return; } env->SetIntField(object, field_mNativeData, (jint)nat); nat->address = (jstring)env->NewGlobalRef(env->GetObjectField(object, field_mAddress)); nat->c_address = env->GetStringUTFChars(nat->address, NULL); nat->rfcomm_channel = env->GetIntField(object, field_mRfcommChannel); nat->rfcomm_sock = socketFd; nat->rfcomm_connected = socketFd >= 0; if (nat->rfcomm_connected) LOGI("%s: ALREADY CONNECTED!", __FUNCTION__); #endif } static void cleanupNativeDataNative(JNIEnv* env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = (native_data_t *)env->GetIntField(object, field_mNativeData); env->ReleaseStringUTFChars(nat->address, nat->c_address); env->DeleteGlobalRef(nat->address); if (nat) free(nat); #endif } static jboolean connectNative(JNIEnv *env, jobject obj) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH int lm; struct sockaddr_rc addr; native_data_t *nat = get_native_data(env, obj); nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (nat->rfcomm_sock < 0) { LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__, strerror(errno)); return JNI_FALSE; } if (debug_no_encrypt()) { lm = RFCOMM_LM_AUTH; } else { lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; } if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__); close(nat->rfcomm_sock); return JNI_FALSE; } memset(&addr, 0, sizeof(struct sockaddr_rc)); get_bdaddr(nat->c_address, &addr.rc_bdaddr); addr.rc_channel = nat->rfcomm_channel; addr.rc_family = AF_BLUETOOTH; nat->rfcomm_connected = 0; while (nat->rfcomm_connected == 0) { if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { if (errno == EINTR) continue; LOGE("%s: connect() failed: %s\n", __FUNCTION__, strerror(errno)); close(nat->rfcomm_sock); nat->rfcomm_sock = -1; return JNI_FALSE; } else { nat->rfcomm_connected = 1; } } return JNI_TRUE; #else return JNI_FALSE; #endif } static jint connectAsyncNative(JNIEnv *env, jobject obj) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH struct sockaddr_rc addr; native_data_t *nat = get_native_data(env, obj); if (nat->rfcomm_connected) { LOGV("RFCOMM socket is already connected or connection is in progress."); return 0; } if (nat->rfcomm_sock < 0) { int lm; nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (nat->rfcomm_sock < 0) { LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__, strerror(errno)); return -1; } if (debug_no_encrypt()) { lm = RFCOMM_LM_AUTH; } else { lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; } if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__); close(nat->rfcomm_sock); return -1; } LOGI("Created RFCOMM socket fd %d.", nat->rfcomm_sock); } memset(&addr, 0, sizeof(struct sockaddr_rc)); get_bdaddr(nat->c_address, &addr.rc_bdaddr); addr.rc_channel = nat->rfcomm_channel; addr.rc_family = AF_BLUETOOTH; if (nat->rfcomm_sock_flags >= 0) { nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0); if (fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) { int rc; nat->rfcomm_connected = 0; errno = 0; rc = connect(nat->rfcomm_sock, (struct sockaddr *)&addr, sizeof(addr)); if (rc >= 0) { nat->rfcomm_connected = 1; LOGI("async connect successful"); return 0; } else if (rc < 0) { if (errno == EINPROGRESS || errno == EAGAIN) { LOGI("async connect is in progress (%s)", strerror(errno)); nat->rfcomm_connected = -1; return 0; } else { LOGE("async connect error: %s (%d)", strerror(errno), errno); close(nat->rfcomm_sock); nat->rfcomm_sock = -1; return -errno; } } } // fcntl(nat->rfcomm_sock ...) } // if (nat->rfcomm_sock_flags >= 0) #endif return -1; } static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj, jint timeout_ms) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH struct sockaddr_rc addr; native_data_t *nat = get_native_data(env, obj); env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms); if (nat->rfcomm_connected > 0) { LOGI("RFCOMM is already connected!"); return 1; } if (nat->rfcomm_sock >= 0 && nat->rfcomm_connected == 0) { LOGI("Re-opening RFCOMM socket."); close(nat->rfcomm_sock); nat->rfcomm_sock = -1; } int ret = connectAsyncNative(env, obj); if (ret < 0) { LOGI("Failed to re-open RFCOMM socket!"); return ret; } if (nat->rfcomm_sock >= 0) { /* Do an asynchronous select() */ int n; fd_set rset, wset; struct timeval to; FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(nat->rfcomm_sock, &rset); FD_SET(nat->rfcomm_sock, &wset); if (timeout_ms >= 0) { to.tv_sec = timeout_ms / 1000; to.tv_usec = 1000 * (timeout_ms % 1000); } n = select(nat->rfcomm_sock + 1, &rset, &wset, NULL, (timeout_ms < 0 ? NULL : &to)); if (timeout_ms > 0) { jint remaining = to.tv_sec*1000 + to.tv_usec/1000; LOGV("Remaining time %ldms", (long)remaining); env->SetIntField(obj, field_mTimeoutRemainingMs, remaining); } if (n <= 0) { if (n < 0) { LOGE("select() on RFCOMM socket: %s (%d)", strerror(errno), errno); return -errno; } return 0; } /* n must be equal to 1 and either rset or wset must have the file descriptor set. */ LOGV("select() returned %d.", n); if (FD_ISSET(nat->rfcomm_sock, &rset) || FD_ISSET(nat->rfcomm_sock, &wset)) { /* A trial async read() will tell us if everything is OK. */ { char ch; errno = 0; int nr = read(nat->rfcomm_sock, &ch, 1); /* It should be that nr != 1 because we just opened a socket and we haven't sent anything over it for the other side to respond... but one can't be paranoid enough. */ if (nr >= 0 || errno != EAGAIN) { LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n", strerror(errno), errno, nr); /* Clear the rfcomm_connected flag to cause this function to re-create the socket and re-attempt the connect() the next time it is called. */ nat->rfcomm_connected = 0; /* Restore the blocking properties of the socket. */ fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); close(nat->rfcomm_sock); nat->rfcomm_sock = -1; return -errno; } } /* Restore the blocking properties of the socket. */ fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags); LOGI("Successful RFCOMM socket connect."); nat->rfcomm_connected = 1; return 1; } } else LOGE("RFCOMM socket file descriptor %d is bad!", nat->rfcomm_sock); #endif return -1; } static void disconnectNative(JNIEnv *env, jobject obj) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, obj); if (nat->rfcomm_sock >= 0) { close(nat->rfcomm_sock); nat->rfcomm_sock = -1; nat->rfcomm_connected = 0; } #endif } static void pretty_log_urc(const char *urc) { size_t i; bool in_line_break = false; char *buf = (char *)calloc(strlen(urc) + 1, sizeof(char)); strcpy(buf, urc); for (i = 0; i < strlen(buf); i++) { switch(buf[i]) { case '\r': case '\n': in_line_break = true; buf[i] = ' '; break; default: if (in_line_break) { in_line_break = false; buf[i-1] = '\n'; } } } IF_LOGV() LOG(LOG_VERBOSE, "Bluetooth AT sent", buf); free(buf); } static jboolean sendURCNative(JNIEnv *env, jobject obj, jstring urc) { #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, obj); if (nat->rfcomm_connected) { const char *c_urc = env->GetStringUTFChars(urc, NULL); jboolean ret = send_line(nat->rfcomm_sock, c_urc) == 0 ? JNI_TRUE : JNI_FALSE; if (ret == JNI_TRUE) pretty_log_urc(c_urc); env->ReleaseStringUTFChars(urc, c_urc); return ret; } #endif return JNI_FALSE; } static jstring readNative(JNIEnv *env, jobject obj, jint timeout_ms) { #ifdef HAVE_BLUETOOTH { native_data_t *nat = get_native_data(env, obj); if (nat->rfcomm_connected) { char buf[256]; const char *ret = get_line(nat->rfcomm_sock, buf, sizeof(buf), timeout_ms, &nat->last_read_err); return ret ? env->NewStringUTF(ret) : NULL; } return NULL; } #else return NULL; #endif } static jint getLastReadStatusNative(JNIEnv *env, jobject obj) { #ifdef HAVE_BLUETOOTH { native_data_t *nat = get_native_data(env, obj); if (nat->rfcomm_connected) return (jint)nat->last_read_err; return 0; } #else return 0; #endif } static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, {"initializeNativeDataNative", "(I)V", (void *)initializeNativeDataNative}, {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, {"connectNative", "()Z", (void *)connectNative}, {"connectAsyncNative", "()I", (void *)connectAsyncNative}, {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative}, {"disconnectNative", "()V", (void *)disconnectNative}, {"sendURCNative", "(Ljava/lang/String;)Z", (void *)sendURCNative}, {"readNative", "(I)Ljava/lang/String;", (void *)readNative}, {"getLastReadStatusNative", "()I", (void *)getLastReadStatusNative}, }; int register_android_bluetooth_HeadsetBase(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, "android/bluetooth/HeadsetBase", sMethods, NELEM(sMethods)); } } /* namespace android */