FileDocCategorySizeDatePackage
BluetoothSocket.javaAPI DocAndroid 5.1 API21253Thu Mar 12 22:22:10 GMT 2015android.bluetooth

BluetoothSocket

public final class BluetoothSocket extends Object implements Closeable
A 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_RFCOMM
Keep 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
mSocketState
prevents 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.

param
type type of socket
param
fd fd to use for connected socket, or -1 for a new socket
param
auth require the remote device to be authenticated
param
encrypt require the connection to be encrypted
param
device remote device that this socket can connect to
param
port remote port
param
uuid SDP uuid
throws
IOException On error, for example Bluetooth not available, or insufficient privileges


       
    //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.

param
type type of socket
param
fd fd to use for connected socket, or -1 for a new socket
param
auth require the remote device to be authenticated
param
encrypt require the connection to be encrypted
param
address remote device that this socket can connect to
param
port remote port
throws
IOException On error, for example Bluetooth not available, or insufficient privileges

        this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null);
    
Methods Summary
android.bluetooth.BluetoothSocketaccept(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.BluetoothSocketacceptSocket(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;
    
intavailable()

        if (VDBG) Log.d(TAG, "available: " + mSocketIS);
        return mSocketIS.available();
    
intbindListen()
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 voidclose()

        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 voidconnect()
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.

throws
IOException on error, for example connection failure

        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.StringconvertAddr(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 voidfinalize()

hide

        try {
            close();
        } finally {
            super.finalize();
        }
    
voidflush()
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.

throws
IOException if an i/o error occurs.

        if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
        if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
        mSocketOS.flush();
    
public java.io.InputStreamgetInputStream()
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

        return mInputStream;
    
public java.io.OutputStreamgetOutputStream()
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

        return mOutputStream;
    
intgetPort()

        return mPort;
    
public BluetoothDevicegetRemoteDevice()
Get the remote device this socket is connecting, or connected, to.

return
remote device

        return mDevice;
    
private intgetSecurityFlags()

        int flags = 0;
        if(mAuth)
            flags |= SEC_FLAG_AUTH;
        if(mEncrypt)
            flags |= SEC_FLAG_ENCRYPT;
        return flags;
    
public booleanisConnected()
Get the connection status of this socket, ie, whether there is an active connection with remote device.

return
true if connected false if not connected

        return mSocketState == SocketState.CONNECTED;
    
intread(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 intreadAll(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 intreadInt(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();
    
voidremoveChannel()

    
voidsetServiceName(java.lang.String name)

        mServiceName = name;
    
private java.lang.StringwaitSocketSignal(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;
    
intwrite(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;