Merge "Added APIs for Connection-oriented channels"
am: e1992384e0
Change-Id: If7cb99a59aee8640f6dddd96f2388e2a5284df45
This commit is contained in:
@@ -79,8 +79,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
* {@link BluetoothDevice} objects representing all paired devices with
|
||||
* {@link #getBondedDevices()}; start device discovery with
|
||||
* {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
|
||||
* listen for incoming connection requests with
|
||||
* {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
|
||||
* listen for incoming RFComm connection requests with {@link
|
||||
* #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)}.
|
||||
* </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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@@ -2156,7 +2165,9 @@ public final class BluetoothAdapter {
|
||||
min16DigitPin);
|
||||
int errno = socket.mSocket.bindListen();
|
||||
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) {
|
||||
//TODO(BT): Throw the same exception error code
|
||||
@@ -2197,12 +2208,18 @@ public final class BluetoothAdapter {
|
||||
* @hide
|
||||
*/
|
||||
public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
|
||||
Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
|
||||
BluetoothServerSocket socket =
|
||||
new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
|
||||
false);
|
||||
false);
|
||||
int errno = socket.mSocket.bindListen();
|
||||
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) {
|
||||
//TODO(BT): Throw the same exception error code
|
||||
@@ -2761,4 +2778,103 @@ public final class BluetoothAdapter {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1921,4 +1921,75 @@ public final class BluetoothDevice implements Parcelable {
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ import java.io.IOException;
|
||||
public final class BluetoothServerSocket implements Closeable {
|
||||
|
||||
private static final String TAG = "BluetoothServerSocket";
|
||||
private static final boolean DBG = false;
|
||||
/*package*/ final BluetoothSocket mSocket;
|
||||
private Handler mHandler;
|
||||
private int mMessage;
|
||||
@@ -169,6 +170,7 @@ public final class BluetoothServerSocket implements Closeable {
|
||||
* close any {@link BluetoothSocket} received from {@link #accept()}.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
|
||||
synchronized (this) {
|
||||
if (mHandler != null) {
|
||||
mHandler.obtainMessage(mMessage).sendToTarget();
|
||||
@@ -196,6 +198,20 @@ public final class BluetoothServerSocket implements Closeable {
|
||||
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.
|
||||
* Currently used only when a channel is auto generated.
|
||||
@@ -227,6 +243,10 @@ public final class BluetoothServerSocket implements Closeable {
|
||||
sb.append("TYPE_L2CAP");
|
||||
break;
|
||||
}
|
||||
case BluetoothSocket.TYPE_L2CAP_LE: {
|
||||
sb.append("TYPE_L2CAP_LE");
|
||||
break;
|
||||
}
|
||||
case BluetoothSocket.TYPE_SCO: {
|
||||
sb.append("TYPE_SCO");
|
||||
break;
|
||||
|
||||
@@ -99,6 +99,16 @@ public final class BluetoothSocket implements Closeable {
|
||||
/** L2CAP socket */
|
||||
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 EADDRINUSE = 98;
|
||||
|
||||
@@ -417,6 +427,7 @@ public final class BluetoothSocket implements Closeable {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType);
|
||||
mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
|
||||
mUuid, mPort, getSecurityFlags());
|
||||
} catch (RemoteException e) {
|
||||
@@ -451,7 +462,7 @@ public final class BluetoothSocket implements Closeable {
|
||||
mSocketState = SocketState.LISTENING;
|
||||
}
|
||||
}
|
||||
if (DBG) Log.d(TAG, "channel: " + channel);
|
||||
if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
|
||||
if (mPort <= -1) {
|
||||
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 {
|
||||
int ret = 0;
|
||||
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;
|
||||
if (VDBG) {
|
||||
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
|
||||
// to the actual message 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) {
|
||||
mSocketOS.write(b, offset, length);
|
||||
} else {
|
||||
@@ -702,7 +713,7 @@ public final class BluetoothSocket implements Closeable {
|
||||
}
|
||||
|
||||
private void createL2capRxBuffer() {
|
||||
if (mType == TYPE_L2CAP) {
|
||||
if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
|
||||
// Allocate the buffer to use for reads.
|
||||
if (VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
|
||||
mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
|
||||
|
||||
Reference in New Issue
Block a user