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