Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39f133ee authored by Stanley Tng's avatar Stanley Tng Committed by android-build-merger
Browse files

Merge "Added APIs for Connection-oriented channels" am: e1992384

am: 288915be

Change-Id: Iaf2aa40e8e6e2621c146fcabe0f46c3dd1bfedc4
parents 90a56a59 288915be
Loading
Loading
Loading
Loading
+121 −5
Original line number Original line Diff line number Diff line
@@ -79,8 +79,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 * {@link BluetoothDevice} objects representing all paired devices with
 * {@link BluetoothDevice} objects representing all paired devices with
 * {@link #getBondedDevices()}; start device discovery with
 * {@link #getBondedDevices()}; start device discovery with
 * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
 * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
 * listen for incoming connection requests with
 * listen for incoming RFComm connection requests with {@link
 * {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
 * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
 * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for
 * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
 * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
 * </p>
 * </p>
 * <p>This class is thread safe.</p>
 * <p>This class is thread safe.</p>
@@ -209,6 +210,14 @@ public final class BluetoothAdapter {
     */
     */
    public static final int STATE_BLE_TURNING_OFF = 16;
    public static final int STATE_BLE_TURNING_OFF = 16;


    /**
     * UUID of the GATT Read Characteristics for LE_PSM value.
     *
     * @hide
     */
    public static final UUID LE_PSM_CHARACTERISTIC_UUID =
            UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");

    /**
    /**
     * Human-readable string helper for AdapterState
     * Human-readable string helper for AdapterState
     *
     *
@@ -2156,7 +2165,9 @@ public final class BluetoothAdapter {
                        min16DigitPin);
                        min16DigitPin);
        int errno = socket.mSocket.bindListen();
        int errno = socket.mSocket.bindListen();
        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
            socket.setChannel(socket.mSocket.getPort());
            int assignedChannel = socket.mSocket.getPort();
            if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel);
            socket.setChannel(assignedChannel);
        }
        }
        if (errno != 0) {
        if (errno != 0) {
            //TODO(BT): Throw the same exception error code
            //TODO(BT): Throw the same exception error code
@@ -2197,12 +2208,18 @@ public final class BluetoothAdapter {
     * @hide
     * @hide
     */
     */
    public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
    public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
        Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
        BluetoothServerSocket socket =
        BluetoothServerSocket socket =
                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
                                          false);
                                          false);
        int errno = socket.mSocket.bindListen();
        int errno = socket.mSocket.bindListen();
        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
            socket.setChannel(socket.mSocket.getPort());
            int assignedChannel = socket.mSocket.getPort();
            if (DBG) {
                Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to "
                        + assignedChannel);
            }
            socket.setChannel(assignedChannel);
        }
        }
        if (errno != 0) {
        if (errno != 0) {
            //TODO(BT): Throw the same exception error code
            //TODO(BT): Throw the same exception error code
@@ -2761,4 +2778,103 @@ public final class BluetoothAdapter {
            scanner.stopScan(scanCallback);
            scanner.stopScan(scanCallback);
        }
        }
    }
    }

    /**
     * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
     * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
     * for incoming connections.
     * <p>A remote device connecting to this socket will be authenticated and communication on this
     * socket will be encrypted.
     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
     * {@link BluetoothServerSocket}.
     * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link
     * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
     * closed, Bluetooth is turned off, or the application exits unexpectedly.
     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
     * defined and performed by the application.
     * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
     * socket from another Android device that is given the PSM value.
     *
     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
     * @return an L2CAP CoC BluetoothServerSocket
     * @throws IOException on error, for example Bluetooth not available, or insufficient
     * permissions, or unable to start this CoC
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH)
    public BluetoothServerSocket listenUsingL2capCoc(int transport)
            throws IOException {
        if (transport != BluetoothDevice.TRANSPORT_LE) {
            throw new IllegalArgumentException("Unsupported transport: " + transport);
        }
        BluetoothServerSocket socket =
                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
        int errno = socket.mSocket.bindListen();
        if (errno != 0) {
            throw new IOException("Error: " + errno);
        }

        int assignedPsm = socket.mSocket.getPort();
        if (assignedPsm == 0) {
            throw new IOException("Error: Unable to assign PSM value");
        }
        if (DBG) {
            Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
                    + assignedPsm);
        }
        socket.setChannel(assignedPsm);

        return socket;
    }

    /**
     * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
     * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
     * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
     * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
     * authenticated communication channel is desired.
     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
     * {@link BluetoothServerSocket}.
     * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
     * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released
     * when this server socket is closed, Bluetooth is turned off, or the application exits
     * unexpectedly.
     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
     * defined and performed by the application.
     * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
     * server socket from another Android device that is given the PSM value.
     *
     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
     * @return an L2CAP CoC BluetoothServerSocket
     * @throws IOException on error, for example Bluetooth not available, or insufficient
     * permissions, or unable to start this CoC
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH)
    public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
            throws IOException {
        if (transport != BluetoothDevice.TRANSPORT_LE) {
            throw new IllegalArgumentException("Unsupported transport: " + transport);
        }
        BluetoothServerSocket socket =
                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
        int errno = socket.mSocket.bindListen();
        if (errno != 0) {
            throw new IOException("Error: " + errno);
        }

        int assignedPsm = socket.mSocket.getPort();
        if (assignedPsm == 0) {
            throw new IOException("Error: Unable to assign PSM value");
        }
        if (DBG) {
            Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
                    + assignedPsm);
        }
        socket.setChannel(assignedPsm);

        return socket;
    }
}
}
+71 −0
Original line number Original line Diff line number Diff line
@@ -1921,4 +1921,75 @@ public final class BluetoothDevice implements Parcelable {
        }
        }
        return null;
        return null;
    }
    }

    /**
     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
     * be used to start a secure outgoing connection to the remote device with the same dynamic
     * protocol/service multiplexer (PSM) value.
     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
     * peer-peer Bluetooth applications.
     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
     * <p>Application using this API is responsible for obtaining PSM value from remote device.
     * <p>The remote device will be authenticated and communication on this socket will be
     * encrypted.
     * <p> Use this socket if an authenticated socket link is possible. Authentication refers
     * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a
     * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
     * int)}.
     *
     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
     * @param psm dynamic PSM value from remote device
     * @return a CoC #BluetoothSocket ready for an outgoing connection
     * @throws IOException on error, for example Bluetooth not available, or insufficient
     * permissions
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH)
    public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
        if (!isBluetoothEnabled()) {
            Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
            throw new IOException();
        }
        if (transport != BluetoothDevice.TRANSPORT_LE) {
            throw new IllegalArgumentException("Unsupported transport: " + transport);
        }
        if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
                null);
    }

    /**
     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
     * be used to start a secure outgoing connection to the remote device with the same dynamic
     * protocol/service multiplexer (PSM) value.
     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
     * for peer-peer Bluetooth applications.
     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
     * <p>Application using this API is responsible for obtaining PSM value from remote device.
     * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
     * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
     * authenticated communication channel is possible.
     *
     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
     * @param psm dynamic PSM value from remote device
     * @return a CoC #BluetoothSocket ready for an outgoing connection
     * @throws IOException on error, for example Bluetooth not available, or insufficient
     * permissions
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH)
    public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
        if (!isBluetoothEnabled()) {
            Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
            throw new IOException();
        }
        if (transport != BluetoothDevice.TRANSPORT_LE) {
            throw new IllegalArgumentException("Unsupported transport: " + transport);
        }
        if (DBG) {
            Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
        }
        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
                null);
    }
}
}
+20 −0
Original line number Original line Diff line number Diff line
@@ -68,6 +68,7 @@ import java.io.IOException;
public final class BluetoothServerSocket implements Closeable {
public final class BluetoothServerSocket implements Closeable {


    private static final String TAG = "BluetoothServerSocket";
    private static final String TAG = "BluetoothServerSocket";
    private static final boolean DBG = false;
    /*package*/ final BluetoothSocket mSocket;
    /*package*/ final BluetoothSocket mSocket;
    private Handler mHandler;
    private Handler mHandler;
    private int mMessage;
    private int mMessage;
@@ -169,6 +170,7 @@ public final class BluetoothServerSocket implements Closeable {
     * close any {@link BluetoothSocket} received from {@link #accept()}.
     * close any {@link BluetoothSocket} received from {@link #accept()}.
     */
     */
    public void close() throws IOException {
    public void close() throws IOException {
        if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
        synchronized (this) {
        synchronized (this) {
            if (mHandler != null) {
            if (mHandler != null) {
                mHandler.obtainMessage(mMessage).sendToTarget();
                mHandler.obtainMessage(mMessage).sendToTarget();
@@ -196,6 +198,20 @@ public final class BluetoothServerSocket implements Closeable {
        return mChannel;
        return mChannel;
    }
    }


    /**
     * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
     * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
     * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
     * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
     * method is called on non-L2CAP server sockets.
     *
     * @return the assigned PSM or LE_PSM value depending on transport
     * @hide
     */
    public int getPsm() {
        return mChannel;
    }

    /**
    /**
     * Sets the channel on which future sockets are bound.
     * Sets the channel on which future sockets are bound.
     * Currently used only when a channel is auto generated.
     * Currently used only when a channel is auto generated.
@@ -227,6 +243,10 @@ public final class BluetoothServerSocket implements Closeable {
                sb.append("TYPE_L2CAP");
                sb.append("TYPE_L2CAP");
                break;
                break;
            }
            }
            case BluetoothSocket.TYPE_L2CAP_LE: {
                sb.append("TYPE_L2CAP_LE");
                break;
            }
            case BluetoothSocket.TYPE_SCO: {
            case BluetoothSocket.TYPE_SCO: {
                sb.append("TYPE_SCO");
                sb.append("TYPE_SCO");
                break;
                break;
+15 −4
Original line number Original line Diff line number Diff line
@@ -99,6 +99,16 @@ public final class BluetoothSocket implements Closeable {
    /** L2CAP socket */
    /** L2CAP socket */
    public static final int TYPE_L2CAP = 3;
    public static final int TYPE_L2CAP = 3;


    /** L2CAP socket on BR/EDR transport
     * @hide
     */
    public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP;

    /** L2CAP socket on LE transport
     * @hide
     */
    public static final int TYPE_L2CAP_LE = 4;

    /*package*/ static final int EBADFD = 77;
    /*package*/ static final int EBADFD = 77;
    /*package*/ static final int EADDRINUSE = 98;
    /*package*/ static final int EADDRINUSE = 98;


@@ -417,6 +427,7 @@ public final class BluetoothSocket implements Closeable {
            return -1;
            return -1;
        }
        }
        try {
        try {
            if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType);
            mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
            mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
                    mUuid, mPort, getSecurityFlags());
                    mUuid, mPort, getSecurityFlags());
        } catch (RemoteException e) {
        } catch (RemoteException e) {
@@ -451,7 +462,7 @@ public final class BluetoothSocket implements Closeable {
                    mSocketState = SocketState.LISTENING;
                    mSocketState = SocketState.LISTENING;
                }
                }
            }
            }
            if (DBG) Log.d(TAG, "channel: " + channel);
            if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
            if (mPort <= -1) {
            if (mPort <= -1) {
                mPort = channel;
                mPort = channel;
            } // else ASSERT(mPort == channel)
            } // else ASSERT(mPort == channel)
@@ -515,7 +526,7 @@ public final class BluetoothSocket implements Closeable {
    /*package*/ int read(byte[] b, int offset, int length) throws IOException {
    /*package*/ int read(byte[] b, int offset, int length) throws IOException {
        int ret = 0;
        int ret = 0;
        if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
        if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
        if (mType == TYPE_L2CAP) {
        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
            int bytesToRead = length;
            int bytesToRead = length;
            if (VDBG) {
            if (VDBG) {
                Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
                Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
@@ -558,7 +569,7 @@ public final class BluetoothSocket implements Closeable {
        //      Rfcomm uses dynamic allocation, and should not have any bindings
        //      Rfcomm uses dynamic allocation, and should not have any bindings
        //      to the actual message length.
        //      to the actual message length.
        if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
        if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
        if (mType == TYPE_L2CAP) {
        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
            if (length <= mMaxTxPacketSize) {
            if (length <= mMaxTxPacketSize) {
                mSocketOS.write(b, offset, length);
                mSocketOS.write(b, offset, length);
            } else {
            } else {
@@ -702,7 +713,7 @@ public final class BluetoothSocket implements Closeable {
    }
    }


    private void createL2capRxBuffer() {
    private void createL2capRxBuffer() {
        if (mType == TYPE_L2CAP) {
        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
            // Allocate the buffer to use for reads.
            // Allocate the buffer to use for reads.
            if (VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
            if (VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
            mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
            mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);