688 lines
19 KiB
C++
688 lines
19 KiB
C++
/*
|
|
* Copyright 2009, The Android Open Source Project
|
|
* Copyright (c) 2011, The Linux Foundation. All rights reserved.
|
|
*
|
|
* 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>
|
|
|
|
#include <cutils/properties.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_BLUETOOTH
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/rfcomm.h>
|
|
#include <bluetooth/l2cap.h>
|
|
#include <bluetooth/sco.h>
|
|
#endif
|
|
|
|
#define TYPE_AS_STR(t) \
|
|
((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : \
|
|
((t) == TYPE_L2CAP ? "L2CAP" : "EL2CAP")))
|
|
|
|
/* dscherba: HACK changing to 65000 to allow for ERTM overhead--hopefully this will not get tossed by the kernel in negotiations */
|
|
#define L2CAP_MAX_MTU 65000
|
|
|
|
namespace android {
|
|
|
|
static jfieldID field_mAuth; /* read-only */
|
|
static jfieldID field_mEncrypt; /* read-only */
|
|
static jfieldID field_mType; /* read-only */
|
|
static jfieldID field_mAddress; /* read-only */
|
|
static jfieldID field_mPort; /* read-only */
|
|
static jfieldID field_mSocketData;
|
|
static jmethodID method_BluetoothSocket_ctor;
|
|
static jclass class_BluetoothSocket;
|
|
|
|
/* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */
|
|
static const int TYPE_RFCOMM = 1;
|
|
static const int TYPE_SCO = 2;
|
|
static const int TYPE_L2CAP = 3;
|
|
static const int TYPE_EL2CAP = 4;
|
|
|
|
static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer
|
|
static const int L2CAP_SO_SNDBUF = 400 * 1024; // 400 KB send buffer
|
|
static const int L2CAP_SO_RCVBUF = 400 * 1024; // 400 KB receive buffer
|
|
|
|
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;
|
|
int sndbuf, rcvbuf;
|
|
jboolean auth;
|
|
jboolean encrypt;
|
|
jint type;
|
|
char value[PROPERTY_VALUE_MAX] = "";
|
|
|
|
type = env->GetIntField(obj, field_mType);
|
|
|
|
switch (type) {
|
|
case TYPE_RFCOMM:
|
|
fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
|
break;
|
|
case TYPE_SCO:
|
|
fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
|
|
break;
|
|
case TYPE_L2CAP:
|
|
fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
|
|
break;
|
|
case TYPE_EL2CAP:
|
|
fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP);
|
|
break;
|
|
default:
|
|
jniThrowIOException(env, ENOSYS);
|
|
return;
|
|
}
|
|
|
|
if (fd < 0) {
|
|
LOGV("socket() failed, throwing");
|
|
jniThrowIOException(env, errno);
|
|
return;
|
|
}
|
|
|
|
auth = env->GetBooleanField(obj, field_mAuth);
|
|
encrypt = env->GetBooleanField(obj, field_mEncrypt);
|
|
|
|
/* kernel does not yet support LM for SCO */
|
|
|
|
/* By default we request to be the MASTER of connection */
|
|
property_get("ro.bluetooth.request.master", value, "true");
|
|
switch (type) {
|
|
case TYPE_RFCOMM:
|
|
lm |= auth ? RFCOMM_LM_AUTH : 0;
|
|
lm |= encrypt ? RFCOMM_LM_ENCRYPT : 0;
|
|
if (!strcmp("true", value)) {
|
|
LOGI("Setting Master socket option");
|
|
lm |= RFCOMM_LM_MASTER;
|
|
}
|
|
break;
|
|
case TYPE_L2CAP:
|
|
case TYPE_EL2CAP:
|
|
lm |= auth ? L2CAP_LM_AUTH : 0;
|
|
lm |= encrypt ? L2CAP_LM_ENCRYPT : 0;
|
|
if (!strcmp("true", value)) {
|
|
LOGI("Setting Master socket option");
|
|
lm |= L2CAP_LM_MASTER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (lm) {
|
|
if (type == TYPE_RFCOMM) {
|
|
if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
|
|
LOGV("setsockopt(RFCOMM_LM) failed, throwing");
|
|
close(fd);
|
|
jniThrowIOException(env, errno);
|
|
return;
|
|
}
|
|
} else if (type == TYPE_L2CAP || type == TYPE_EL2CAP) {
|
|
if (setsockopt(fd, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm))) {
|
|
LOGV("setsockopt(L2CAP_LM) failed, throwing");
|
|
close(fd);
|
|
jniThrowIOException(env, errno);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == TYPE_RFCOMM) {
|
|
sndbuf = RFCOMM_SO_SNDBUF;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
|
|
LOGV("setsockopt(SO_SNDBUF) failed, throwing");
|
|
close(fd);
|
|
jniThrowIOException(env, errno);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Setting L2CAP socket options */
|
|
if (type == TYPE_L2CAP || type == TYPE_EL2CAP) {
|
|
struct l2cap_options opts;
|
|
int optlen = sizeof(opts), err;
|
|
err = getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen );
|
|
if (!err) {
|
|
/* setting MTU for [E]L2CAP */
|
|
opts.omtu = opts.imtu = L2CAP_MAX_MTU;
|
|
|
|
/* Enable ERTM for [E]L2CAP */
|
|
if (type == TYPE_EL2CAP) {
|
|
opts.flush_to = 0xffff; /* infinite */
|
|
opts.mode = L2CAP_MODE_ERTM;
|
|
opts.fcs = 1;
|
|
opts.txwin_size = 64;
|
|
opts.max_tx = 10;
|
|
}
|
|
err = setsockopt( fd, SOL_L2CAP, L2CAP_OPTIONS, &opts, optlen );
|
|
}
|
|
|
|
/* Set larger SNDBUF & RCVBUF for EL2CAP connections */
|
|
if (type == TYPE_EL2CAP) {
|
|
sndbuf = L2CAP_SO_SNDBUF;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
|
|
LOGV("setsockopt(SO_SNDBUF) failed, throwing");
|
|
close(fd);
|
|
jniThrowIOException(env, errno);
|
|
return;
|
|
}
|
|
|
|
rcvbuf = L2CAP_SO_RCVBUF;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))) {
|
|
LOGV("setsockopt(SO_RCVBUF) failed, throwing");
|
|
close(fd);
|
|
jniThrowIOException(env, errno);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm);
|
|
|
|
initSocketFromFdNative(env, obj, fd);
|
|
return;
|
|
#endif
|
|
jniThrowIOException(env, ENOSYS);
|
|
}
|
|
|
|
static void setAmpPolicyNative(JNIEnv *env, jobject obj, jint amppol) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (!s)
|
|
return;
|
|
|
|
int err;
|
|
err = setsockopt(s->fd, SOL_BLUETOOTH, BT_AMP_POLICY, &pol, sizeof(amppol));
|
|
if (err) {
|
|
LOGV("setsockopt() failed, throwing");
|
|
jniThrowIOException(env, errno);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
#endif
|
|
jniThrowIOException(env, ENOSYS);
|
|
}
|
|
|
|
static int getMtuNative(JNIEnv *env, jobject obj) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
|
|
jint type = env->GetIntField(obj, field_mType);
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (type == TYPE_RFCOMM || !s)
|
|
return L2CAP_MAX_MTU;
|
|
|
|
struct l2cap_options opts;
|
|
int optlen = sizeof(opts), err;
|
|
err = getsockopt(s->fd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen );
|
|
if (!err) {
|
|
return opts.omtu;
|
|
} else {
|
|
LOGV("getsockopt() failed, throwing");
|
|
jniThrowIOException(env, errno);
|
|
}
|
|
|
|
return L2CAP_MAX_MTU;
|
|
#endif
|
|
jniThrowIOException(env, ENOSYS);
|
|
return -1;
|
|
}
|
|
|
|
static void connectNative(JNIEnv *env, jobject obj) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
|
|
int ret;
|
|
jint type;
|
|
const char *c_address;
|
|
jstring address;
|
|
bdaddr_t bdaddress;
|
|
socklen_t addr_sz;
|
|
struct sockaddr *addr;
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (!s)
|
|
return;
|
|
|
|
type = env->GetIntField(obj, field_mType);
|
|
|
|
/* parse address into bdaddress */
|
|
address = (jstring) env->GetObjectField(obj, field_mAddress);
|
|
c_address = env->GetStringUTFChars(address, NULL);
|
|
if (get_bdaddr(c_address, &bdaddress)) {
|
|
env->ReleaseStringUTFChars(address, c_address);
|
|
jniThrowIOException(env, EINVAL);
|
|
return;
|
|
}
|
|
env->ReleaseStringUTFChars(address, c_address);
|
|
|
|
switch (type) {
|
|
case TYPE_RFCOMM:
|
|
struct sockaddr_rc addr_rc;
|
|
addr = (struct sockaddr *)&addr_rc;
|
|
addr_sz = sizeof(addr_rc);
|
|
|
|
memset(addr, 0, addr_sz);
|
|
addr_rc.rc_family = AF_BLUETOOTH;
|
|
addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
|
|
memcpy(&addr_rc.rc_bdaddr, &bdaddress, sizeof(bdaddr_t));
|
|
|
|
break;
|
|
case TYPE_SCO:
|
|
struct sockaddr_sco addr_sco;
|
|
addr = (struct sockaddr *)&addr_sco;
|
|
addr_sz = sizeof(addr_sco);
|
|
|
|
memset(addr, 0, addr_sz);
|
|
addr_sco.sco_family = AF_BLUETOOTH;
|
|
memcpy(&addr_sco.sco_bdaddr, &bdaddress, sizeof(bdaddr_t));
|
|
|
|
break;
|
|
case TYPE_L2CAP:
|
|
case TYPE_EL2CAP:
|
|
struct sockaddr_l2 addr_l2;
|
|
addr = (struct sockaddr *)&addr_l2;
|
|
addr_sz = sizeof(addr_l2);
|
|
|
|
memset(addr, 0, addr_sz);
|
|
addr_l2.l2_family = AF_BLUETOOTH;
|
|
addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
|
|
memcpy(&addr_l2.l2_bdaddr, &bdaddress, sizeof(bdaddr_t));
|
|
|
|
break;
|
|
default:
|
|
jniThrowIOException(env, ENOSYS);
|
|
return;
|
|
}
|
|
|
|
ret = asocket_connect(s, addr, addr_sz, -1);
|
|
LOGV("...connect(%d, %s) = %d (errno %d)",
|
|
s->fd, TYPE_AS_STR(type), ret, errno);
|
|
|
|
if (ret)
|
|
jniThrowIOException(env, errno);
|
|
|
|
return;
|
|
#endif
|
|
jniThrowIOException(env, ENOSYS);
|
|
}
|
|
|
|
/* Returns errno instead of throwing, so java can check errno */
|
|
static int bindListenNative(JNIEnv *env, jobject obj) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
|
|
jint type;
|
|
socklen_t addr_sz;
|
|
struct sockaddr *addr;
|
|
bdaddr_t bdaddr = *BDADDR_ANY;
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (!s)
|
|
return EINVAL;
|
|
|
|
type = env->GetIntField(obj, field_mType);
|
|
|
|
switch (type) {
|
|
case TYPE_RFCOMM:
|
|
struct sockaddr_rc addr_rc;
|
|
addr = (struct sockaddr *)&addr_rc;
|
|
addr_sz = sizeof(addr_rc);
|
|
|
|
memset(addr, 0, addr_sz);
|
|
addr_rc.rc_family = AF_BLUETOOTH;
|
|
addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
|
|
memcpy(&addr_rc.rc_bdaddr, &bdaddr, sizeof(bdaddr_t));
|
|
break;
|
|
case TYPE_SCO:
|
|
struct sockaddr_sco addr_sco;
|
|
addr = (struct sockaddr *)&addr_sco;
|
|
addr_sz = sizeof(addr_sco);
|
|
|
|
memset(addr, 0, addr_sz);
|
|
addr_sco.sco_family = AF_BLUETOOTH;
|
|
memcpy(&addr_sco.sco_bdaddr, &bdaddr, sizeof(bdaddr_t));
|
|
break;
|
|
case TYPE_L2CAP:
|
|
case TYPE_EL2CAP:
|
|
struct sockaddr_l2 addr_l2;
|
|
addr = (struct sockaddr *)&addr_l2;
|
|
addr_sz = sizeof(addr_l2);
|
|
|
|
memset(addr, 0, addr_sz);
|
|
addr_l2.l2_family = AF_BLUETOOTH;
|
|
addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
|
|
memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
|
|
break;
|
|
default:
|
|
return ENOSYS;
|
|
}
|
|
|
|
if (bind(s->fd, addr, addr_sz)) {
|
|
LOGV("...bind(%d) gave errno %d", s->fd, errno);
|
|
return errno;
|
|
}
|
|
|
|
if (listen(s->fd, 1)) {
|
|
LOGV("...listen(%d) gave errno %d", s->fd, errno);
|
|
return errno;
|
|
}
|
|
|
|
LOGV("...bindListenNative(%d) success", s->fd);
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
return ENOSYS;
|
|
}
|
|
|
|
static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
|
|
int fd;
|
|
jint type;
|
|
struct sockaddr *addr;
|
|
socklen_t addr_sz;
|
|
jstring addr_jstr;
|
|
char addr_cstr[BTADDR_SIZE];
|
|
bdaddr_t *bdaddr;
|
|
jboolean auth;
|
|
jboolean encrypt;
|
|
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (!s)
|
|
return NULL;
|
|
|
|
type = env->GetIntField(obj, field_mType);
|
|
|
|
switch (type) {
|
|
case TYPE_RFCOMM:
|
|
struct sockaddr_rc addr_rc;
|
|
addr = (struct sockaddr *)&addr_rc;
|
|
addr_sz = sizeof(addr_rc);
|
|
bdaddr = &addr_rc.rc_bdaddr;
|
|
memset(addr, 0, addr_sz);
|
|
break;
|
|
case TYPE_SCO:
|
|
struct sockaddr_sco addr_sco;
|
|
addr = (struct sockaddr *)&addr_sco;
|
|
addr_sz = sizeof(addr_sco);
|
|
bdaddr = &addr_sco.sco_bdaddr;
|
|
memset(addr, 0, addr_sz);
|
|
break;
|
|
case TYPE_L2CAP:
|
|
case TYPE_EL2CAP:
|
|
struct sockaddr_l2 addr_l2;
|
|
addr = (struct sockaddr *)&addr_l2;
|
|
addr_sz = sizeof(addr_l2);
|
|
bdaddr = &addr_l2.l2_bdaddr;
|
|
memset(addr, 0, addr_sz);
|
|
break;
|
|
default:
|
|
jniThrowIOException(env, ENOSYS);
|
|
return NULL;
|
|
}
|
|
|
|
fd = asocket_accept(s, addr, &addr_sz, timeout);
|
|
|
|
LOGV("...accept(%d, %s) = %d (errno %d)",
|
|
s->fd, TYPE_AS_STR(type), fd, errno);
|
|
|
|
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(bdaddr, addr_cstr);
|
|
|
|
addr_jstr = env->NewStringUTF(addr_cstr);
|
|
return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor,
|
|
type, 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;
|
|
}
|
|
|
|
static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
|
|
jint length) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
|
|
int ret;
|
|
jbyte *b;
|
|
int sz;
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (!s)
|
|
return -1;
|
|
if (jb == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return -1;
|
|
}
|
|
sz = env->GetArrayLength(jb);
|
|
if (offset < 0 || length < 0 || offset + length > sz) {
|
|
jniThrowIOException(env, EINVAL);
|
|
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;
|
|
}
|
|
|
|
static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
|
|
jint length) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
|
|
int ret;
|
|
jbyte *b;
|
|
int sz;
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (!s)
|
|
return -1;
|
|
if (jb == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return -1;
|
|
}
|
|
sz = env->GetArrayLength(jb);
|
|
if (offset < 0 || length < 0 || offset + length > sz) {
|
|
jniThrowIOException(env, EINVAL);
|
|
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 abortNative(JNIEnv *env, jobject obj) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
struct asocket *s = get_socketData(env, obj);
|
|
|
|
if (!s)
|
|
return;
|
|
|
|
asocket_abort(s);
|
|
|
|
LOGV("...asocket_abort(%d) complete", s->fd);
|
|
return;
|
|
#endif
|
|
jniThrowIOException(env, ENOSYS);
|
|
}
|
|
|
|
static void destroyNative(JNIEnv *env, jobject obj) {
|
|
#ifdef HAVE_BLUETOOTH
|
|
LOGV(__FUNCTION__);
|
|
struct asocket *s = get_socketData(env, obj);
|
|
int fd = s->fd;
|
|
|
|
if (!s)
|
|
return;
|
|
|
|
asocket_destroy(s);
|
|
|
|
LOGV("...asocket_destroy(%d) complete", fd);
|
|
return;
|
|
#endif
|
|
jniThrowIOException(env, ENOSYS);
|
|
}
|
|
|
|
static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) {
|
|
jniThrowIOException(env, err);
|
|
}
|
|
|
|
static JNINativeMethod sMethods[] = {
|
|
{"initSocketNative", "()V", (void*) initSocketNative},
|
|
{"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative},
|
|
{"setAmpPolicyNative", "(I)V", (void*) setAmpPolicyNative},
|
|
{"getMtuNative", "()I", (void*) getMtuNative},
|
|
{"connectNative", "()V", (void *) connectNative},
|
|
{"bindListenNative", "()I", (void *) bindListenNative},
|
|
{"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
|
|
{"availableNative", "()I", (void *) availableNative},
|
|
{"readNative", "([BII)I", (void *) readNative},
|
|
{"writeNative", "([BII)I", (void *) writeNative},
|
|
{"abortNative", "()V", (void *) abortNative},
|
|
{"destroyNative", "()V", (void *) destroyNative},
|
|
{"throwErrnoNative", "(I)V", (void *) throwErrnoNative},
|
|
};
|
|
|
|
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_mType = env->GetFieldID(clazz, "mType", "I");
|
|
field_mAddress = env->GetFieldID(clazz, "mAddress", "Ljava/lang/String;");
|
|
field_mPort = env->GetFieldID(clazz, "mPort", "I");
|
|
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>", "(IIZZLjava/lang/String;I)V");
|
|
return AndroidRuntime::registerNativeMethods(env,
|
|
"android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
|
|
}
|
|
|
|
} /* namespace android */
|
|
|