Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.bluetooth;
     18 
     19 import android.os.ParcelUuid;
     20 import android.os.ParcelFileDescriptor;
     21 import android.os.RemoteException;
     22 import android.util.Log;
     23 
     24 import java.io.BufferedInputStream;
     25 import java.io.Closeable;
     26 import java.io.FileDescriptor;
     27 import java.io.IOException;
     28 import java.io.InputStream;
     29 import java.io.OutputStream;
     30 import java.util.Arrays;
     31 import java.util.Locale;
     32 import java.util.UUID;
     33 import android.net.LocalSocket;
     34 
     35 import java.nio.Buffer;
     36 import java.nio.ByteOrder;
     37 import java.nio.ByteBuffer;
     38 /**
     39  * A connected or connecting Bluetooth socket.
     40  *
     41  * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
     42  * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
     43  * side, use a {@link BluetoothServerSocket} to create a listening server
     44  * socket. When a connection is accepted by the {@link BluetoothServerSocket},
     45  * it will return a new {@link BluetoothSocket} to manage the connection.
     46  * On the client side, use a single {@link BluetoothSocket} to both initiate
     47  * an outgoing connection and to manage the connection.
     48  *
     49  * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
     50  * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
     51  * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
     52  *
     53  * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
     54  * {@link BluetoothDevice#createRfcommSocketToServiceRecord
     55  * BluetoothDevice.createRfcommSocketToServiceRecord()}.
     56  * Then call {@link #connect()} to attempt a connection to the remote device.
     57  * This call will block until a connection is established or the connection
     58  * fails.
     59  *
     60  * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
     61  * {@link BluetoothServerSocket} documentation.
     62  *
     63  * <p>Once the socket is connected, whether initiated as a client or accepted
     64  * as a server, open the IO streams by calling {@link #getInputStream} and
     65  * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
     66  * and {@link java.io.OutputStream} objects, respectively, which are
     67  * automatically connected to the socket.
     68  *
     69  * <p>{@link BluetoothSocket} is thread
     70  * safe. In particular, {@link #close} will always immediately abort ongoing
     71  * operations and close the socket.
     72  *
     73  * <p class="note"><strong>Note:</strong>
     74  * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
     75  *
     76  * <div class="special reference">
     77  * <h3>Developer Guides</h3>
     78  * <p>For more information about using Bluetooth, read the
     79  * <a href="{@docRoot}guide/topics/wireless/bluetooth.html">Bluetooth</a> developer guide.</p>
     80  * </div>
     81  *
     82  * {@see BluetoothServerSocket}
     83  * {@see java.io.InputStream}
     84  * {@see java.io.OutputStream}
     85  */
     86 public final class BluetoothSocket implements Closeable {
     87     private static final String TAG = "BluetoothSocket";
     88     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     89     private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
     90 
     91     /** @hide */
     92     public static final int MAX_RFCOMM_CHANNEL = 30;
     93     /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
     94 
     95     /** RFCOMM socket */
     96     public static final int TYPE_RFCOMM = 1;
     97 
     98     /** SCO socket */
     99     public static final int TYPE_SCO = 2;
    100 
    101     /** L2CAP socket */
    102     public static final int TYPE_L2CAP = 3;
    103 
    104     /*package*/ static final int EBADFD = 77;
    105     /*package*/ static final int EADDRINUSE = 98;
    106 
    107     /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
    108     /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
    109     /*package*/ static final int BTSOCK_FLAG_NO_SDP  = 1 << 2;
    110     /*package*/ static final int SEC_FLAG_AUTH_MITM  = 1 << 3;
    111     /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT  = 1 << 4;
    112 
    113     private final int mType;  /* one of TYPE_RFCOMM etc */
    114     private BluetoothDevice mDevice;    /* remote device */
    115     private String mAddress;    /* remote address */
    116     private final boolean mAuth;
    117     private final boolean mEncrypt;
    118     private final BluetoothInputStream mInputStream;
    119     private final BluetoothOutputStream mOutputStream;
    120     private final ParcelUuid mUuid;
    121     private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */
    122     private boolean mAuthMitm = false;   /* when true Man-in-the-middle protection will be enabled*/
    123     private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */
    124     private ParcelFileDescriptor mPfd;
    125     private LocalSocket mSocket;
    126     private InputStream mSocketIS;
    127     private OutputStream mSocketOS;
    128     private int mPort;  /* RFCOMM channel or L2CAP psm */
    129     private int mFd;
    130     private String mServiceName;
    131     private static int PROXY_CONNECTION_TIMEOUT = 5000;
    132 
    133     private static int SOCK_SIGNAL_SIZE = 20;
    134 
    135     private ByteBuffer mL2capBuffer = null;
    136     private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer.
    137     private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received.
    138 
    139     private enum SocketState {
    140         INIT,
    141         CONNECTED,
    142         LISTENING,
    143         CLOSED,
    144     }
    145 
    146     /** prevents all native calls after destroyNative() */
    147     private volatile SocketState mSocketState;
    148 
    149     /** protects mSocketState */
    150     //private final ReentrantReadWriteLock mLock;
    151 
    152     /**
    153      * Construct a BluetoothSocket.
    154      * @param type    type of socket
    155      * @param fd      fd to use for connected socket, or -1 for a new socket
    156      * @param auth    require the remote device to be authenticated
    157      * @param encrypt require the connection to be encrypted
    158      * @param device  remote device that this socket can connect to
    159      * @param port    remote port
    160      * @param uuid    SDP uuid
    161      * @throws IOException On error, for example Bluetooth not available, or
    162      *                     insufficient privileges
    163      */
    164     /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
    165             BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
    166         this(type, fd, auth, encrypt, device, port, uuid, false, false);
    167     }
    168 
    169     /**
    170      * Construct a BluetoothSocket.
    171      * @param type    type of socket
    172      * @param fd      fd to use for connected socket, or -1 for a new socket
    173      * @param auth    require the remote device to be authenticated
    174      * @param encrypt require the connection to be encrypted
    175      * @param device  remote device that this socket can connect to
    176      * @param port    remote port
    177      * @param uuid    SDP uuid
    178      * @param mitm    enforce man-in-the-middle protection.
    179      * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
    180      * @throws IOException On error, for example Bluetooth not available, or
    181      *                     insufficient privileges
    182      */
    183     /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
    184             BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin)
    185                     throws IOException {
    186         if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
    187         if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1
    188                 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
    189             if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
    190                 throw new IOException("Invalid RFCOMM channel: " + port);
    191             }
    192         }
    193         if (uuid != null)
    194             mUuid = uuid;
    195         else mUuid = new ParcelUuid(new UUID(0, 0));
    196         mType = type;
    197         mAuth = auth;
    198         mAuthMitm = mitm;
    199         mMin16DigitPin = min16DigitPin;
    200         mEncrypt = encrypt;
    201         mDevice = device;
    202         mPort = port;
    203         mFd = fd;
    204 
    205         mSocketState = SocketState.INIT;
    206 
    207         if (device == null) {
    208             // Server socket
    209             mAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
    210         } else {
    211             // Remote socket
    212             mAddress = device.getAddress();
    213         }
    214         mInputStream = new BluetoothInputStream(this);
    215         mOutputStream = new BluetoothOutputStream(this);
    216     }
    217     private BluetoothSocket(BluetoothSocket s) {
    218         if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType);
    219         mUuid = s.mUuid;
    220         mType = s.mType;
    221         mAuth = s.mAuth;
    222         mEncrypt = s.mEncrypt;
    223         mPort = s.mPort;
    224         mInputStream = new BluetoothInputStream(this);
    225         mOutputStream = new BluetoothOutputStream(this);
    226         mMaxRxPacketSize = s.mMaxRxPacketSize;
    227         mMaxTxPacketSize = s.mMaxTxPacketSize;
    228 
    229         mServiceName = s.mServiceName;
    230         mExcludeSdp = s.mExcludeSdp;
    231         mAuthMitm = s.mAuthMitm;
    232         mMin16DigitPin = s.mMin16DigitPin;
    233     }
    234     private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException {
    235         BluetoothSocket as = new BluetoothSocket(this);
    236         as.mSocketState = SocketState.CONNECTED;
    237         FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
    238         if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds));
    239         if(fds == null || fds.length != 1) {
    240             Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds));
    241             as.close();
    242             throw new IOException("bt socket acept failed");
    243         }
    244 
    245         as.mPfd = new ParcelFileDescriptor(fds[0]);
    246         as.mSocket = new LocalSocket(fds[0]);
    247         as.mSocketIS = as.mSocket.getInputStream();
    248         as.mSocketOS = as.mSocket.getOutputStream();
    249         as.mAddress = RemoteAddr;
    250         as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr);
    251         return as;
    252     }
    253     /**
    254      * Construct a BluetoothSocket from address. Used by native code.
    255      * @param type    type of socket
    256      * @param fd      fd to use for connected socket, or -1 for a new socket
    257      * @param auth    require the remote device to be authenticated
    258      * @param encrypt require the connection to be encrypted
    259      * @param address remote device that this socket can connect to
    260      * @param port    remote port
    261      * @throws IOException On error, for example Bluetooth not available, or
    262      *                     insufficient privileges
    263      */
    264     private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
    265             int port) throws IOException {
    266         this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false);
    267     }
    268 
    269     /** @hide */
    270     @Override
    271     protected void finalize() throws Throwable {
    272         try {
    273             close();
    274         } finally {
    275             super.finalize();
    276         }
    277     }
    278     private int getSecurityFlags() {
    279         int flags = 0;
    280         if(mAuth)
    281             flags |= SEC_FLAG_AUTH;
    282         if(mEncrypt)
    283             flags |= SEC_FLAG_ENCRYPT;
    284         if(mExcludeSdp)
    285             flags |= BTSOCK_FLAG_NO_SDP;
    286         if(mAuthMitm)
    287             flags |= SEC_FLAG_AUTH_MITM;
    288         if(mMin16DigitPin)
    289             flags |= SEC_FLAG_AUTH_16_DIGIT;
    290         return flags;
    291     }
    292 
    293     /**
    294      * Get the remote device this socket is connecting, or connected, to.
    295      * @return remote device
    296      */
    297     public BluetoothDevice getRemoteDevice() {
    298         return mDevice;
    299     }
    300 
    301     /**
    302      * Get the input stream associated with this socket.
    303      * <p>The input stream will be returned even if the socket is not yet
    304      * connected, but operations on that stream will throw IOException until
    305      * the associated socket is connected.
    306      * @return InputStream
    307      */
    308     public InputStream getInputStream() throws IOException {
    309         return mInputStream;
    310     }
    311 
    312     /**
    313      * Get the output stream associated with this socket.
    314      * <p>The output stream will be returned even if the socket is not yet
    315      * connected, but operations on that stream will throw IOException until
    316      * the associated socket is connected.
    317      * @return OutputStream
    318      */
    319     public OutputStream getOutputStream() throws IOException {
    320         return mOutputStream;
    321     }
    322 
    323     /**
    324      * Get the connection status of this socket, ie, whether there is an active connection with
    325      * remote device.
    326      * @return true if connected
    327      *         false if not connected
    328      */
    329     public boolean isConnected() {
    330         return mSocketState == SocketState.CONNECTED;
    331     }
    332 
    333     /*package*/ void setServiceName(String name) {
    334         mServiceName = name;
    335     }
    336 
    337     /**
    338      * Attempt to connect to a remote device.
    339      * <p>This method will block until a connection is made or the connection
    340      * fails. If this method returns without an exception then this socket
    341      * is now connected.
    342      * <p>Creating new connections to
    343      * remote Bluetooth devices should not be attempted while device discovery
    344      * is in progress. Device discovery is a heavyweight procedure on the
    345      * Bluetooth adapter and will significantly slow a device connection.
    346      * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
    347      * discovery. Discovery is not managed by the Activity,
    348      * but is run as a system service, so an application should always call
    349      * {@link BluetoothAdapter#cancelDiscovery()} even if it
    350      * did not directly request a discovery, just to be sure.
    351      * <p>{@link #close} can be used to abort this call from another thread.
    352      * @throws IOException on error, for example connection failure
    353      */
    354     public void connect() throws IOException {
    355         if (mDevice == null) throw new IOException("Connect is called on null device");
    356 
    357         try {
    358             if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
    359             IBluetooth bluetoothProxy =
    360                     BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
    361             if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
    362             mPfd = bluetoothProxy.connectSocket(mDevice, mType,
    363                     mUuid, mPort, getSecurityFlags());
    364             synchronized(this)
    365             {
    366                 if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
    367                 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
    368                 if (mPfd == null) throw new IOException("bt socket connect failed");
    369                 FileDescriptor fd = mPfd.getFileDescriptor();
    370                 mSocket = new LocalSocket(fd);
    371                 mSocketIS = mSocket.getInputStream();
    372                 mSocketOS = mSocket.getOutputStream();
    373             }
    374             int channel = readInt(mSocketIS);
    375             if (channel <= 0)
    376                 throw new IOException("bt socket connect failed");
    377             mPort = channel;
    378             waitSocketSignal(mSocketIS);
    379             synchronized(this)
    380             {
    381                 if (mSocketState == SocketState.CLOSED)
    382                     throw new IOException("bt socket closed");
    383                 mSocketState = SocketState.CONNECTED;
    384             }
    385         } catch (RemoteException e) {
    386             Log.e(TAG, Log.getStackTraceString(new Throwable()));
    387             throw new IOException("unable to send RPC: " + e.getMessage());
    388         }
    389     }
    390 
    391     /**
    392      * Currently returns unix errno instead of throwing IOException,
    393      * so that BluetoothAdapter can check the error code for EADDRINUSE
    394      */
    395     /*package*/ int bindListen() {
    396         int ret;
    397         if (mSocketState == SocketState.CLOSED) return EBADFD;
    398         IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
    399         if (bluetoothProxy == null) {
    400             Log.e(TAG, "bindListen fail, reason: bluetooth is off");
    401             return -1;
    402         }
    403         try {
    404             mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
    405                     mUuid, mPort, getSecurityFlags());
    406         } catch (RemoteException e) {
    407             Log.e(TAG, Log.getStackTraceString(new Throwable()));
    408             return -1;
    409         }
    410 
    411         // read out port number
    412         try {
    413             synchronized(this) {
    414                 if (DBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " +
    415                                 mPfd);
    416                 if(mSocketState != SocketState.INIT) return EBADFD;
    417                 if(mPfd == null) return -1;
    418                 FileDescriptor fd = mPfd.getFileDescriptor();
    419                 if (DBG) Log.d(TAG, "bindListen(), new LocalSocket ");
    420                 mSocket = new LocalSocket(fd);
    421                 if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
    422                 mSocketIS = mSocket.getInputStream();
    423                 mSocketOS = mSocket.getOutputStream();
    424             }
    425             if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
    426             int channel = readInt(mSocketIS);
    427             synchronized(this) {
    428                 if(mSocketState == SocketState.INIT)
    429                     mSocketState = SocketState.LISTENING;
    430             }
    431             if (DBG) Log.d(TAG, "channel: " + channel);
    432             if (mPort <= -1) {
    433                 mPort = channel;
    434             } // else ASSERT(mPort == channel)
    435             ret = 0;
    436         } catch (IOException e) {
    437             if (mPfd != null) {
    438                 try {
    439                     mPfd.close();
    440                 } catch (IOException e1) {
    441                     Log.e(TAG, "bindListen, close mPfd: " + e1);
    442                 }
    443                 mPfd = null;
    444             }
    445             Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
    446             return -1;
    447         }
    448         return ret;
    449     }
    450 
    451     /*package*/ BluetoothSocket accept(int timeout) throws IOException {
    452         BluetoothSocket acceptedSocket;
    453         if (mSocketState != SocketState.LISTENING)
    454             throw new IOException("bt socket is not in listen state");
    455         if(timeout > 0) {
    456             Log.d(TAG, "accept() set timeout (ms):" + timeout);
    457            mSocket.setSoTimeout(timeout);
    458         }
    459         String RemoteAddr = waitSocketSignal(mSocketIS);
    460         if(timeout > 0)
    461             mSocket.setSoTimeout(0);
    462         synchronized(this)
    463         {
    464             if (mSocketState != SocketState.LISTENING)
    465                 throw new IOException("bt socket is not in listen state");
    466             acceptedSocket = acceptSocket(RemoteAddr);
    467             //quick drop the reference of the file handle
    468         }
    469         return acceptedSocket;
    470     }
    471 
    472     /*package*/ int available() throws IOException {
    473         if (VDBG) Log.d(TAG, "available: " + mSocketIS);
    474         return mSocketIS.available();
    475     }
    476     /**
    477      * Wait until the data in sending queue is emptied. A polling version
    478      * for flush implementation. Used to ensure the writing data afterwards will
    479      * be packed in new RFCOMM frame.
    480      * @throws IOException
    481      *             if an i/o error occurs.
    482      */
    483     /*package*/ void flush() throws IOException {
    484         if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
    485         if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
    486         mSocketOS.flush();
    487     }
    488 
    489     /*package*/ int read(byte[] b, int offset, int length) throws IOException {
    490         int ret = 0;
    491         if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
    492         if(mType == TYPE_L2CAP)
    493         {
    494             int bytesToRead = length;
    495             if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
    496                     + "mL2capBuffer= " + mL2capBuffer);
    497             if (mL2capBuffer == null) {
    498                 createL2capRxBuffer();
    499             }
    500             if (mL2capBuffer.remaining() == 0) {
    501                 if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
    502                 if (fillL2capRxBuffer() == -1) {
    503                     return -1;
    504                 }
    505             }
    506             if (bytesToRead > mL2capBuffer.remaining()) {
    507                 bytesToRead = mL2capBuffer.remaining();
    508             }
    509             if(VDBG) Log.v(TAG, "get(): offset: " + offset
    510                     + " bytesToRead: " + bytesToRead);
    511             mL2capBuffer.get(b, offset, bytesToRead);
    512             ret = bytesToRead;
    513         }else {
    514             if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
    515             ret = mSocketIS.read(b, offset, length);
    516         }
    517         if (ret < 0)
    518             throw new IOException("bt socket closed, read return: " + ret);
    519         if (VDBG) Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
    520         return ret;
    521     }
    522 
    523     /*package*/ int write(byte[] b, int offset, int length) throws IOException {
    524 
    525         //TODO: Since bindings can exist between the SDU size and the
    526         //      protocol, we might need to throw an exception instead of just
    527         //      splitting the write into multiple smaller writes.
    528         //      Rfcomm uses dynamic allocation, and should not have any bindings
    529         //      to the actual message length.
    530             if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
    531             if (mType == TYPE_L2CAP) {
    532                 if(length <= mMaxTxPacketSize) {
    533                     mSocketOS.write(b, offset, length);
    534                 } else {
    535                     if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
    536                             + "Packet will be divided into SDU packets of size "
    537                             + mMaxTxPacketSize);
    538                     int tmpOffset = offset;
    539                     int bytesToWrite = length;
    540                     while (bytesToWrite > 0) {
    541                         int tmpLength = (bytesToWrite > mMaxTxPacketSize)
    542                                 ? mMaxTxPacketSize
    543                                 : bytesToWrite;
    544                         mSocketOS.write(b, tmpOffset, tmpLength);
    545                         tmpOffset += tmpLength;
    546                         bytesToWrite -= tmpLength;
    547                     }
    548                 }
    549             } else {
    550                 mSocketOS.write(b, offset, length);
    551             }
    552             // There is no good way to confirm since the entire process is asynchronous anyway
    553             if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
    554             return length;
    555     }
    556 
    557     @Override
    558     public void close() throws IOException {
    559         if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: "
    560                 + mSocketState);
    561         if(mSocketState == SocketState.CLOSED)
    562             return;
    563         else
    564         {
    565             synchronized(this)
    566             {
    567                  if(mSocketState == SocketState.CLOSED)
    568                     return;
    569                  mSocketState = SocketState.CLOSED;
    570                  if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort +
    571                          ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS +
    572                          "mSocket: " + mSocket);
    573                  if(mSocket != null) {
    574                     if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
    575                     mSocket.shutdownInput();
    576                     mSocket.shutdownOutput();
    577                     mSocket.close();
    578                     mSocket = null;
    579                 }
    580                 if (mPfd != null) {
    581                     mPfd.close();
    582                     mPfd = null;
    583                 }
    584            }
    585         }
    586     }
    587 
    588     /*package */ void removeChannel() {
    589     }
    590 
    591     /*package */ int getPort() {
    592         return mPort;
    593     }
    594 
    595     /**
    596      * Get the maximum supported Transmit packet size for the underlying transport.
    597      * Use this to optimize the writes done to the output socket, to avoid sending
    598      * half full packets.
    599      * @return the maximum supported Transmit packet size for the underlying transport.
    600      */
    601     public int getMaxTransmitPacketSize(){
    602         return mMaxTxPacketSize;
    603     }
    604 
    605     /**
    606      * Get the maximum supported Receive packet size for the underlying transport.
    607      * Use this to optimize the reads done on the input stream, as any call to read
    608      * will return a maximum of this amount of bytes - or for some transports a
    609      * multiple of this value.
    610      * @return the maximum supported Receive packet size for the underlying transport.
    611      */
    612     public int getMaxReceivePacketSize(){
    613         return mMaxRxPacketSize;
    614     }
    615 
    616     /**
    617      * Get the type of the underlying connection.
    618      * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
    619      */
    620     public int getConnectionType() {
    621         return mType;
    622     }
    623 
    624     /**
    625      * Change if a SDP entry should be automatically created.
    626      * Must be called before calling .bind, for the call to have any effect.
    627      * @param mExcludeSdp <li>TRUE  - do not auto generate SDP record.
    628      *                    <li>FALSE - default - auto generate SPP SDP record.
    629      * @hide
    630      */
    631     public void setExcludeSdp(boolean excludeSdp) {
    632         this.mExcludeSdp = excludeSdp;
    633     }
    634 
    635     private String convertAddr(final byte[] addr)  {
    636         return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
    637                 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
    638     }
    639     private String waitSocketSignal(InputStream is) throws IOException {
    640         byte [] sig = new byte[SOCK_SIGNAL_SIZE];
    641         int ret = readAll(is, sig);
    642         if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE +
    643                 " bytes signal ret: " + ret);
    644         ByteBuffer bb = ByteBuffer.wrap(sig);
    645         /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
    646         bb.order(ByteOrder.nativeOrder());
    647         int size = bb.getShort();
    648         if(size != SOCK_SIGNAL_SIZE)
    649             throw new IOException("Connection failure, wrong signal size: " + size);
    650         byte [] addr = new byte[6];
    651         bb.get(addr);
    652         int channel = bb.getInt();
    653         int status = bb.getInt();
    654         mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
    655         mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
    656         String RemoteAddr = convertAddr(addr);
    657         if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
    658                 + RemoteAddr + ", channel: " + channel + ", status: " + status
    659                 + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
    660         if(status != 0)
    661             throw new IOException("Connection failure, status: " + status);
    662         return RemoteAddr;
    663     }
    664 
    665     private void createL2capRxBuffer(){
    666         if(mType == TYPE_L2CAP) {
    667             // Allocate the buffer to use for reads.
    668             if(VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
    669             mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
    670             if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
    671             mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
    672             if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" +
    673                     mL2capBuffer.remaining());
    674         }
    675     }
    676 
    677     private int readAll(InputStream is, byte[] b) throws IOException {
    678         int left = b.length;
    679         while(left > 0) {
    680             int ret = is.read(b, b.length - left, left);
    681             if(ret <= 0)
    682                  throw new IOException("read failed, socket might closed or timeout, read ret: "
    683                          + ret);
    684             left -= ret;
    685             if(left != 0)
    686                 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
    687                             ", expect size: " + b.length);
    688         }
    689         return b.length;
    690     }
    691 
    692     private int readInt(InputStream is) throws IOException {
    693         byte[] ibytes = new byte[4];
    694         int ret = readAll(is, ibytes);
    695         if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
    696         ByteBuffer bb = ByteBuffer.wrap(ibytes);
    697         bb.order(ByteOrder.nativeOrder());
    698         return bb.getInt();
    699     }
    700 
    701     private int fillL2capRxBuffer() throws IOException {
    702         mL2capBuffer.rewind();
    703         int ret = mSocketIS.read(mL2capBuffer.array());
    704         if(ret == -1) {
    705             // reached end of stream - return -1
    706             mL2capBuffer.limit(0);
    707             return -1;
    708         }
    709         mL2capBuffer.limit(ret);
    710         return ret;
    711     }
    712 
    713 
    714 }
    715