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

Commit fca64263 authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

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

Merge "Merge "Added APIs for Connection-oriented channels" am: e1992384 am: 288915be am: 39f133ee"
parents 0c10e4b7 518c679c
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
     *
     *
@@ -2160,7 +2169,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
@@ -2201,12 +2212,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
@@ -2765,4 +2782,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]);