OSDN Git Service

New BluetoothSocket API.
authorNick Pelly <npelly@google.com>
Wed, 27 May 2009 02:13:43 +0000 (19:13 -0700)
committerNick Pelly <npelly@google.com>
Fri, 29 May 2009 23:16:05 +0000 (16:16 -0700)
Modeled on blocking java.net.Socket and java.net.ServerSocket library.

Public interface is:

public final class BluetoothSocket implements Closeable {
   public static BluetoothSocket createRfcommSocket(String address, int port) throws IOException;
   public static BluetoothSocket createInsecureRfcommSocket(String address, int port) throws IOException;

   public void connect() throws IOException;
   public void close() throws IOException;

   public String getAddress();
   public InputStream getInputStream() throws IOException;
   public OutputStream getOutputStream() throws IOException;
}

public final class BluetoothServerSocket implements Closeable {
   public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException;
   public static BluetoothServerSocket listenUsingUnsecureRfcommOn(int port) throws IOException;

   public BluetoothSocket accept() throws IOException;
   public BluetoothSocket accept(int timeout) throws IOException;
   public void close() throws IOException;

}

core/java/android/bluetooth/BluetoothInputStream.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothOutputStream.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothServerSocket.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothSocket.java [new file with mode: 0644]
core/java/android/bluetooth/RfcommSocket.java [deleted file]
core/jni/Android.mk
core/jni/AndroidRuntime.cpp
core/jni/android_bluetooth_BluetoothSocket.cpp [new file with mode: 0644]
core/jni/android_bluetooth_RfcommSocket.cpp [deleted file]
core/jni/android_bluetooth_common.cpp
core/jni/android_bluetooth_common.h

diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
new file mode 100644 (file)
index 0000000..ceae70c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.bluetooth;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * BluetoothInputStream.
+ *
+ * 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 {
+    private BluetoothSocket mSocket;
+
+    /*package*/ BluetoothInputStream(BluetoothSocket s) {
+        mSocket = s;
+    }
+
+    /**
+     * Return number of bytes available before this stream will block.
+     */
+    public int available() throws IOException {
+        return mSocket.availableNative();
+    }
+
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    /**
+     * Reads a single byte from this stream and returns it as an integer in the
+     * range from 0 to 255. Returns -1 if the end of the stream has been
+     * reached. Blocks until one byte has been read, the end of the source
+     * stream is detected or an exception is thrown.
+     *
+     * @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
+     */
+    public int read() throws IOException {
+        return mSocket.readNative();
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
new file mode 100644 (file)
index 0000000..32e6d17
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.bluetooth;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * BluetoothOutputStream.
+ *
+ * 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 {
+    private BluetoothSocket mSocket;
+
+    /*package*/ BluetoothOutputStream(BluetoothSocket s) {
+        mSocket = s;
+    }
+
+    /**
+     * Close this output stream and the socket associated with it.
+     */
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    /**
+     * Writes a single byte to this stream. Only the least significant byte of
+     * the integer {@code oneByte} is written to the stream.
+     *
+     * @param oneByte
+     *            the byte to be written.
+     * @throws IOException
+     *             if an error occurs while writing to this stream.
+     * @since Android 1.0
+     */
+    public void write(int oneByte) throws IOException {
+        mSocket.writeNative(oneByte);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
new file mode 100644 (file)
index 0000000..ca46701
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.bluetooth;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Server (listening) Bluetooth Socket.
+ *
+ * Currently only supports RFCOMM sockets.
+ *
+ * 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: Clean up javadoc grammer and formatting.
+ * TODO: Remove @hide
+ * @hide
+ */
+public final class BluetoothServerSocket implements Closeable {
+    private final BluetoothSocket mSocket;
+
+    /**
+     * Construct a listening, secure RFCOMM server socket.
+     * The remote device connecting to this socket will be authenticated and
+     * communication on this socket will be encrypted.
+     * Call #accept to retrieve connections to this socket.
+     * @return An RFCOMM BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(true, true);
+        try {
+            socket.mSocket.bindListenNative(port);
+        } catch (IOException e) {
+            try {
+                socket.close();
+            } catch (IOException e2) { }
+            throw e;
+        }
+        return socket;
+    }
+
+    /**
+     * Construct an unencrypted, unauthenticated, RFCOMM server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return An RFCOMM BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(false, false);
+        try {
+            socket.mSocket.bindListenNative(port);
+        } catch (IOException e) {
+            try {
+                socket.close();
+            } catch (IOException e2) { }
+            throw e;
+        }
+        return socket;
+    }
+
+    /**
+     * Construct a socket for incoming connections.
+     * @param auth    Require the remote device to be authenticated
+     * @param encrypt Require the connection to be encrypted
+     * @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);
+    }
+
+    /**
+     * Block until a connection is established.
+     * Returns a connected #BluetoothSocket. This server socket can be reused
+     * for subsequent incoming connections by calling #accept repeatedly.
+     * #close can be used to abort this call from another thread.
+     * @return A connected #BluetoothSocket
+     * @throws IOException On error, for example this call was aborted
+     */
+    public BluetoothSocket accept() throws IOException {
+        return accept(-1);
+    }
+
+    /**
+     * Block until a connection is established, with timeout.
+     * Returns a connected #BluetoothSocket. This server socket can be reused
+     * for subsequent incoming connections by calling #accept repeatedly.
+     * #close can be used to abort this call from another thread.
+     * @return A connected #BluetoothSocket
+     * @throws IOException On error, for example this call was aborted, or
+     *                     timeout
+     */
+    public BluetoothSocket accept(int timeout) throws IOException {
+        return mSocket.acceptNative(timeout);
+    }
+
+    /**
+     * Closes this socket.
+     * This will cause other blocking calls on this socket to immediately
+     * throw an IOException.
+     */
+    public void close() throws IOException {
+        mSocket.closeNative();
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
new file mode 100644 (file)
index 0000000..fd8885e
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.bluetooth;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Represents a connected or connecting Bluetooth Socket.
+ *
+ * Currently only supports RFCOMM sockets.
+ *
+ * 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: Clean up javadoc grammer and formatting.
+ * TODO: Remove @hide
+ * @hide
+ */
+public final class BluetoothSocket implements Closeable {
+    private final int mPort;
+    private final String mAddress;    /* remote address */
+    private final boolean mAuth;
+    private final boolean mEncrypt;
+    private final BluetoothInputStream mInputStream;
+    private final BluetoothOutputStream mOutputStream;
+
+    private int mSocketData;    /* used by native code only */
+
+    /**
+     * Construct a secure RFCOMM socket ready to start an outgoing connection.
+     * Call #connect on the returned #BluetoothSocket to begin the connection.
+     * The remote device will be authenticated and communication on this socket
+     * will be encrypted.
+     * @param address remote Bluetooth address that this socket can connect to
+     * @param port    remote port
+     * @return an RFCOMM BluetoothSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothSocket createRfcommSocket(String address, int port)
+            throws IOException {
+        return new BluetoothSocket(-1, true, true, address, port);
+    }
+
+    /**
+     * Construct an insecure RFCOMM socket ready to start an outgoing
+     * connection.
+     * Call #connect on the returned #BluetoothSocket to begin the connection.
+     * The remote device will not be authenticated and communication on this
+     * socket will not be encrypted.
+     * @param address remote Bluetooth address that this socket can connect to
+     * @param port    remote port
+     * @return An RFCOMM BluetoothSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothSocket createInsecureRfcommSocket(String address, int port)
+            throws IOException {
+        return new BluetoothSocket(-1, false, false, address, port);
+    }
+
+    /**
+     * Construct a Bluetooth.
+     * @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
+     * @param address remote Bluetooth address that this socket can connect to
+     * @param port    remote port
+     * @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 {
+        mAuth = auth;
+        mEncrypt = encrypt;
+        mAddress = address;
+        mPort = port;
+        if (fd == -1) {
+            initSocketNative();
+        } else {
+            initSocketFromFdNative(fd);
+        }
+        mInputStream = new BluetoothInputStream(this);
+        mOutputStream = new BluetoothOutputStream(this);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Attempt to connect to a remote device.
+     * This method will block until a connection is made or the connection
+     * fails. If this method returns without an exception then this socket
+     * is now connected. #close can be used to abort this call from another
+     * thread.
+     * @throws IOException On error, for example connection failure
+     */
+    public void connect() throws IOException {
+        connectNative(mAddress, mPort, -1);
+    }
+
+    /**
+     * Closes this socket.
+     * This will cause other blocking calls on this socket to immediately
+     * throw an IOException.
+     */
+    public void close() throws IOException {
+        closeNative();
+    }
+
+    /**
+     * Return the address we are connecting, or connected, to.
+     * @return Bluetooth address, or null if this socket has not yet attempted
+     *         or established a connection.
+     */
+    public String getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * Get the input stream associated with this socket.
+     * The input stream will be returned even if the socket is not yet
+     * connected, but operations on that stream will throw IOException until
+     * the associated socket is connected.
+     * @return InputStream
+     */
+    public InputStream getInputStream() throws IOException {
+        return mInputStream;
+    }
+
+    /**
+     * Get the output stream associated with this socket.
+     * The output stream will be returned even if the socket is not yet
+     * connected, but operations on that stream will throw IOException until
+     * the associated socket is connected.
+     * @return OutputStream
+     */
+    public OutputStream getOutputStream() throws IOException {
+        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;
+    /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException;
+    /*package*/ native int availableNative();
+    /*package*/ native int readNative();
+    /*package*/ native void writeNative(int data);
+    /*package*/ native void closeNative();
+    private native void destroyNative();
+}
diff --git a/core/java/android/bluetooth/RfcommSocket.java b/core/java/android/bluetooth/RfcommSocket.java
deleted file mode 100644 (file)
index a33263f..0000000
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-package android.bluetooth;
-
-import java.io.IOException;
-import java.io.FileOutputStream;
-import java.io.FileInputStream;
-import java.io.OutputStream;
-import java.io.InputStream;
-import java.io.FileDescriptor;
-
-/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- * 
- * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket
- * is similar to a normal socket in that it takes an address and a port number.
- * The difference is of course that the address is a Bluetooth-device address,
- * and the port number is an RFCOMM channel. The API allows for the
- * establishment of listening sockets via methods
- * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and
- * {@link #accept(RfcommSocket, int) accept}, as well as for the making of
- * outgoing connections with {@link #connect(String, int) connect},
- * {@link #connectAsync(String, int) connectAsync}, and
- * {@link #waitForAsyncConnect(int) waitForAsyncConnect}.
- * 
- * After constructing a socket, you need to {@link #create() create} it and then
- * {@link #destroy() destroy} it when you are done using it. Both
- * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return
- * a {@link java.io.FileDescriptor FileDescriptor} for the actual data.
- * Alternatively, you may call {@link #getInputStream() getInputStream} and
- * {@link #getOutputStream() getOutputStream} to retrieve the respective streams
- * without going through the FileDescriptor.
- *
- * @hide
- */
-public class RfcommSocket {
-
-    /**
-     * Used by the native implementation of the class.
-     */
-    private int mNativeData;
-
-    /**
-     * Used by the native implementation of the class.
-     */
-    private int mPort;
-
-    /**
-     * Used by the native implementation of the class.
-     */
-    private String mAddress;
-
-    /**
-     * We save the return value of {@link #create() create} and
-     * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to
-     * retrieve the I/O streams.
-     */
-    private FileDescriptor mFd;
-
-    /**
-     * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect},
-     * if the return value is zero, then, the the remaining time left to wait is
-     * written into this variable (by the native implementation). It is possible
-     * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before
-     * the user-specified timeout expires, which is why we save the remaining
-     * time in this member variable for the user to retrieve by calling method
-     * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}.
-     */
-    private int mTimeoutRemainingMs;
-
-    /**
-     * Set to true when an asynchronous (nonblocking) connect is in progress.
-     * {@see #connectAsync(String,int)}.
-     */
-    private boolean mIsConnecting;
-
-    /**
-     * Set to true after a successful call to {@link #bind(String,int) bind} and
-     * used for error checking in {@link #listen(int) listen}. Reset to false
-     * on {@link #destroy() destroy}.
-     */
-    private boolean mIsBound = false;
-
-    /**
-     * Set to true after a successful call to {@link #listen(int) listen} and
-     * used for error checking in {@link #accept(RfcommSocket,int) accept}.
-     * Reset to false on {@link #destroy() destroy}.
-     */
-    private boolean mIsListening = false;
-
-    /**
-     * Used to store the remaining time after an accept with a non-negative
-     * timeout returns unsuccessfully. It is possible that a blocking
-     * {@link #accept(int) accept} may wait for less than the time specified by
-     * the user, which is why we store the remainder in this member variable for
-     * it to be retrieved with method
-     * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}.
-     */
-    private int mAcceptTimeoutRemainingMs;
-
-    /**
-     * Maintained by {@link #getInputStream() getInputStream}.
-     */
-    protected FileInputStream mInputStream;
-
-    /**
-     * Maintained by {@link #getOutputStream() getOutputStream}.
-     */
-    protected FileOutputStream mOutputStream;
-
-    private native void initializeNativeDataNative();
-
-    /**
-     * Constructor.
-     */
-    public RfcommSocket() {
-        initializeNativeDataNative();
-    }
-
-    private native void cleanupNativeDataNative();
-
-    /**
-     * Called by the GC to clean up the native data that we set up when we
-     * construct the object.
-     */
-    protected void finalize() throws Throwable {
-        try {
-            cleanupNativeDataNative();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private native static void classInitNative();
-
-    static {
-        classInitNative();
-    }
-
-    /**
-     * Creates a socket. You need to call this method before performing any
-     * other operation on a socket.
-     * 
-     * @return FileDescriptor for the data stream.
-     * @throws IOException
-     * @see #destroy()
-     */
-    public FileDescriptor create() throws IOException {
-        if (mFd == null) {
-            mFd = createNative();
-        }
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        return mFd;
-    }
-
-    private native FileDescriptor createNative();
-
-    /**
-     * Destroys a socket created by {@link #create() create}. Call this
-     * function when you no longer use the socket in order to release the
-     * underlying OS resources.
-     * 
-     * @see #create()
-     */
-    public void destroy() {
-        synchronized (this) {
-            destroyNative();
-            mFd = null;
-            mIsBound = false;
-            mIsListening = false;
-        }
-    }
-
-    private native void destroyNative();
-
-    /**
-     * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket.
-     * 
-     * @return the FileDescriptor
-     * @throws IOException
-     *             when the socket has not been {@link #create() created}.
-     */
-    public FileDescriptor getFileDescriptor() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        return mFd;
-    }
-
-    /**
-     * Retrieves the input stream from the socket. Alternatively, you can do
-     * that from the FileDescriptor returned by {@link #create() create} or
-     * {@link #accept(RfcommSocket, int) accept}.
-     * 
-     * @return InputStream
-     * @throws IOException
-     *             if you have not called {@link #create() create} on the
-     *             socket.
-     */
-    public InputStream getInputStream() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-
-        synchronized (this) {
-            if (mInputStream == null) {
-                mInputStream = new FileInputStream(mFd);
-            }
-
-            return mInputStream;
-        }
-    }
-
-    /**
-     * Retrieves the output stream from the socket. Alternatively, you can do
-     * that from the FileDescriptor returned by {@link #create() create} or
-     * {@link #accept(RfcommSocket, int) accept}.
-     * 
-     * @return OutputStream
-     * @throws IOException
-     *             if you have not called {@link #create() create} on the
-     *             socket.
-     */
-    public OutputStream getOutputStream() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-
-        synchronized (this) {
-            if (mOutputStream == null) {
-                mOutputStream = new FileOutputStream(mFd);
-            }
-
-            return mOutputStream;
-        }
-    }
-
-    /**
-     * Starts a blocking connect to a remote RFCOMM socket. It takes the address
-     * of a device and the RFCOMM channel (port) to which to connect.
-     * 
-     * @param address
-     *            is the Bluetooth address of the remote device.
-     * @param port
-     *            is the RFCOMM channel
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if {@link #create() create} has not been called.
-     * @see #connectAsync(String, int)
-     */
-    public boolean connect(String address, int port) throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            return connectNative(address, port);
-        }
-    }
-
-    private native boolean connectNative(String address, int port);
-
-    /**
-     * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket.
-     * It takes the address of the device to connect to, as well as the RFCOMM
-     * channel (port). On successful return (return value is true), you need to
-     * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to
-     * block for up to a specified number of milliseconds while waiting for the
-     * asyncronous connect to complete.
-     * 
-     * @param address
-     *            of remote device
-     * @param port
-     *            the RFCOMM channel
-     * @return true when the asynchronous connect has successfully started,
-     *         false if there was an error.
-     * @throws IOException
-     *             is you have not called {@link #create() create}
-     * @see #waitForAsyncConnect(int)
-     * @see #getRemainingAsyncConnectWaitingTimeMs()
-     * @see #connect(String, int)
-     */
-    public boolean connectAsync(String address, int port) throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            mIsConnecting = connectAsyncNative(address, port);
-            return mIsConnecting;
-        }
-    }
-
-    private native boolean connectAsyncNative(String address, int port);
-
-    /**
-     * Interrupts an asynchronous connect in progress. This method does nothing
-     * when there is no asynchronous connect in progress.
-     * 
-     * @throws IOException
-     *             if you have not called {@link #create() create}.
-     * @see #connectAsync(String, int)
-     */
-    public void interruptAsyncConnect() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            if (mIsConnecting) {
-                mIsConnecting = !interruptAsyncConnectNative();
-            }
-        }
-    }
-
-    private native boolean interruptAsyncConnectNative();
-
-    /**
-     * Tells you whether there is an asynchronous connect in progress. This
-     * method returns an undefined value when there is a synchronous connect in
-     * progress.
-     * 
-     * @return true if there is an asyc connect in progress, false otherwise
-     * @see #connectAsync(String, int)
-     */
-    public boolean isConnecting() {
-        return mIsConnecting;
-    }
-
-    /**
-     * Blocks for a specified amount of milliseconds while waiting for an
-     * asynchronous connect to complete. Returns an integer value to indicate
-     * one of the following: the connect succeeded, the connect is still in
-     * progress, or the connect failed. It is possible for this method to block
-     * for less than the time specified by the user, and still return zero
-     * (i.e., async connect is still in progress.) For this reason, if the
-     * return value is zero, you need to call method
-     * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}
-     * to retrieve the remaining time.
-     * 
-     * @param timeoutMs
-     *            the time to block while waiting for the async connect to
-     *            complete.
-     * @return a positive value if the connect succeeds; zero, if the connect is
-     *         still in progress, and a negative value if the connect failed.
-     * 
-     * @throws IOException
-     * @see #getRemainingAsyncConnectWaitingTimeMs()
-     * @see #connectAsync(String, int)
-     */
-    public int waitForAsyncConnect(int timeoutMs) throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            int ret = waitForAsyncConnectNative(timeoutMs);
-            if (ret != 0) {
-                mIsConnecting = false;
-            }
-            return ret;
-        }
-    }
-
-    private native int waitForAsyncConnectNative(int timeoutMs);
-
-    /**
-     * Returns the number of milliseconds left to wait after the last call to
-     * {@link #waitForAsyncConnect(int) waitForAsyncConnect}.
-     * 
-     * It is possible that waitForAsyncConnect() waits for less than the time
-     * specified by the user, and still returns zero (i.e., async connect is
-     * still in progress.) For this reason, if the return value is zero, you
-     * need to call this method to retrieve the remaining time before you call
-     * waitForAsyncConnect again.
-     * 
-     * @return the remaining timeout in milliseconds.
-     * @see #waitForAsyncConnect(int)
-     * @see #connectAsync(String, int)
-     */
-    public int getRemainingAsyncConnectWaitingTimeMs() {
-        return mTimeoutRemainingMs;
-    }
-
-    /**
-     * Shuts down both directions on a socket.
-     * 
-     * @return true on success, false on failure; if the return value is false,
-     *         the socket might be left in a patially shut-down state (i.e. one
-     *         direction is shut down, but the other is still open.) In this
-     *         case, you should {@link #destroy() destroy} and then
-     *         {@link #create() create} the socket again.
-     * @throws IOException
-     *             is you have not caled {@link #create() create}.
-     * @see #shutdownInput()
-     * @see #shutdownOutput()
-     */
-    public boolean shutdown() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            if (shutdownNative(true)) {
-                return shutdownNative(false);
-            }
-
-            return false;
-        }
-    }
-
-    /**
-     * Shuts down the input stream of the socket, but leaves the output stream
-     * in its current state.
-     * 
-     * @return true on success, false on failure
-     * @throws IOException
-     *             is you have not called {@link #create() create}
-     * @see #shutdown()
-     * @see #shutdownOutput()
-     */
-    public boolean shutdownInput() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            return shutdownNative(true);
-        }
-    }
-
-    /**
-     * Shut down the output stream of the socket, but leaves the input stream in
-     * its current state.
-     * 
-     * @return true on success, false on failure
-     * @throws IOException
-     *             is you have not called {@link #create() create}
-     * @see #shutdown()
-     * @see #shutdownInput()
-     */
-    public boolean shutdownOutput() throws IOException {
-        synchronized (this) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            return shutdownNative(false);
-        }
-    }
-
-    private native boolean shutdownNative(boolean shutdownInput);
-
-    /**
-     * Tells you whether a socket is connected to another socket. This could be
-     * for input or output or both.
-     * 
-     * @return true if connected, false otherwise.
-     * @see #isInputConnected()
-     * @see #isOutputConnected()
-     */
-    public boolean isConnected() {
-        return isConnectedNative() > 0;
-    }
-
-    /**
-     * Determines whether input is connected (i.e., whether you can receive data
-     * on this socket.)
-     * 
-     * @return true if input is connected, false otherwise.
-     * @see #isConnected()
-     * @see #isOutputConnected()
-     */
-    public boolean isInputConnected() {
-        return (isConnectedNative() & 1) != 0;
-    }
-
-    /**
-     * Determines whether output is connected (i.e., whether you can send data
-     * on this socket.)
-     * 
-     * @return true if output is connected, false otherwise.
-     * @see #isConnected()
-     * @see #isInputConnected()
-     */
-    public boolean isOutputConnected() {
-        return (isConnectedNative() & 2) != 0;
-    }
-
-    private native int isConnectedNative();
-
-    /**
-     * Binds a listening socket to the local device, or a non-listening socket
-     * to a remote device. The port is automatically selected as the first
-     * available port in the range 12 to 30.
-     *
-     * NOTE: Currently we ignore the device parameter and always bind the socket
-     * to the local device, assuming that it is a listening socket.
-     *
-     * TODO: Use bind(0) in native code to have the kernel select an unused
-     * port.
-     *
-     * @param device
-     *            Bluetooth address of device to bind to (currently ignored).
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if you have not called {@link #create() create}
-     * @see #listen(int)
-     * @see #accept(RfcommSocket,int)
-     */
-    public boolean bind(String device) throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        for (int port = 12; port <= 30; port++) {
-            if (bindNative(device, port)) {
-                mIsBound = true;
-                return true;
-            }
-        }
-        mIsBound = false;
-        return false;
-    }
-
-    /**
-     * Binds a listening socket to the local device, or a non-listening socket
-     * to a remote device.
-     *
-     * NOTE: Currently we ignore the device parameter and always bind the socket
-     * to the local device, assuming that it is a listening socket.
-     *
-     * @param device
-     *            Bluetooth address of device to bind to (currently ignored).
-     * @param port
-     *            RFCOMM channel to bind socket to.
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if you have not called {@link #create() create}
-     * @see #listen(int)
-     * @see #accept(RfcommSocket,int)
-     */
-    public boolean bind(String device, int port) throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        mIsBound = bindNative(device, port);
-        return mIsBound;
-    }
-
-    private native boolean bindNative(String device, int port);
-
-    /**
-     * Starts listening for incoming connections on this socket, after it has
-     * been bound to an address and RFCOMM channel with
-     * {@link #bind(String,int) bind}.
-     * 
-     * @param backlog
-     *            the number of pending incoming connections to queue for
-     *            {@link #accept(RfcommSocket, int) accept}.
-     * @return true on success, false on failure
-     * @throws IOException
-     *             if you have not called {@link #create() create} or if the
-     *             socket has not been bound to a device and RFCOMM channel.
-     */
-    public boolean listen(int backlog) throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        if (!mIsBound) {
-            throw new IOException("socket not bound");
-        }
-        mIsListening = listenNative(backlog);
-        return mIsListening;
-    }
-
-    private native boolean listenNative(int backlog);
-
-    /**
-     * Accepts incoming-connection requests for a listening socket bound to an
-     * RFCOMM channel. The user may provide a time to wait for an incoming
-     * connection.
-     * 
-     * Note that this method may return null (i.e., no incoming connection)
-     * before the user-specified timeout expires. For this reason, on a null
-     * return value, you need to call
-     * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}
-     * in order to see how much time is left to wait, before you call this
-     * method again.
-     * 
-     * @param newSock
-     *            is set to the new socket that is created as a result of a
-     *            successful accept.
-     * @param timeoutMs
-     *            time (in milliseconds) to block while waiting to an
-     *            incoming-connection request. A negative value is an infinite
-     *            wait.
-     * @return FileDescriptor of newSock on success, null on failure. Failure
-     *         occurs if the timeout expires without a successful connect.
-     * @throws IOException
-     *             if the socket has not been {@link #create() create}ed, is
-     *             not bound, or is not a listening socket.
-     * @see #bind(String, int)
-     * @see #listen(int)
-     * @see #getRemainingAcceptWaitingTimeMs()
-     */
-    public FileDescriptor accept(RfcommSocket newSock, int timeoutMs)
-            throws IOException {
-        synchronized (newSock) {
-            if (mFd == null) {
-                throw new IOException("socket not created");
-            }
-            if (mIsListening == false) {
-                throw new IOException("not listening on socket");
-            }
-            newSock.mFd = acceptNative(newSock, timeoutMs);
-            return newSock.mFd;
-        }
-    }
-
-    /**
-     * Returns the number of milliseconds left to wait after the last call to
-     * {@link #accept(RfcommSocket, int) accept}.
-     * 
-     * Since accept() may return null (i.e., no incoming connection) before the
-     * user-specified timeout expires, you need to call this method in order to
-     * see how much time is left to wait, and wait for that amount of time
-     * before you call accept again.
-     * 
-     * @return the remaining time, in milliseconds.
-     */
-    public int getRemainingAcceptWaitingTimeMs() {
-        return mAcceptTimeoutRemainingMs;
-    }
-
-    private native FileDescriptor acceptNative(RfcommSocket newSock,
-            int timeoutMs);
-
-    /**
-     * Get the port (rfcomm channel) associated with this socket.
-     *
-     * This is only valid if the port has been set via a successful call to
-     * {@link #bind(String, int)}, {@link #connect(String, int)}
-     * or {@link #connectAsync(String, int)}. This can be checked
-     * with {@link #isListening()} and {@link #isConnected()}.
-     * @return Port (rfcomm channel)
-     */
-    public int getPort() throws IOException {
-        if (mFd == null) {
-            throw new IOException("socket not created");
-        }
-        if (!mIsListening && !isConnected()) {
-            throw new IOException("not listening or connected on socket");
-        }
-        return mPort;
-    }
-
-    /**
-     * Return true if this socket is listening ({@link #listen(int)}
-     * has been called successfully).
-     */
-    public boolean isListening() {
-        return mIsListening;
-    }
-}
index 9ad93eb..65f0591 100644 (file)
@@ -102,7 +102,7 @@ LOCAL_SRC_FILES:= \
        android_bluetooth_HeadsetBase.cpp \
        android_bluetooth_common.cpp \
        android_bluetooth_BluetoothAudioGateway.cpp \
-       android_bluetooth_RfcommSocket.cpp \
+       android_bluetooth_BluetoothSocket.cpp \
        android_bluetooth_ScoSocket.cpp \
        android_server_BluetoothDeviceService.cpp \
        android_server_BluetoothEventLoop.cpp \
index c3104b8..3ad5493 100644 (file)
@@ -140,7 +140,7 @@ extern int register_android_text_KeyCharacterMap(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
 extern int register_android_bluetooth_HeadsetBase(JNIEnv* env);
 extern int register_android_bluetooth_BluetoothAudioGateway(JNIEnv* env);
-extern int register_android_bluetooth_RfcommSocket(JNIEnv *env);
+extern int register_android_bluetooth_BluetoothSocket(JNIEnv *env);
 extern int register_android_bluetooth_ScoSocket(JNIEnv *env);
 extern int register_android_server_BluetoothDeviceService(JNIEnv* env);
 extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
@@ -1098,7 +1098,7 @@ static const RegJNIRec gRegJNI[] = {
     REG_JNI(register_android_opengl_classes),
     REG_JNI(register_android_bluetooth_HeadsetBase),
     REG_JNI(register_android_bluetooth_BluetoothAudioGateway),
-    REG_JNI(register_android_bluetooth_RfcommSocket),
+    REG_JNI(register_android_bluetooth_BluetoothSocket),
     REG_JNI(register_android_bluetooth_ScoSocket),
     REG_JNI(register_android_server_BluetoothDeviceService),
     REG_JNI(register_android_server_BluetoothEventLoop),
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
new file mode 100644 (file)
index 0000000..08f9cb6
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * 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;
+}
+
+static jint readNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    char buf;
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return -1;
+
+    if (asocket_read(s, &buf, 1, -1) < 0) {
+        jniThrowIOException(env, errno);
+        return -1;
+    }
+
+    return (jint)buf;
+
+#endif
+    jniThrowIOException(env, ENOSYS);
+    return -1;
+}
+
+static void writeNative(JNIEnv *env, jobject obj, jint data) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+
+    const char buf = (char)data;
+    struct asocket *s = get_socketData(env, obj);
+
+    if (!s)
+        return;
+
+    if (asocket_write(s, &buf, 1, -1) < 0)
+        jniThrowIOException(env, errno);
+
+    return;
+#endif
+    jniThrowIOException(env, ENOSYS);
+}
+
+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)", (void *) connectNative},
+    {"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},
+    {"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 */
+
diff --git a/core/jni/android_bluetooth_RfcommSocket.cpp b/core/jni/android_bluetooth_RfcommSocket.cpp
deleted file mode 100644 (file)
index 3ed35d9..0000000
+++ /dev/null
@@ -1,621 +0,0 @@
-/*
-** 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 "bluetooth_RfcommSocket.cpp"
-
-#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 <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/poll.h>
-
-#ifdef HAVE_BLUETOOTH
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sco.h>
-#endif
-
-namespace android {
-
-#ifdef HAVE_BLUETOOTH
-static jfieldID field_mNativeData;
-static jfieldID field_mTimeoutRemainingMs;
-static jfieldID field_mAcceptTimeoutRemainingMs;
-static jfieldID field_mAddress;
-static jfieldID field_mPort;
-
-typedef struct {
-    jstring address;
-    const char *c_address;
-    int rfcomm_channel;
-    int last_read_err;
-    int rfcomm_sock;
-    // < 0 -- in progress, 
-    //   0 -- not connected
-    // > 0 connected
-    //     1 input is open
-    //     2 output is open
-    //     3 both input and output are open
-    int rfcomm_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 inline void init_socket_info(
-    JNIEnv *env, jobject object,
-    native_data_t *nat,
-    jstring address,
-    jint rfcomm_channel) {
-    nat->address = (jstring)env->NewGlobalRef(address);
-    nat->c_address = env->GetStringUTFChars(nat->address, NULL);
-    nat->rfcomm_channel = (int)rfcomm_channel;
-}
-
-static inline void cleanup_socket_info(JNIEnv *env, native_data_t *nat) {
-    if (nat->c_address != NULL) {
-        env->ReleaseStringUTFChars(nat->address, nat->c_address);
-        env->DeleteGlobalRef(nat->address);
-        nat->c_address = NULL;
-    }
-}
-#endif
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    field_mNativeData = get_field(env, clazz, "mNativeData", "I");
-    field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I");
-    field_mAcceptTimeoutRemainingMs = get_field(env, clazz, "mAcceptTimeoutRemainingMs", "I");
-    field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;");
-    field_mPort = get_field(env, clazz, "mPort", "I");
-#endif
-}
-
-static void initializeNativeDataNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-
-    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
-    if (nat == NULL) {
-        LOGE("%s: out of memory!", __FUNCTION__);
-        return;
-    }
-
-    env->SetIntField(object, field_mNativeData, (jint)nat);
-    nat->rfcomm_sock = -1;
-    nat->rfcomm_connected = 0;
-#endif
-}
-
-static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        free(nat);
-    }
-#endif
-}
-
-static jobject createNative(JNIEnv *env, jobject obj) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    int lm;
-    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 NULL;
-    }
-        
-    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 NULL;
-    }
-
-    return jniCreateFileDescriptor(env, nat->rfcomm_sock);
-#else
-    return NULL;
-#endif
-}
-
-static void destroyNative(JNIEnv *env, jobject obj) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-    cleanup_socket_info(env, nat);
-    if (nat->rfcomm_sock >= 0) {
-        close(nat->rfcomm_sock);
-        nat->rfcomm_sock = -1;
-    }
-#endif
-}
-
-
-static jboolean connectNative(JNIEnv *env, jobject obj,
-                              jstring address, jint port) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-
-    if (nat->rfcomm_sock >= 0) {
-        if (nat->rfcomm_connected) {
-            LOGI("RFCOMM socket: %s.",
-                 (nat->rfcomm_connected > 0) ? "already connected" : "connection is in progress");
-            return JNI_TRUE;
-        }
-
-        init_socket_info(env, obj, nat, address, port);
-
-        struct sockaddr_rc addr;
-        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("connect error: %s (%d)\n", strerror(errno), errno);
-                break;
-            } else {
-                nat->rfcomm_connected = 3; // input and output
-            }
-        }
-    } else {
-        LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
-    }
-
-    if (nat->rfcomm_connected > 0) {
-        env->SetIntField(obj, field_mPort, port);
-        return JNI_TRUE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean connectAsyncNative(JNIEnv *env, jobject obj,
-                                   jstring address, jint port) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-
-    if (nat->rfcomm_sock < 0) {
-        LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
-        return JNI_FALSE;
-    }
-
-    if (nat->rfcomm_connected) {
-        LOGI("RFCOMM socket: %s.",
-             (nat->rfcomm_connected > 0) ?
-             "already connected" : "connection is in progress");
-        return JNI_TRUE;
-    }
-
-    init_socket_info(env, obj, nat, address, port);
-
-    struct sockaddr_rc addr;
-    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_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 = 3;
-            LOGI("RFCOMM async connect immediately successful");
-            env->SetIntField(obj, field_mPort, port);
-            return JNI_TRUE;
-        }
-        else if (rc < 0) {
-            if (errno == EINPROGRESS || errno == EAGAIN)
-                {
-                    LOGI("RFCOMM async connect is in progress (%s)",
-                         strerror(errno));
-                    nat->rfcomm_connected = -1;
-                    env->SetIntField(obj, field_mPort, port);
-                    return JNI_TRUE;
-                }
-            else
-                {
-                    LOGE("RFCOMM async connect error (%d): %s (%d)",
-                         nat->rfcomm_sock, strerror(errno), errno);
-                    return JNI_FALSE;
-                }
-        }
-    } // fcntl(nat->rfcomm_sock ...)
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean interruptAsyncConnectNative(JNIEnv *env, jobject obj) {
-    //WRITEME
-    return JNI_TRUE;
-}
-
-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_sock < 0) {
-        LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
-        return -1;
-    }
-
-    if (nat->rfcomm_connected > 0) {
-        LOGI("%s: RFCOMM is already connected!", __FUNCTION__);
-        return 1;
-    }
-
-    /* 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;
-        LOGI("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 -1;
-        }
-        return 0;
-    }
-    /* n must be equal to 1 and either rset or wset must have the
-       file descriptor set. */
-    LOGI("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);
-            return -1;
-        }
-        /* 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 = 3; // input and output
-        return 1;
-    }
-#endif
-    return -1;
-}
-
-static jboolean shutdownNative(JNIEnv *env, jobject obj,
-                jboolean shutdownInput) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    /* NOTE: If you change the bcode to modify nat, make sure you 
-       add synchronize(this) to the method calling this native
-       method. 
-    */
-    native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-    int rc = shutdown(nat->rfcomm_sock, 
-            shutdownInput ? SHUT_RD : SHUT_WR);
-    if (!rc) {
-        nat->rfcomm_connected &= 
-            shutdownInput ? ~1 : ~2;
-        return JNI_TRUE;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jint isConnectedNative(JNIEnv *env, jobject obj) {
-    LOGI(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    const native_data_t *nat = get_native_data(env, obj);
-    return nat->rfcomm_connected;
-#endif
-    return 0;
-}
-
-//@@@@@@@@@ bind to device???
-static jboolean bindNative(JNIEnv *env, jobject obj, jstring device,
-                           jint port) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-
-    /* NOTE: If you change the code to modify nat, make sure you
-       add synchronize(this) to the method calling this native
-       method.
-    */
-    const native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-
-    struct sockaddr_rc laddr;
-    int lm;
-
-    lm = 0;
-/*
-    lm |= RFCOMM_LM_MASTER;
-    lm |= RFCOMM_LM_AUTH;
-    lm |= RFCOMM_LM_ENCRYPT;
-    lm |= RFCOMM_LM_SECURE;
-*/
-
-    if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
-        LOGE("Can't set RFCOMM link mode");
-        return JNI_FALSE;
-    }
-
-    laddr.rc_family = AF_BLUETOOTH;
-    bacpy(&laddr.rc_bdaddr, BDADDR_ANY);
-    laddr.rc_channel = port;
-
-    if (bind(nat->rfcomm_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
-        LOGE("Can't bind RFCOMM socket");
-        return JNI_FALSE;
-    }
-
-    env->SetIntField(obj, field_mPort, port);
-
-    return JNI_TRUE;
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean listenNative(JNIEnv *env, jobject obj, jint backlog) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    /* NOTE: If you change the code to modify nat, make sure you
-       add synchronize(this) to the method calling this native
-       method.
-    */
-    const native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-    return listen(nat->rfcomm_sock, backlog) < 0 ? JNI_FALSE : JNI_TRUE;
-#else
-    return JNI_FALSE;
-#endif
-}
-
-static int set_nb(int sk, bool nb) {
-    int flags = fcntl(sk, F_GETFL);
-    if (flags < 0) {
-        LOGE("Can't get socket flags with fcntl(): %s (%d)",
-             strerror(errno), errno);
-        close(sk);
-        return -1;
-    }
-    flags &= ~O_NONBLOCK;
-    if (nb) flags |= O_NONBLOCK;
-    int status = fcntl(sk, F_SETFL, flags);
-    if (status < 0) {
-        LOGE("Can't set socket to nonblocking mode with fcntl(): %s (%d)",
-             strerror(errno), errno);
-        close(sk);
-        return -1;
-    }
-    return 0;
-}
-
-// Note: the code should check at a higher level to see whether
-// listen() has been called.
-#ifdef HAVE_BLUETOOTH
-static int do_accept(JNIEnv* env, jobject object, int sock,
-                     jobject newsock,
-                     jfieldID out_address,
-                     bool must_succeed) {
-
-    if (must_succeed && set_nb(sock, true) < 0)
-        return -1;
-
-    struct sockaddr_rc raddr;
-    int alen = sizeof(raddr);
-    int nsk = accept(sock, (struct sockaddr *) &raddr, &alen);
-    if (nsk < 0) {
-        LOGE("Error on accept from socket fd %d: %s (%d).",
-             sock,
-             strerror(errno),
-             errno);
-        if (must_succeed) set_nb(sock, false);
-        return -1;
-    }
-
-    char addr[BTADDR_SIZE];
-    get_bdaddr_as_string(&raddr.rc_bdaddr, addr);
-    env->SetObjectField(newsock, out_address, env->NewStringUTF(addr));
-
-    LOGI("Successful accept() on AG socket %d: new socket %d, address %s, RFCOMM channel %d",
-         sock,
-         nsk,
-         addr,
-         raddr.rc_channel);
-    if (must_succeed) set_nb(sock, false);
-    return nsk;
-}
-#endif /*HAVE_BLUETOOTH*/
-
-static jobject acceptNative(JNIEnv *env, jobject obj,
-                            jobject newsock, jint timeoutMs) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, obj);
-    if (nat->rfcomm_sock < 0) {
-        LOGE("socket(RFCOMM) error: socket not created");
-        return JNI_FALSE;
-    }
-
-    if (newsock == NULL) {
-        LOGE("%s: newsock = NULL\n", __FUNCTION__);
-        return JNI_FALSE;
-    }
-
-    int nsk = -1;
-    if (timeoutMs < 0) {
-        /* block until accept() succeeds */
-        nsk = do_accept(env, obj, nat->rfcomm_sock,
-                        newsock, field_mAddress, false);
-        if (nsk < 0) {
-            return NULL;
-        }
-    }
-    else {
-        /* wait with a timeout */
-
-        struct pollfd fds;
-        fds.fd = nat->rfcomm_sock;
-        fds.events = POLLIN | POLLPRI | POLLOUT | POLLERR;
-
-        env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, 0);
-        int n = poll(&fds, 1, timeoutMs);
-        if (n <= 0) {
-            if (n < 0)  {
-                LOGE("listening poll() on RFCOMM socket: %s (%d)",
-                     strerror(errno),
-                     errno);
-                env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, timeoutMs);
-            }
-            else {
-                LOGI("listening poll() on RFCOMM socket timed out");
-            }
-            return NULL;
-        }
-
-        LOGI("listening poll() on RFCOMM socket returned %d", n);
-        if (fds.fd == nat->rfcomm_sock) {
-            if (fds.revents & (POLLIN | POLLPRI | POLLOUT)) {
-                LOGI("Accepting connection.\n");
-                nsk = do_accept(env, obj, nat->rfcomm_sock,
-                                newsock, field_mAddress, true);
-                if (nsk < 0) {
-                    return NULL;
-                }
-            }
-        }
-    }
-
-    LOGI("Connection accepted, new socket fd = %d.", nsk);
-    native_data_t *newnat = get_native_data(env, newsock);
-    newnat->rfcomm_sock = nsk;
-    newnat->rfcomm_connected = 3;
-    return jniCreateFileDescriptor(env, nsk);
-#else
-    return NULL;
-#endif
-}
-
-static JNINativeMethod sMethods[] = {
-     /* name, signature, funcPtr */
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
-    {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
-
-    {"createNative", "()Ljava/io/FileDescriptor;", (void *)createNative},
-    {"destroyNative", "()V", (void *)destroyNative},
-    {"connectNative", "(Ljava/lang/String;I)Z", (void *)connectNative},
-    {"connectAsyncNative", "(Ljava/lang/String;I)Z", (void *)connectAsyncNative},
-    {"interruptAsyncConnectNative", "()Z", (void *)interruptAsyncConnectNative},
-    {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative},
-    {"shutdownNative", "(Z)Z", (void *)shutdownNative},
-    {"isConnectedNative", "()I", (void *)isConnectedNative},
-
-    {"bindNative", "(Ljava/lang/String;I)Z", (void*)bindNative},
-    {"listenNative", "(I)Z", (void*)listenNative},
-    {"acceptNative", "(Landroid/bluetooth/RfcommSocket;I)Ljava/io/FileDescriptor;", (void*)acceptNative},
-};
-
-int register_android_bluetooth_RfcommSocket(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(env,
-        "android/bluetooth/RfcommSocket", sMethods, NELEM(sMethods));
-}
-
-} /* namespace android */
index c81af1c..2bab208 100644 (file)
@@ -384,17 +384,18 @@ jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply) {
     return byteArray;
 }
 
-void get_bdaddr(const char *str, bdaddr_t *ba) {
+int get_bdaddr(const char *str, bdaddr_t *ba) {
     char *d = ((char *)ba) + 5, *endp;
     int i;
     for(i = 0; i < 6; i++) {
         *d-- = strtol(str, &endp, 16);
         if (*endp != ':' && i != 5) {
             memset(ba, 0, sizeof(bdaddr_t));
-            return;
+            return -1;
         }
         str = endp + 1;
     }
+    return 0;
 }
 
 void get_bdaddr_as_string(const bdaddr_t *ba, char *str) {
index c30ba22..f0fbbb5 100644 (file)
@@ -135,7 +135,7 @@ jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply);
 jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply);
 jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply);
 
-void get_bdaddr(const char *str, bdaddr_t *ba);
+int get_bdaddr(const char *str, bdaddr_t *ba);
 void get_bdaddr_as_string(const bdaddr_t *ba, char *str);
 
 bool debug_no_encrypt();