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.Arrays;
31import java.util.Locale;
32import java.util.UUID;
33import android.net.LocalSocket;
34
35import java.nio.Buffer;
36import java.nio.ByteOrder;
37import 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/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
80 * </div>
81 *
82 * {@see BluetoothServerSocket}
83 * {@see java.io.InputStream}
84 * {@see java.io.OutputStream}
85 */
86public 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 = LocalSocket.createConnectedLocalSocket(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 = LocalSocket.createConnectedLocalSocket(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 (fd == null) {
420                    Log.e(TAG, "bindListen(), null file descriptor");
421                    return -1;
422                }
423
424                if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket");
425                mSocket = LocalSocket.createConnectedLocalSocket(fd);
426                if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()");
427                mSocketIS = mSocket.getInputStream();
428                mSocketOS = mSocket.getOutputStream();
429            }
430            if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
431            int channel = readInt(mSocketIS);
432            synchronized(this) {
433                if(mSocketState == SocketState.INIT)
434                    mSocketState = SocketState.LISTENING;
435            }
436            if (DBG) Log.d(TAG, "channel: " + channel);
437            if (mPort <= -1) {
438                mPort = channel;
439            } // else ASSERT(mPort == channel)
440            ret = 0;
441        } catch (IOException e) {
442            if (mPfd != null) {
443                try {
444                    mPfd.close();
445                } catch (IOException e1) {
446                    Log.e(TAG, "bindListen, close mPfd: " + e1);
447                }
448                mPfd = null;
449            }
450            Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
451            return -1;
452        }
453        return ret;
454    }
455
456    /*package*/ BluetoothSocket accept(int timeout) throws IOException {
457        BluetoothSocket acceptedSocket;
458        if (mSocketState != SocketState.LISTENING)
459            throw new IOException("bt socket is not in listen state");
460        if(timeout > 0) {
461            Log.d(TAG, "accept() set timeout (ms):" + timeout);
462           mSocket.setSoTimeout(timeout);
463        }
464        String RemoteAddr = waitSocketSignal(mSocketIS);
465        if(timeout > 0)
466            mSocket.setSoTimeout(0);
467        synchronized(this)
468        {
469            if (mSocketState != SocketState.LISTENING)
470                throw new IOException("bt socket is not in listen state");
471            acceptedSocket = acceptSocket(RemoteAddr);
472            //quick drop the reference of the file handle
473        }
474        return acceptedSocket;
475    }
476
477    /*package*/ int available() throws IOException {
478        if (VDBG) Log.d(TAG, "available: " + mSocketIS);
479        return mSocketIS.available();
480    }
481    /**
482     * Wait until the data in sending queue is emptied. A polling version
483     * for flush implementation. Used to ensure the writing data afterwards will
484     * be packed in new RFCOMM frame.
485     * @throws IOException
486     *             if an i/o error occurs.
487     */
488    /*package*/ void flush() throws IOException {
489        if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
490        if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
491        mSocketOS.flush();
492    }
493
494    /*package*/ int read(byte[] b, int offset, int length) throws IOException {
495        int ret = 0;
496        if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
497        if(mType == TYPE_L2CAP)
498        {
499            int bytesToRead = length;
500            if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
501                    + "mL2capBuffer= " + mL2capBuffer);
502            if (mL2capBuffer == null) {
503                createL2capRxBuffer();
504            }
505            if (mL2capBuffer.remaining() == 0) {
506                if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
507                if (fillL2capRxBuffer() == -1) {
508                    return -1;
509                }
510            }
511            if (bytesToRead > mL2capBuffer.remaining()) {
512                bytesToRead = mL2capBuffer.remaining();
513            }
514            if(VDBG) Log.v(TAG, "get(): offset: " + offset
515                    + " bytesToRead: " + bytesToRead);
516            mL2capBuffer.get(b, offset, bytesToRead);
517            ret = bytesToRead;
518        }else {
519            if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
520            ret = mSocketIS.read(b, offset, length);
521        }
522        if (ret < 0)
523            throw new IOException("bt socket closed, read return: " + ret);
524        if (VDBG) Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
525        return ret;
526    }
527
528    /*package*/ int write(byte[] b, int offset, int length) throws IOException {
529
530        //TODO: Since bindings can exist between the SDU size and the
531        //      protocol, we might need to throw an exception instead of just
532        //      splitting the write into multiple smaller writes.
533        //      Rfcomm uses dynamic allocation, and should not have any bindings
534        //      to the actual message length.
535            if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
536            if (mType == TYPE_L2CAP) {
537                if(length <= mMaxTxPacketSize) {
538                    mSocketOS.write(b, offset, length);
539                } else {
540                    if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
541                            + "Packet will be divided into SDU packets of size "
542                            + mMaxTxPacketSize);
543                    int tmpOffset = offset;
544                    int bytesToWrite = length;
545                    while (bytesToWrite > 0) {
546                        int tmpLength = (bytesToWrite > mMaxTxPacketSize)
547                                ? mMaxTxPacketSize
548                                : bytesToWrite;
549                        mSocketOS.write(b, tmpOffset, tmpLength);
550                        tmpOffset += tmpLength;
551                        bytesToWrite -= tmpLength;
552                    }
553                }
554            } else {
555                mSocketOS.write(b, offset, length);
556            }
557            // There is no good way to confirm since the entire process is asynchronous anyway
558            if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
559            return length;
560    }
561
562    @Override
563    public void close() throws IOException {
564        Log.d(TAG, "close() this: " + this + ", channel: " + mPort +
565            ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS +
566            "mSocket: " + mSocket + ", mSocketState: " + mSocketState);
567        if(mSocketState == SocketState.CLOSED)
568            return;
569        else
570        {
571            synchronized(this)
572            {
573                 if(mSocketState == SocketState.CLOSED)
574                    return;
575                 mSocketState = SocketState.CLOSED;
576                 if(mSocket != null) {
577                    if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
578                    mSocket.shutdownInput();
579                    mSocket.shutdownOutput();
580                    mSocket.close();
581                    mSocket = null;
582                }
583                if (mPfd != null) {
584                    mPfd.close();
585                    mPfd = null;
586                }
587           }
588        }
589    }
590
591    /*package */ void removeChannel() {
592    }
593
594    /*package */ int getPort() {
595        return mPort;
596    }
597
598    /**
599     * Get the maximum supported Transmit packet size for the underlying transport.
600     * Use this to optimize the writes done to the output socket, to avoid sending
601     * half full packets.
602     * @return the maximum supported Transmit packet size for the underlying transport.
603     */
604    public int getMaxTransmitPacketSize(){
605        return mMaxTxPacketSize;
606    }
607
608    /**
609     * Get the maximum supported Receive packet size for the underlying transport.
610     * Use this to optimize the reads done on the input stream, as any call to read
611     * will return a maximum of this amount of bytes - or for some transports a
612     * multiple of this value.
613     * @return the maximum supported Receive packet size for the underlying transport.
614     */
615    public int getMaxReceivePacketSize(){
616        return mMaxRxPacketSize;
617    }
618
619    /**
620     * Get the type of the underlying connection.
621     * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
622     */
623    public int getConnectionType() {
624        return mType;
625    }
626
627    /**
628     * Change if a SDP entry should be automatically created.
629     * Must be called before calling .bind, for the call to have any effect.
630     * @param mExcludeSdp <li>TRUE  - do not auto generate SDP record.
631     *                    <li>FALSE - default - auto generate SPP SDP record.
632     * @hide
633     */
634    public void setExcludeSdp(boolean excludeSdp) {
635        this.mExcludeSdp = excludeSdp;
636    }
637
638    private String convertAddr(final byte[] addr)  {
639        return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
640                addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
641    }
642    private String waitSocketSignal(InputStream is) throws IOException {
643        byte [] sig = new byte[SOCK_SIGNAL_SIZE];
644        int ret = readAll(is, sig);
645        if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE +
646                " bytes signal ret: " + ret);
647        ByteBuffer bb = ByteBuffer.wrap(sig);
648        /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
649        bb.order(ByteOrder.nativeOrder());
650        int size = bb.getShort();
651        if(size != SOCK_SIGNAL_SIZE)
652            throw new IOException("Connection failure, wrong signal size: " + size);
653        byte [] addr = new byte[6];
654        bb.get(addr);
655        int channel = bb.getInt();
656        int status = bb.getInt();
657        mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
658        mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
659        String RemoteAddr = convertAddr(addr);
660        if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
661                + RemoteAddr + ", channel: " + channel + ", status: " + status
662                + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
663        if(status != 0)
664            throw new IOException("Connection failure, status: " + status);
665        return RemoteAddr;
666    }
667
668    private void createL2capRxBuffer(){
669        if(mType == TYPE_L2CAP) {
670            // Allocate the buffer to use for reads.
671            if(VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
672            mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
673            if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
674            mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
675            if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" +
676                    mL2capBuffer.remaining());
677        }
678    }
679
680    private int readAll(InputStream is, byte[] b) throws IOException {
681        int left = b.length;
682        while(left > 0) {
683            int ret = is.read(b, b.length - left, left);
684            if(ret <= 0)
685                 throw new IOException("read failed, socket might closed or timeout, read ret: "
686                         + ret);
687            left -= ret;
688            if(left != 0)
689                Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
690                            ", expect size: " + b.length);
691        }
692        return b.length;
693    }
694
695    private int readInt(InputStream is) throws IOException {
696        byte[] ibytes = new byte[4];
697        int ret = readAll(is, ibytes);
698        if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
699        ByteBuffer bb = ByteBuffer.wrap(ibytes);
700        bb.order(ByteOrder.nativeOrder());
701        return bb.getInt();
702    }
703
704    private int fillL2capRxBuffer() throws IOException {
705        mL2capBuffer.rewind();
706        int ret = mSocketIS.read(mL2capBuffer.array());
707        if(ret == -1) {
708            // reached end of stream - return -1
709            mL2capBuffer.limit(0);
710            return -1;
711        }
712        mL2capBuffer.limit(ret);
713        return ret;
714    }
715
716
717}
718