Loading core/java/android/bluetooth/BluetoothAdapter.java +121 −5 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 * * Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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; } } } core/java/android/bluetooth/BluetoothDevice.java +71 −0 Original line number Original line Diff line number Diff line Loading @@ -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); } } } core/java/android/bluetooth/BluetoothServerSocket.java +20 −0 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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. Loading Loading @@ -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; Loading core/java/android/bluetooth/BluetoothSocket.java +15 −4 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -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]); Loading Loading
core/java/android/bluetooth/BluetoothAdapter.java +121 −5 Original line number Original line Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 * * Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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; } } }
core/java/android/bluetooth/BluetoothDevice.java +71 −0 Original line number Original line Diff line number Diff line Loading @@ -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); } } }
core/java/android/bluetooth/BluetoothServerSocket.java +20 −0 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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. Loading Loading @@ -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; Loading
core/java/android/bluetooth/BluetoothSocket.java +15 −4 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -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]); Loading