From 47e82dee6b18c33fab8c2cdf4f68b20d3663079e Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 1 Jun 2009 19:09:37 -0700 Subject: [PATCH] Implement bulk read and writes for Bluetooth sockets. Before: 0.1 kB/s After: 100 kB/s (in my java BT speed test app) --- .../bluetooth/BluetoothInputStream.java | 42 +++++++++++++-- .../bluetooth/BluetoothOutputStream.java | 34 +++++++++++- .../android/bluetooth/BluetoothSocket.java | 4 +- .../jni/android_bluetooth_BluetoothSocket.cpp | 52 ++++++++++++++----- 4 files changed, 113 insertions(+), 19 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java index ceae70c586d39..e6f501c9b69f0 100644 --- a/core/java/android/bluetooth/BluetoothInputStream.java +++ b/core/java/android/bluetooth/BluetoothInputStream.java @@ -24,7 +24,6 @@ import java.io.InputStream; * * Used to write to a Bluetooth socket. * - * TODO: Implement bulk writes (instead of one byte at a time). * @hide */ /*package*/ final class BluetoothInputStream extends InputStream { @@ -54,9 +53,46 @@ import java.io.InputStream; * @return the byte read or -1 if the end of stream has been reached. * @throws IOException * if the stream is closed or another IOException occurs. - * @since Android 1.0 + * @since Android 1.5 */ public int read() throws IOException { - return mSocket.readNative(); + byte b[] = new byte[1]; + int ret = mSocket.readNative(b, 0, 1); + if (ret == 1) { + return (int)b[0]; + } else { + return -1; + } + } + + /** + * Reads at most {@code length} bytes from this stream and stores them in + * the byte array {@code b} starting at {@code offset}. + * + * @param b + * the byte array in which to store the bytes read. + * @param offset + * the initial position in {@code buffer} to store the bytes + * read from this stream. + * @param length + * the maximum number of bytes to store in {@code b}. + * @return the number of bytes actually read or -1 if the end of the stream + * has been reached. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code length < 0}, or if + * {@code offset + length} is greater than the length of + * {@code b}. + * @throws IOException + * if the stream is closed or another IOException occurs. + * @since Android 1.5 + */ + public int read(byte[] b, int offset, int length) throws IOException { + if (b == null) { + throw new NullPointerException("byte array is null"); + } + if ((offset | length) < 0 || length > b.length - offset) { + throw new ArrayIndexOutOfBoundsException("invalid offset or length"); + } + return mSocket.readNative(b, offset, length); } } diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java index 32e6d17c82a83..7e2ead478afb5 100644 --- a/core/java/android/bluetooth/BluetoothOutputStream.java +++ b/core/java/android/bluetooth/BluetoothOutputStream.java @@ -24,7 +24,6 @@ import java.io.OutputStream; * * Used to read from a Bluetooth socket. * - * TODO: Implement bulk reads (instead of one byte at a time). * @hide */ /*package*/ final class BluetoothOutputStream extends OutputStream { @@ -52,6 +51,37 @@ import java.io.OutputStream; * @since Android 1.0 */ public void write(int oneByte) throws IOException { - mSocket.writeNative(oneByte); + byte b[] = new byte[1]; + b[0] = (byte)oneByte; + mSocket.writeNative(b, 0, 1); + } + + /** + * Writes {@code count} bytes from the byte array {@code buffer} starting + * at position {@code offset} to this stream. + * + * @param b + * the buffer to be written. + * @param offset + * the start position in {@code buffer} from where to get bytes. + * @param count + * the number of bytes from {@code buffer} to write to this + * stream. + * @throws IOException + * if an error occurs while writing to this stream. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code count < 0}, or if + * {@code offset + count} is bigger than the length of + * {@code buffer}. + * @since Android 1.0 + */ + public void write(byte[] b, int offset, int count) throws IOException { + if (b == null) { + throw new NullPointerException("buffer is null"); + } + if ((offset | count) < 0 || count > b.length - offset) { + throw new IndexOutOfBoundsException("invalid offset or length"); + } + mSocket.writeNative(b, offset, count); } } diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index fd8885ece9935..670146b245ee6 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -169,8 +169,8 @@ public final class BluetoothSocket implements Closeable { /*package*/ native void bindListenNative(int port) throws IOException; /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException; /*package*/ native int availableNative(); - /*package*/ native int readNative(); - /*package*/ native void writeNative(int data); + /*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(); } diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp index dc4c1d43ab0e1..e9c04a54f49aa 100644 --- a/core/jni/android_bluetooth_BluetoothSocket.cpp +++ b/core/jni/android_bluetooth_BluetoothSocket.cpp @@ -227,44 +227,72 @@ static jint availableNative(JNIEnv *env, jobject obj) { return -1; } -static jint readNative(JNIEnv *env, jobject obj) { +/** 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__); - char buf; + int ret; + jbyte *b; struct asocket *s = get_socketData(env, obj); if (!s) return -1; - if (asocket_read(s, &buf, 1, -1) < 0) { - jniThrowIOException(env, errno); + b = env->GetByteArrayElements(jb, NULL); + if (b == NULL) { + jniThrowIOException(env, EINVAL); return -1; } - return (jint)buf; + 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 void writeNative(JNIEnv *env, jobject obj, jint data) { +/** 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__); - const char buf = (char)data; + int ret; + jbyte *b; struct asocket *s = get_socketData(env, obj); if (!s) - return; + return -1; - if (asocket_write(s, &buf, 1, -1) < 0) + 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; - return; #endif jniThrowIOException(env, ENOSYS); + return -1; } static void closeNative(JNIEnv *env, jobject obj) { @@ -301,8 +329,8 @@ static JNINativeMethod sMethods[] = { {"bindListenNative", "(I)V", (void *) bindListenNative}, {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative}, {"availableNative", "()I", (void *) availableNative}, - {"readNative", "()I", (void *) readNative}, - {"writeNative", "(I)V", (void *) writeNative}, + {"readNative", "([BII)I", (void *) readNative}, + {"writeNative", "([BII)I", (void *) writeNative}, {"closeNative", "()V", (void *) closeNative}, {"destroyNative", "()V", (void *) destroyNative}, };