blob: e9c04a54f49aa6c4d444c716201a7b6900a64b22 [file] [log] [blame]
/*
* Copyright 2009, 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 "BluetoothSocket.cpp"
#include "android_bluetooth_common.h"
#include "android_runtime/AndroidRuntime.h"
#include "JNIHelp.h"
#include "utils/Log.h"
#include "cutils/abort_socket.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#ifdef HAVE_BLUETOOTH
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#endif
namespace android {
static jfieldID field_mAuth; /* read-only */
static jfieldID field_mEncrypt; /* read-only */
static jfieldID field_mSocketData;
static jmethodID method_BluetoothSocket_ctor;
static jclass class_BluetoothSocket;
static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
struct asocket *s =
(struct asocket *) env->GetIntField(obj, field_mSocketData);
if (!s)
jniThrowException(env, "java/io/IOException", "null socketData");
return s;
}
static void initSocketFromFdNative(JNIEnv *env, jobject obj, jint fd) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
struct asocket *s = asocket_init(fd);
if (!s) {
LOGV("asocket_init() failed, throwing");
jniThrowIOException(env, errno);
return;
}
env->SetIntField(obj, field_mSocketData, (jint)s);
return;
#endif
jniThrowIOException(env, ENOSYS);
}
static void initSocketNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int fd;
int lm = 0;
jboolean auth;
jboolean encrypt;
/*TODO: do not hardcode to rfcomm */
fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (fd < 0) {
LOGV("socket() failed, throwing");
jniThrowIOException(env, errno);
return;
}
auth = env->GetBooleanField(obj, field_mAuth);
encrypt = env->GetBooleanField(obj, field_mEncrypt);
lm |= auth ? RFCOMM_LM_AUTH : 0;
lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
if (lm) {
if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
LOGV("setsockopt() failed, throwing");
jniThrowIOException(env, errno);
return;
}
}
initSocketFromFdNative(env, obj, fd);
return;
#endif
jniThrowIOException(env, ENOSYS);
}
static void connectNative(JNIEnv *env, jobject obj, jstring address,
jint port, jint timeout) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int ret;
struct sockaddr_rc addr;
const char *c_address;
struct asocket *s = get_socketData(env, obj);
if (!s)
return;
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = port;
c_address = env->GetStringUTFChars(address, NULL);
if (get_bdaddr((const char *)c_address, &addr.rc_bdaddr)) {
env->ReleaseStringUTFChars(address, c_address);
jniThrowIOException(env, EINVAL);
return;
}
env->ReleaseStringUTFChars(address, c_address);
ret = asocket_connect(s, (struct sockaddr *)&addr, sizeof(addr), timeout);
if (ret)
jniThrowIOException(env, errno);
return;
#endif
jniThrowIOException(env, ENOSYS);
}
static void bindListenNative(JNIEnv *env, jobject obj, jint port) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
struct sockaddr_rc addr;
struct asocket *s = get_socketData(env, obj);
if (!s)
return;
memset(&addr, 0, sizeof(struct sockaddr_rc));
addr.rc_family = AF_BLUETOOTH;
addr.rc_bdaddr = *BDADDR_ANY;
addr.rc_channel = port;
if (bind(s->fd, (struct sockaddr *)&addr, sizeof(addr))) {
jniThrowIOException(env, errno);
return;
}
if (listen(s->fd, 1)) {
jniThrowIOException(env, errno);
return;
}
return;
#endif
jniThrowIOException(env, ENOSYS);
}
static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int fd;
struct sockaddr_rc addr;
int addrlen = sizeof(addr);
jstring addr_jstr;
char addr_cstr[BTADDR_SIZE];
jboolean auth;
jboolean encrypt;
struct asocket *s = get_socketData(env, obj);
if (!s)
return NULL;
fd = asocket_accept(s, (struct sockaddr *)&addr, &addrlen, timeout);
if (fd < 0) {
jniThrowIOException(env, errno);
return NULL;
}
/* Connected - return new BluetoothSocket */
auth = env->GetBooleanField(obj, field_mAuth);
encrypt = env->GetBooleanField(obj, field_mEncrypt);
get_bdaddr_as_string(&addr.rc_bdaddr, addr_cstr);
addr_jstr = env->NewStringUTF(addr_cstr);
return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor, fd,
auth, encrypt, addr_jstr, -1);
#endif
jniThrowIOException(env, ENOSYS);
return NULL;
}
static jint availableNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int available;
struct asocket *s = get_socketData(env, obj);
if (!s)
return -1;
if (ioctl(s->fd, FIONREAD, &available) < 0) {
jniThrowIOException(env, errno);
return -1;
}
return available;
#endif
jniThrowIOException(env, ENOSYS);
return -1;
}
/** jb must not be null. offset and offset+length must be within array */
static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
jint length) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int ret;
jbyte *b;
struct asocket *s = get_socketData(env, obj);
if (!s)
return -1;
b = env->GetByteArrayElements(jb, NULL);
if (b == NULL) {
jniThrowIOException(env, EINVAL);
return -1;
}
ret = asocket_read(s, &b[offset], length, -1);
if (ret < 0) {
jniThrowIOException(env, errno);
env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
return -1;
}
env->ReleaseByteArrayElements(jb, b, 0);
return (jint)ret;
#endif
jniThrowIOException(env, ENOSYS);
return -1;
}
/** jb must not be null. offset and offset+length must be within array */
static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
jint length) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int ret;
jbyte *b;
struct asocket *s = get_socketData(env, obj);
if (!s)
return -1;
b = env->GetByteArrayElements(jb, NULL);
if (b == NULL) {
jniThrowIOException(env, EINVAL);
return -1;
}
ret = asocket_write(s, &b[offset], length, -1);
if (ret < 0) {
jniThrowIOException(env, errno);
env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
return -1;
}
env->ReleaseByteArrayElements(jb, b, JNI_ABORT); // no need to commit
return (jint)ret;
#endif
jniThrowIOException(env, ENOSYS);
return -1;
}
static void closeNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
struct asocket *s = get_socketData(env, obj);
if (!s)
return;
asocket_abort(s);
return;
#endif
jniThrowIOException(env, ENOSYS);
}
static void destroyNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
struct asocket *s = get_socketData(env, obj);
if (!s)
return;
asocket_destroy(s);
return;
#endif
jniThrowIOException(env, ENOSYS);
}
static JNINativeMethod sMethods[] = {
{"initSocketNative", "()V", (void*) initSocketNative},
{"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative},
{"connectNative", "(Ljava/lang/String;II)V", (void *) connectNative},
{"bindListenNative", "(I)V", (void *) bindListenNative},
{"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
{"availableNative", "()I", (void *) availableNative},
{"readNative", "([BII)I", (void *) readNative},
{"writeNative", "([BII)I", (void *) writeNative},
{"closeNative", "()V", (void *) closeNative},
{"destroyNative", "()V", (void *) destroyNative},
};
int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
jclass clazz = env->FindClass("android/bluetooth/BluetoothSocket");
if (clazz == NULL)
return -1;
class_BluetoothSocket = (jclass) env->NewGlobalRef(clazz);
field_mAuth = env->GetFieldID(clazz, "mAuth", "Z");
field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z");
field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I");
method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IZZLjava/lang/String;I)V");
return AndroidRuntime::registerNativeMethods(env,
"android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
}
} /* namespace android */