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