BluetoothSocketpublic final class BluetoothSocket extends Object implements CloseableA connected or connecting Bluetooth socket.
The interface for Bluetooth Sockets is similar to that of TCP sockets:
{@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
side, use a {@link BluetoothServerSocket} to create a listening server
socket. When a connection is accepted by the {@link BluetoothServerSocket},
it will return a new {@link BluetoothSocket} to manage the connection.
On the client side, use a single {@link BluetoothSocket} to both initiate
an outgoing connection and to manage the connection.
The most common type of Bluetooth socket is RFCOMM, which is the type
supported by the Android APIs. RFCOMM is a connection-oriented, streaming
transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
To create a {@link BluetoothSocket} for connecting to a known device, use
{@link BluetoothDevice#createRfcommSocketToServiceRecord
BluetoothDevice.createRfcommSocketToServiceRecord()}.
Then call {@link #connect()} to attempt a connection to the remote device.
This call will block until a connection is established or the connection
fails.
To create a {@link BluetoothSocket} as a server (or "host"), see the
{@link BluetoothServerSocket} documentation.
Once the socket is connected, whether initiated as a client or accepted
as a server, open the IO streams by calling {@link #getInputStream} and
{@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
and {@link java.io.OutputStream} objects, respectively, which are
automatically connected to the socket.
{@link BluetoothSocket} is thread
safe. In particular, {@link #close} will always immediately abort ongoing
operations and close the socket.
Note:
Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
Developer Guides
For more information about using Bluetooth, read the
Bluetooth developer guide.
{@see BluetoothServerSocket}
{@see java.io.InputStream}
{@see java.io.OutputStream} |
Fields Summary |
---|
private static final String | TAG | private static final boolean | DBG | private static final boolean | VDBG | public static final int | MAX_RFCOMM_CHANNEL | static final int | TYPE_RFCOMMKeep TYPE_ fields in sync with BluetoothSocket.cpp | static final int | TYPE_SCO | static final int | TYPE_L2CAP | static final int | EBADFD | static final int | EADDRINUSE | static final int | SEC_FLAG_ENCRYPT | static final int | SEC_FLAG_AUTH | private final int | mType | private BluetoothDevice | mDevice | private String | mAddress | private final boolean | mAuth | private final boolean | mEncrypt | private final BluetoothInputStream | mInputStream | private final BluetoothOutputStream | mOutputStream | private final android.os.ParcelUuid | mUuid | private android.os.ParcelFileDescriptor | mPfd | private android.net.LocalSocket | mSocket | private InputStream | mSocketIS | private OutputStream | mSocketOS | private int | mPort | private int | mFd | private String | mServiceName | private static int | PROXY_CONNECTION_TIMEOUT | private static int | SOCK_SIGNAL_SIZE | private volatile SocketState | mSocketStateprevents all native calls after destroyNative() |
Constructors Summary |
---|
BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, android.os.ParcelUuid uuid)Construct a BluetoothSocket.
//private final ReentrantReadWriteLock mLock;
/*package*/
if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) {
if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
throw new IOException("Invalid RFCOMM channel: " + port);
}
}
if(uuid != null)
mUuid = uuid;
else mUuid = new ParcelUuid(new UUID(0, 0));
mType = type;
mAuth = auth;
mEncrypt = encrypt;
mDevice = device;
mPort = port;
mFd = fd;
mSocketState = SocketState.INIT;
if (device == null) {
// Server socket
mAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
} else {
// Remote socket
mAddress = device.getAddress();
}
mInputStream = new BluetoothInputStream(this);
mOutputStream = new BluetoothOutputStream(this);
| private BluetoothSocket(BluetoothSocket s)
mUuid = s.mUuid;
mType = s.mType;
mAuth = s.mAuth;
mEncrypt = s.mEncrypt;
mPort = s.mPort;
mInputStream = new BluetoothInputStream(this);
mOutputStream = new BluetoothOutputStream(this);
mServiceName = s.mServiceName;
| private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port)Construct a BluetoothSocket from address. Used by native code.
this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null);
|
Methods Summary |
---|
android.bluetooth.BluetoothSocket | accept(int timeout)
BluetoothSocket acceptedSocket;
if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state");
if(timeout > 0) {
Log.d(TAG, "accept() set timeout (ms):" + timeout);
mSocket.setSoTimeout(timeout);
}
String RemoteAddr = waitSocketSignal(mSocketIS);
if(timeout > 0)
mSocket.setSoTimeout(0);
synchronized(this)
{
if (mSocketState != SocketState.LISTENING)
throw new IOException("bt socket is not in listen state");
acceptedSocket = acceptSocket(RemoteAddr);
//quick drop the reference of the file handle
}
return acceptedSocket;
| private android.bluetooth.BluetoothSocket | acceptSocket(java.lang.String RemoteAddr)
BluetoothSocket as = new BluetoothSocket(this);
as.mSocketState = SocketState.CONNECTED;
FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + fds);
if(fds == null || fds.length != 1) {
Log.e(TAG, "socket fd passed from stack failed, fds: " + fds);
as.close();
throw new IOException("bt socket acept failed");
}
as.mSocket = new LocalSocket(fds[0]);
as.mSocketIS = as.mSocket.getInputStream();
as.mSocketOS = as.mSocket.getOutputStream();
as.mAddress = RemoteAddr;
as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr);
return as;
| int | available()
if (VDBG) Log.d(TAG, "available: " + mSocketIS);
return mSocketIS.available();
| int | bindListen()Currently returns unix errno instead of throwing IOException,
so that BluetoothAdapter can check the error code for EADDRINUSE
int ret;
if (mSocketState == SocketState.CLOSED) return EBADFD;
IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
if (bluetoothProxy == null) {
Log.e(TAG, "bindListen fail, reason: bluetooth is off");
return -1;
}
try {
mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
mUuid, mPort, getSecurityFlags());
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return -1;
}
// read out port number
try {
synchronized(this) {
if (DBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " +
mPfd);
if(mSocketState != SocketState.INIT) return EBADFD;
if(mPfd == null) return -1;
FileDescriptor fd = mPfd.getFileDescriptor();
if (DBG) Log.d(TAG, "bindListen(), new LocalSocket ");
mSocket = new LocalSocket(fd);
if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
mSocketIS = mSocket.getInputStream();
mSocketOS = mSocket.getOutputStream();
}
if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
int channel = readInt(mSocketIS);
synchronized(this) {
if(mSocketState == SocketState.INIT)
mSocketState = SocketState.LISTENING;
}
if (DBG) Log.d(TAG, "channel: " + channel);
if (mPort == -1) {
mPort = channel;
} // else ASSERT(mPort == channel)
ret = 0;
} catch (IOException e) {
if (mPfd != null) {
try {
mPfd.close();
} catch (IOException e1) {
Log.e(TAG, "bindListen, close mPfd: " + e1);
}
mPfd = null;
}
Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
return -1;
}
return ret;
| public void | close()
if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState);
if(mSocketState == SocketState.CLOSED)
return;
else
{
synchronized(this)
{
if(mSocketState == SocketState.CLOSED)
return;
mSocketState = SocketState.CLOSED;
if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
", mSocketOS: " + mSocketOS + "mSocket: " + mSocket);
if(mSocket != null) {
if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
mSocket.shutdownInput();
mSocket.shutdownOutput();
mSocket.close();
mSocket = null;
}
if (mPfd != null) {
mPfd.close();
mPfd = null;
}
}
}
| public void | connect()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.
Creating new connections to
remote Bluetooth devices should not be attempted while device discovery
is in progress. Device discovery is a heavyweight procedure on the
Bluetooth adapter and will significantly slow a device connection.
Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
discovery. Discovery is not managed by the Activity,
but is run as a system service, so an application should always call
{@link BluetoothAdapter#cancelDiscovery()} even if it
did not directly request a discovery, just to be sure.
{@link #close} can be used to abort this call from another thread.
if (mDevice == null) throw new IOException("Connect is called on null device");
try {
if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
mPfd = bluetoothProxy.connectSocket(mDevice, mType,
mUuid, mPort, getSecurityFlags());
synchronized(this)
{
if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
if (mPfd == null) throw new IOException("bt socket connect failed");
FileDescriptor fd = mPfd.getFileDescriptor();
mSocket = new LocalSocket(fd);
mSocketIS = mSocket.getInputStream();
mSocketOS = mSocket.getOutputStream();
}
int channel = readInt(mSocketIS);
if (channel <= 0)
throw new IOException("bt socket connect failed");
mPort = channel;
waitSocketSignal(mSocketIS);
synchronized(this)
{
if (mSocketState == SocketState.CLOSED)
throw new IOException("bt socket closed");
mSocketState = SocketState.CONNECTED;
}
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
throw new IOException("unable to send RPC: " + e.getMessage());
}
| private java.lang.String | convertAddr(byte[] addr)
return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
| protected void | finalize()
try {
close();
} finally {
super.finalize();
}
| void | flush()Wait until the data in sending queue is emptied. A polling version
for flush implementation. Used to ensure the writing data afterwards will
be packed in new RFCOMM frame.
if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
mSocketOS.flush();
| public java.io.InputStream | getInputStream()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 mInputStream;
| public java.io.OutputStream | getOutputStream()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 mOutputStream;
| int | getPort()
return mPort;
| public BluetoothDevice | getRemoteDevice()Get the remote device this socket is connecting, or connected, to.
return mDevice;
| private int | getSecurityFlags()
int flags = 0;
if(mAuth)
flags |= SEC_FLAG_AUTH;
if(mEncrypt)
flags |= SEC_FLAG_ENCRYPT;
return flags;
| public boolean | isConnected()Get the connection status of this socket, ie, whether there is an active connection with
remote device.
return mSocketState == SocketState.CONNECTED;
| int | read(byte[] b, int offset, int length)
if (mSocketIS == null) throw new IOException("read is called on null InputStream");
if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
int ret = mSocketIS.read(b, offset, length);
if(ret < 0)
throw new IOException("bt socket closed, read return: " + ret);
if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret);
return ret;
| private int | readAll(java.io.InputStream is, byte[] b)
int left = b.length;
while(left > 0) {
int ret = is.read(b, b.length - left, left);
if(ret <= 0)
throw new IOException("read failed, socket might closed or timeout, read ret: " + ret);
left -= ret;
if(left != 0)
Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
", expect size: " + b.length);
}
return b.length;
| private int | readInt(java.io.InputStream is)
byte[] ibytes = new byte[4];
int ret = readAll(is, ibytes);
if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
ByteBuffer bb = ByteBuffer.wrap(ibytes);
bb.order(ByteOrder.nativeOrder());
return bb.getInt();
| void | removeChannel()
| void | setServiceName(java.lang.String name)
mServiceName = name;
| private java.lang.String | waitSocketSignal(java.io.InputStream is)
byte [] sig = new byte[SOCK_SIGNAL_SIZE];
int ret = readAll(is, sig);
if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
ByteBuffer bb = ByteBuffer.wrap(sig);
bb.order(ByteOrder.nativeOrder());
int size = bb.getShort();
if(size != SOCK_SIGNAL_SIZE)
throw new IOException("Connection failure, wrong signal size: " + size);
byte [] addr = new byte[6];
bb.get(addr);
int channel = bb.getInt();
int status = bb.getInt();
String RemoteAddr = convertAddr(addr);
if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
+ RemoteAddr + ", channel: " + channel + ", status: " + status);
if(status != 0)
throw new IOException("Connection failure, status: " + status);
return RemoteAddr;
| int | write(byte[] b, int offset, int length)
if (mSocketOS == null) throw new IOException("write is called on null OutputStream");
if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
mSocketOS.write(b, offset, length);
// There is no good way to confirm since the entire process is asynchronous anyway
if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
return length;
|
|