summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Pelly <npelly@google.com>2009-06-02 15:57:18 -0700
committerNick Pelly <npelly@google.com>2009-06-02 16:05:09 -0700
commit6a669fac385b51b8bb01844b77a9a43840dda854 (patch)
treeae86e3e41162c08fa56b1f84f68a50fe52d0493a
parent47e82dee6b18c33fab8c2cdf4f68b20d3663079e (diff)
downloadframeworks_base-6a669fac385b51b8bb01844b77a9a43840dda854.zip
frameworks_base-6a669fac385b51b8bb01844b77a9a43840dda854.tar.gz
frameworks_base-6a669fac385b51b8bb01844b77a9a43840dda854.tar.bz2
Implement and expose SCO socket support in BluetoothSocket.java.
Implement L2CAP socket support, but do not expose it (untested). NEXT: Switch to Builder style constructor instead of factory method.
-rw-r--r--core/java/android/bluetooth/BluetoothServerSocket.java44
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java53
-rw-r--r--core/jni/android_bluetooth_BluetoothSocket.cpp219
3 files changed, 264 insertions, 52 deletions
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ca46701..f3baeab 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -27,7 +27,7 @@ import java.io.IOException;
* RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
* also known as the Serial Port Profile (SPP).
*
- * TODO: Consider implementing SCO and L2CAP sockets.
+ * TODO: Consider exposing L2CAP sockets.
* TODO: Clean up javadoc grammer and formatting.
* TODO: Remove @hide
* @hide
@@ -45,9 +45,10 @@ public final class BluetoothServerSocket implements Closeable {
* insufficient permissions.
*/
public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException {
- BluetoothServerSocket socket = new BluetoothServerSocket(true, true);
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, true, true, port);
try {
- socket.mSocket.bindListenNative(port);
+ socket.mSocket.bindListenNative();
} catch (IOException e) {
try {
socket.close();
@@ -65,9 +66,31 @@ public final class BluetoothServerSocket implements Closeable {
* insufficient permissions.
*/
public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
- BluetoothServerSocket socket = new BluetoothServerSocket(false, false);
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, false, false, port);
try {
- socket.mSocket.bindListenNative(port);
+ socket.mSocket.bindListenNative();
+ } catch (IOException e) {
+ try {
+ socket.close();
+ } catch (IOException e2) { }
+ throw e;
+ }
+ return socket;
+ }
+
+ /**
+ * Construct a SCO server socket.
+ * Call #accept to retrieve connections to this socket.
+ * @return A SCO BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ */
+ public static BluetoothServerSocket listenUsingScoOn() throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_SCO, false, false, -1);
+ try {
+ socket.mSocket.bindListenNative();
} catch (IOException e) {
try {
socket.close();
@@ -79,13 +102,16 @@ public final class BluetoothServerSocket implements Closeable {
/**
* Construct a socket for incoming connections.
- * @param auth Require the remote device to be authenticated
- * @param encrypt Require the connection to be encrypted
+ * @param type type of socket
+ * @param auth require the remote device to be authenticated
+ * @param encrypt require the connection to be encrypted
+ * @param port remote port
* @throws IOException On error, for example Bluetooth not available, or
* insufficient priveleges
*/
- private BluetoothServerSocket(boolean auth, boolean encrypt) throws IOException {
- mSocket = new BluetoothSocket(-1, auth, encrypt, null, -1);
+ private BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
+ throws IOException {
+ mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port);
}
/**
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 670146b..de1f326 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -29,13 +29,19 @@ import java.io.OutputStream;
* RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
* also known as the Serial Port Profile (SPP).
*
- * TODO: Consider implementing SCO and L2CAP sockets.
+ * TODO: Consider exposing L2CAP sockets.
* TODO: Clean up javadoc grammer and formatting.
* TODO: Remove @hide
* @hide
*/
public final class BluetoothSocket implements Closeable {
- private final int mPort;
+ /** Keep TYPE_RFCOMM etc in sync with BluetoothSocket.cpp */
+ /*package*/ static final int TYPE_RFCOMM = 1;
+ /*package*/ static final int TYPE_SCO = 2;
+ /*package*/ static final int TYPE_L2CAP = 3;
+
+ private final int mType; /* one of TYPE_RFCOMM etc */
+ private final int mPort; /* RFCOMM channel or L2CAP psm */
private final String mAddress; /* remote address */
private final boolean mAuth;
private final boolean mEncrypt;
@@ -57,7 +63,7 @@ public final class BluetoothSocket implements Closeable {
*/
public static BluetoothSocket createRfcommSocket(String address, int port)
throws IOException {
- return new BluetoothSocket(-1, true, true, address, port);
+ return new BluetoothSocket(TYPE_RFCOMM, -1, true, true, address, port);
}
/**
@@ -74,11 +80,25 @@ public final class BluetoothSocket implements Closeable {
*/
public static BluetoothSocket createInsecureRfcommSocket(String address, int port)
throws IOException {
- return new BluetoothSocket(-1, false, false, address, port);
+ return new BluetoothSocket(TYPE_RFCOMM, -1, false, false, address, port);
+ }
+
+ /**
+ * Construct a SCO socket ready to start an outgoing connection.
+ * Call #connect on the returned #BluetoothSocket to begin the connection.
+ * @param address remote Bluetooth address that this socket can connect to
+ * @return a SCO BluetoothSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions.
+ */
+ public static BluetoothSocket createScoSocket(String address, int port)
+ throws IOException {
+ return new BluetoothSocket(TYPE_SCO, -1, true, true, address, port);
}
/**
* Construct a Bluetooth.
+ * @param type type of socket
* @param fd fd to use for connected socket, or -1 for a new socket
* @param auth require the remote device to be authenticated
* @param encrypt require the connection to be encrypted
@@ -87,8 +107,9 @@ public final class BluetoothSocket implements Closeable {
* @throws IOException On error, for example Bluetooth not available, or
* insufficient priveleges
*/
- /*package*/ BluetoothSocket(int fd, boolean auth, boolean encrypt, String address, int port)
- throws IOException {
+ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
+ int port) throws IOException {
+ mType = type;
mAuth = auth;
mEncrypt = encrypt;
mAddress = address;
@@ -120,7 +141,7 @@ public final class BluetoothSocket implements Closeable {
* @throws IOException On error, for example connection failure
*/
public void connect() throws IOException {
- connectNative(mAddress, mPort, -1);
+ connectNative();
}
/**
@@ -163,14 +184,14 @@ public final class BluetoothSocket implements Closeable {
return mOutputStream;
}
- private native void initSocketNative();
- private native void initSocketFromFdNative(int fd);
- private native void connectNative(String address, int port, int timeout);
- /*package*/ native void bindListenNative(int port) throws IOException;
+ private native void initSocketNative() throws IOException;
+ private native void initSocketFromFdNative(int fd) throws IOException;
+ private native void connectNative() throws IOException;
+ /*package*/ native void bindListenNative() throws IOException;
/*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException;
- /*package*/ native int availableNative();
- /*package*/ native int readNative(byte[] b, int offset, int length);
- /*package*/ native int writeNative(byte[] b, int offset, int length);
- /*package*/ native void closeNative();
- private native void destroyNative();
+ /*package*/ native int availableNative() throws IOException;
+ /*package*/ native int readNative(byte[] b, int offset, int length) throws IOException;
+ /*package*/ native int writeNative(byte[] b, int offset, int length) throws IOException;
+ /*package*/ native void closeNative() throws IOException;
+ private native void destroyNative() throws IOException;
}
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
index e9c04a5..9c4f7c7 100644
--- a/core/jni/android_bluetooth_BluetoothSocket.cpp
+++ b/core/jni/android_bluetooth_BluetoothSocket.cpp
@@ -31,16 +31,29 @@
#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" : "L2CAP"))
+
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; // TODO: Test l2cap code paths
+
static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
struct asocket *s =
(struct asocket *) env->GetIntField(obj, field_mSocketData);
@@ -76,9 +89,25 @@ static void initSocketNative(JNIEnv *env, jobject obj) {
int lm = 0;
jboolean auth;
jboolean encrypt;
+ jint type;
+
+ 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;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
- /*TODO: do not hardcode to rfcomm */
- fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (fd < 0) {
LOGV("socket() failed, throwing");
jniThrowIOException(env, errno);
@@ -88,8 +117,17 @@ static void initSocketNative(JNIEnv *env, jobject obj) {
auth = env->GetBooleanField(obj, field_mAuth);
encrypt = env->GetBooleanField(obj, field_mEncrypt);
- lm |= auth ? RFCOMM_LM_AUTH : 0;
- lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+ /* kernel does not yet support LM for SCO */
+ switch (type) {
+ case TYPE_RFCOMM:
+ lm |= auth ? RFCOMM_LM_AUTH : 0;
+ lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+ break;
+ case TYPE_L2CAP:
+ lm |= auth ? L2CAP_LM_AUTH : 0;
+ lm |= encrypt? L2CAP_LM_ENCRYPT : 0;
+ break;
+ }
if (lm) {
if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
@@ -99,36 +137,83 @@ static void initSocketNative(JNIEnv *env, jobject obj) {
}
}
+ LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm);
+
initSocketFromFdNative(env, obj, fd);
return;
#endif
jniThrowIOException(env, ENOSYS);
}
-static void connectNative(JNIEnv *env, jobject obj, jstring address,
- jint port, jint timeout) {
+static void connectNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int ret;
- struct sockaddr_rc addr;
+ 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;
- addr.rc_family = AF_BLUETOOTH;
- addr.rc_channel = port;
+ 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((const char *)c_address, &addr.rc_bdaddr)) {
+ if (get_bdaddr(c_address, &bdaddress)) {
env->ReleaseStringUTFChars(address, c_address);
jniThrowIOException(env, EINVAL);
return;
}
env->ReleaseStringUTFChars(address, c_address);
- ret = asocket_connect(s, (struct sockaddr *)&addr, sizeof(addr), timeout);
+ 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:
+ 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);
@@ -138,22 +223,57 @@ static void connectNative(JNIEnv *env, jobject obj, jstring address,
jniThrowIOException(env, ENOSYS);
}
-static void bindListenNative(JNIEnv *env, jobject obj, jint port) {
+static void bindListenNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
- struct sockaddr_rc addr;
+ jint type;
+ socklen_t addr_sz;
+ struct sockaddr *addr;
+ bdaddr_t bdaddr = *BDADDR_ANY;
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;
+ 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:
+ 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:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
- if (bind(s->fd, (struct sockaddr *)&addr, sizeof(addr))) {
+ if (bind(s->fd, addr, addr_sz)) {
jniThrowIOException(env, errno);
return;
}
@@ -173,10 +293,12 @@ static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
LOGV(__FUNCTION__);
int fd;
- struct sockaddr_rc addr;
- int addrlen = sizeof(addr);
+ jint type;
+ struct sockaddr *addr;
+ socklen_t addr_sz;
jstring addr_jstr;
char addr_cstr[BTADDR_SIZE];
+ bdaddr_t *bdaddr;
jboolean auth;
jboolean encrypt;
@@ -185,7 +307,39 @@ static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
if (!s)
return NULL;
- fd = asocket_accept(s, (struct sockaddr *)&addr, &addrlen, timeout);
+ 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:
+ 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);
@@ -195,10 +349,12 @@ static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
/* 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);
+
+ get_bdaddr_as_string(bdaddr, addr_cstr);
+
addr_jstr = env->NewStringUTF(addr_cstr);
- return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor, fd,
- auth, encrypt, addr_jstr, -1);
+ return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor,
+ type, fd, auth, encrypt, addr_jstr, -1);
#endif
jniThrowIOException(env, ENOSYS);
@@ -304,6 +460,8 @@ static void closeNative(JNIEnv *env, jobject obj) {
return;
asocket_abort(s);
+
+ LOGV("...asocket_abort(%d) complete", s->fd);
return;
#endif
jniThrowIOException(env, ENOSYS);
@@ -313,10 +471,14 @@ 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);
@@ -325,8 +487,8 @@ static void destroyNative(JNIEnv *env, jobject obj) {
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},
+ {"connectNative", "()V", (void *) connectNative},
+ {"bindListenNative", "()V", (void *) bindListenNative},
{"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
{"availableNative", "()I", (void *) availableNative},
{"readNative", "([BII)I", (void *) readNative},
@@ -340,10 +502,13 @@ int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
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>", "(IZZLjava/lang/String;I)V");
+ method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IIZZLjava/lang/String;I)V");
return AndroidRuntime::registerNativeMethods(env,
"android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
}