summaryrefslogtreecommitdiff
path: root/framework
diff options
context:
space:
mode:
author Bhakthavatsala Raghavendra <bhaktha@google.com> 2024-11-08 00:16:04 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2024-11-08 00:16:04 +0000
commit891fea7286c9e0ebe376e1554afcb288eed366eb (patch)
tree6d992fb78e7e5f814baddeae96479814023166d1 /framework
parent635ddf506f7799301aca728e4db554b1e2b54eda (diff)
parentaf3a017be782f21896c101264b31af55a5cf0314 (diff)
Merge "Add socket settings interface" into main
Diffstat (limited to 'framework')
-rw-r--r--framework/api/current.txt23
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java81
-rw-r--r--framework/java/android/bluetooth/BluetoothDevice.java54
-rw-r--r--framework/java/android/bluetooth/BluetoothSocket.java30
-rw-r--r--framework/java/android/bluetooth/BluetoothSocketSettings.java349
5 files changed, 527 insertions, 10 deletions
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 4347504e24..6f27962380 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -52,6 +52,7 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
+ method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingSocketSettings(@NonNull android.bluetooth.BluetoothSocketSettings) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setName(String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startDiscovery();
method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
@@ -531,6 +532,7 @@ package android.bluetooth {
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method @NonNull public android.bluetooth.BluetoothSocket createL2capChannel(int) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
+ method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @NonNull public android.bluetooth.BluetoothSocket createUsingSocketSettings(@NonNull android.bluetooth.BluetoothSocketSettings) throws java.io.IOException;
method public int describeContents();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean fetchUuidsWithSdp();
method public String getAddress();
@@ -1080,6 +1082,7 @@ package android.bluetooth {
method public android.bluetooth.BluetoothDevice getRemoteDevice();
method public boolean isConnected();
field public static final int TYPE_L2CAP = 3; // 0x3
+ field @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public static final int TYPE_LE = 4; // 0x4
field public static final int TYPE_RFCOMM = 1; // 0x1
field public static final int TYPE_SCO = 2; // 0x2
}
@@ -1112,6 +1115,26 @@ package android.bluetooth {
field public static final int UNSPECIFIED = 0; // 0x0
}
+ @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public final class BluetoothSocketSettings {
+ method @IntRange(from=128, to=255) public int getL2capPsm();
+ method @Nullable public String getRfcommServiceName();
+ method @Nullable public java.util.UUID getRfcommUuid();
+ method public int getSocketType();
+ method public boolean isAuthenticationRequired();
+ method public boolean isEncryptionRequired();
+ }
+
+ @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public static final class BluetoothSocketSettings.Builder {
+ ctor public BluetoothSocketSettings.Builder();
+ method @NonNull public android.bluetooth.BluetoothSocketSettings build();
+ method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setAuthenticationRequired(boolean);
+ method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setEncryptionRequired(boolean);
+ method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setL2capPsm(@IntRange(from=128, to=255) int);
+ method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setRfcommServiceName(@NonNull String);
+ method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setRfcommUuid(@NonNull java.util.UUID);
+ method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setSocketType(int);
+ }
+
public final class BluetoothStatusCodes {
field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index 64055909bc..8b814cb6bf 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -4542,8 +4542,7 @@ public final class BluetoothAdapter {
* another Android device that is given the PSM value.
*
* @return an L2CAP CoC BluetoothServerSocket
- * @throws IOException on error, for example Bluetooth not available, or insufficient
- * permissions, or unable to start this CoC
+ * @throws IOException on error, for example Bluetooth not available or unable to start this CoC
*/
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@@ -4598,8 +4597,7 @@ public final class BluetoothAdapter {
* socket from another Android device that is given the PSM value.
*
* @return an L2CAP CoC BluetoothServerSocket
- * @throws IOException on error, for example Bluetooth not available, or insufficient
- * permissions, or unable to start this CoC
+ * @throws IOException on error, for example Bluetooth not available or unable to start this CoC
*/
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@@ -4631,6 +4629,81 @@ public final class BluetoothAdapter {
}
/**
+ * Creates a listening server channel for Bluetooth connections with the specified socket
+ * settings {@link BluetoothSocketSettings}.
+ *
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
+ * {@link BluetoothServerSocket}.
+ *
+ * <p>This API supports {@link BluetoothSocket#TYPE_RFCOMM} and {{@link BluetoothSocket#TYPE_LE}
+ * only, which can be set using {@link BluetoothSocketSettings#setSocketType()}.
+ * <li>For `BluetoothSocket.TYPE_RFCOMM`: The RFCOMM UUID must be provided using {@link
+ * BluetoothSocketSettings#setRfcommUuid()}.
+ * <li>For `BluetoothSocket.TYPE_LE`: The system assigns a dynamic protocol/service multiplexer
+ * (PSM) value. This value can be read from {@link BluetoothServerSocket#getPsm()}. This
+ * value is released when the server socket is closed, Bluetooth is turned off, or the
+ * application exits unexpectedly. The mechanism for disclosing the PSM value to the client
+ * is application-defined.
+ *
+ * <p>Use {@link BluetoothDevice#createUsingSocketSettings(BluetoothSocketSettings)} to
+ * connect to this server socket from another Android device using the L2cap
+ * protocol/service multiplexer(PSM) value or the RFCOMM service UUID as input.
+ *
+ * @param settings Bluetooth socket settings {@link BluetoothSocketSettings}.
+ * @return a {@link BluetoothServerSocket}
+ * @throws IllegalArgumentException if BluetoothSocket#TYPE_RFCOMM socket is requested with no
+ * UUID.
+ * @throws IOException on error, for example Bluetooth not available or unable to start this LE
+ * Connection-oriented Channel (CoC).
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+ public @NonNull BluetoothServerSocket listenUsingSocketSettings(
+ @NonNull BluetoothSocketSettings settings) throws IOException {
+
+ BluetoothServerSocket socket;
+ int type = settings.getSocketType();
+ if (type == BluetoothSocket.TYPE_RFCOMM) {
+ if (settings.getRfcommUuid() == null) {
+ throw new IllegalArgumentException("RFCOMM server missing UUID");
+ }
+ return createNewRfcommSocketAndRecord(
+ settings.getRfcommServiceName(),
+ settings.getRfcommUuid(),
+ settings.isAuthenticationRequired(),
+ settings.isEncryptionRequired());
+ } else if (type == BluetoothSocket.TYPE_LE) {
+ socket =
+ new BluetoothServerSocket(
+ settings.getSocketType(),
+ settings.isAuthenticationRequired(),
+ settings.isEncryptionRequired(),
+ SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
+ false,
+ false);
+ } else {
+ throw new IOException("Error: Invalid socket type: " + type);
+ }
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ throw new IOException("Error: " + errno);
+ }
+ if (type == BluetoothSocket.TYPE_LE) {
+ int assignedPsm = socket.mSocket.getPort();
+ if (assignedPsm == 0) {
+ throw new IOException("Error: Unable to assign PSM value");
+ }
+ if (DBG) {
+ Log.d(TAG, "listenUsingSocketSettings: set assigned PSM to " + assignedPsm);
+ }
+ socket.setChannel(assignedPsm);
+ }
+
+ return socket;
+ }
+
+ /**
* Register a {@link #OnMetadataChangedListener} to receive update about metadata changes for
* this {@link BluetoothDevice}. Registration must be done when Bluetooth is ON and will last
* until {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index 0fa2ad4789..9f01575cbc 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -3218,6 +3218,60 @@ public final class BluetoothDevice implements Parcelable, Attributable {
}
/**
+ * Creates a client socket to connect to a remote Bluetooth server with the specified socket
+ * settings {@link BluetoothSocketSettings} This API is used to connect to a remote server
+ * hosted using {@link BluetoothAdapter#listenUsingSocketSettings}.
+ *
+ * <ul>
+ * <li>For `BluetoothSocket.TYPE_RFCOMM`: The RFCOMM UUID must be provided using {@link
+ * BluetoothSocketSettings#setRfcommUuid()}.
+ * <li>For `BluetoothSocket.TYPE_LE`: The L2cap protocol/service multiplexer (PSM) value must
+ * be provided using {@link BluetoothSocketSettings#setL2capPsm()}.
+ * </ul>
+ *
+ * <p>Application using this API is responsible for obtaining protocol/service multiplexer (psm)
+ * value from remote device.
+ *
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
+ *
+ * @param settings Bluetooth socket settings {@link BluetoothSocketSettings}.
+ * @return a {@link BluetoothSocket} ready for an outgoing connection.
+ * @throws IllegalArgumentException if BluetoothSocket#TYPE_RFCOMM socket with no UUID is passed
+ * as input or if BluetoothSocket#TYPE_LE with invalid PSM is passed.
+ * @throws IOException on error, for example Bluetooth not available.
+ */
+ @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+ public @NonNull BluetoothSocket createUsingSocketSettings(
+ @NonNull BluetoothSocketSettings settings) throws IOException {
+ if (!isBluetoothEnabled()) {
+ Log.e(TAG, "createUsingSocketSettings: Bluetooth is not enabled");
+ throw new IOException();
+ }
+ if (DBG) {
+ Log.d(TAG, "createUsingSocketSettings: =" + settings.getL2capPsm());
+ }
+ ParcelUuid uuid = null;
+ int psm = settings.getL2capPsm();
+ if (settings.getSocketType() == BluetoothSocket.TYPE_RFCOMM) {
+ if (settings.getRfcommUuid() == null) {
+ throw new IllegalArgumentException("null uuid: " + settings.getRfcommUuid());
+ }
+ uuid = new ParcelUuid(settings.getRfcommUuid());
+ } else if (settings.getSocketType() == BluetoothSocket.TYPE_LE) {
+ if (psm < 128 || psm > 255) {
+ throw new IllegalArgumentException("Invalid PSM/Channel value: " + psm);
+ }
+ }
+ return new BluetoothSocket(
+ this,
+ settings.getSocketType(),
+ settings.isAuthenticationRequired(),
+ settings.isEncryptionRequired(),
+ psm,
+ uuid);
+ }
+
+ /**
* Set a keyed metadata of this {@link BluetoothDevice} to a {@link String} value. Only bonded
* devices's metadata will be persisted across Bluetooth restart. Metadata will be removed when
* the device's bond state is moved to {@link #BOND_NONE}.
diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java
index a104101f12..901407c00b 100644
--- a/framework/java/android/bluetooth/BluetoothSocket.java
+++ b/framework/java/android/bluetooth/BluetoothSocket.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
@@ -42,6 +43,8 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
@@ -112,6 +115,8 @@ public final class BluetoothSocket implements Closeable {
/**
* L2CAP socket on BR/EDR transport
*
+ * <p>To be removed once Flags.FLAG_SOCKET_SETTINGS_API is removed
+ *
* @hide
*/
public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP;
@@ -119,10 +124,28 @@ public final class BluetoothSocket implements Closeable {
/**
* L2CAP socket on LE transport
*
+ * <p>To be removed once Flags.FLAG_SOCKET_SETTINGS_API is removed
+ *
* @hide
*/
public static final int TYPE_L2CAP_LE = 4;
+ /** L2CAP socket on LE transport */
+ @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+ public static final int TYPE_LE = 4;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"BluetoothSocket.TYPE_"},
+ value = {
+ BluetoothSocket.TYPE_RFCOMM,
+ BluetoothSocket.TYPE_SCO,
+ BluetoothSocket.TYPE_L2CAP,
+ BluetoothSocket.TYPE_LE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SocketType {}
+
/*package*/ static final int EBADFD = 77;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -195,12 +218,7 @@ public final class BluetoothSocket implements Closeable {
* @throws IOException On error, for example Bluetooth not available, or insufficient privileges
*/
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, LOCAL_MAC_ADDRESS})
- /*package*/ BluetoothSocket(
- int type,
- boolean auth,
- boolean encrypt,
- int port,
- ParcelUuid uuid)
+ /*package*/ BluetoothSocket(int type, boolean auth, boolean encrypt, int port, ParcelUuid uuid)
throws IOException {
this(type, auth, encrypt, port, uuid, false, false);
}
diff --git a/framework/java/android/bluetooth/BluetoothSocketSettings.java b/framework/java/android/bluetooth/BluetoothSocketSettings.java
new file mode 100644
index 0000000000..6fa8b0dc8b
--- /dev/null
+++ b/framework/java/android/bluetooth/BluetoothSocketSettings.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import static android.bluetooth.BluetoothSocket.SocketType;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
+
+import com.android.bluetooth.flags.Flags;
+
+import java.util.UUID;
+
+/**
+ * Defines parameters for creating Bluetooth server and client socket channels.
+ *
+ * <p>Used with {@link BluetoothAdapter#listenUsingSocketSettings} to create a server socket and
+ * {@link BluetoothDevice#createUsingSocketSettings} to create a client socket.
+ *
+ * @see BluetoothAdapter#listenUsingSocketSettings
+ * @see BluetoothDevice#createUsingSocketSettings
+ */
+@FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+public final class BluetoothSocketSettings {
+
+ private static final int L2CAP_PSM_UNSPECIFIED = -1;
+
+ /** Type of the Bluetooth socket */
+ @SocketType private int mSocketType;
+
+ /** Encryption requirement for the Bluetooth socket. */
+ private boolean mEncryptionRequired;
+
+ /** Authentication requirement for the Bluetooth socket. */
+ private boolean mAuthenticationRequired;
+
+ /** L2CAP Protocol/Service Multiplexer (PSM) for the Bluetooth Socket. */
+ private int mL2capPsm;
+
+ /** RFCOMM service name associated with the Bluetooth socket. */
+ private String mRfcommServiceName;
+
+ /** RFCOMM service UUID associated with the Bluetooth socket. */
+ private UUID mRfcommUuid;
+
+ /**
+ * Returns the type of the Bluetooth socket.
+ *
+ * <p>Defaults to {@code BluetoothSocket#TYPE_RFCOMM}.
+ */
+ @RequiresNoPermission
+ @SocketType
+ public int getSocketType() {
+ return mSocketType;
+ }
+
+ /** Returns the L2CAP PSM value used for a BluetoothSocket#TYPE_LE socket. */
+ @RequiresNoPermission
+ public @IntRange(from = 128, to = 255) int getL2capPsm() {
+ return mL2capPsm;
+ }
+
+ /**
+ * Returns the RFCOMM service name used for a BluetoothSocket#TYPE_RFCOMM socket.
+ *
+ * <p>Defaults to {@code null}.
+ */
+ @Nullable
+ @RequiresNoPermission
+ public String getRfcommServiceName() {
+ return mRfcommServiceName;
+ }
+
+ /**
+ * Returns the RFCOMM service UUID used for a BluetoothSocket#TYPE_RFCOMM socket.
+ *
+ * <p>Defaults to {@code null}.
+ */
+ @Nullable
+ @RequiresNoPermission
+ public UUID getRfcommUuid() {
+ return mRfcommUuid;
+ }
+
+ /**
+ * Checks if encryption is enabled for the Bluetooth socket.
+ *
+ * <p>Defaults to {@code false}.
+ */
+ @RequiresNoPermission
+ public boolean isEncryptionRequired() {
+ return mEncryptionRequired;
+ }
+
+ /**
+ * Checks if authentication is enabled for the Bluetooth socket.
+ *
+ * <p>Defaults to {@code false}.
+ */
+ @RequiresNoPermission
+ public boolean isAuthenticationRequired() {
+ return mAuthenticationRequired;
+ }
+
+ /**
+ * Returns a {@link String} that describes each BluetoothSocketSettings parameter current value.
+ */
+ @Override
+ public String toString() {
+ if (mSocketType == BluetoothSocket.TYPE_RFCOMM) {
+ return "BluetoothSocketSettings{"
+ + "mSocketType="
+ + mSocketType
+ + ", mEncryptionRequired="
+ + mEncryptionRequired
+ + ", mAuthenticationRequired="
+ + mAuthenticationRequired
+ + ", mRfcommServiceName="
+ + mRfcommServiceName
+ + ", mRfcommUuid="
+ + mRfcommUuid
+ + "}";
+ } else {
+ return "BluetoothSocketSettings{"
+ + "mSocketType="
+ + mSocketType
+ + ", mL2capPsm="
+ + mL2capPsm
+ + ", mEncryptionRequired="
+ + mEncryptionRequired
+ + ", mAuthenticationRequired="
+ + mAuthenticationRequired
+ + "}";
+ }
+ }
+
+ private BluetoothSocketSettings(
+ int socketType,
+ int l2capPsm,
+ boolean encryptionRequired,
+ boolean authenticationRequired,
+ String rfcommServiceName,
+ UUID rfcommUuid) {
+ mSocketType = socketType;
+ mL2capPsm = l2capPsm;
+ mEncryptionRequired = encryptionRequired;
+ mAuthenticationRequired = authenticationRequired;
+ mRfcommUuid = rfcommUuid;
+ mRfcommServiceName = rfcommServiceName;
+ }
+
+ /** Builder for {@link BluetoothSocketSettings}. */
+ @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+ public static final class Builder {
+ private int mSocketType = BluetoothSocket.TYPE_RFCOMM;
+ private int mL2capPsm = L2CAP_PSM_UNSPECIFIED;
+ private boolean mEncryptionRequired = false;
+ private boolean mAuthenticationRequired = false;
+ private String mRfcommServiceName = null;
+ private UUID mRfcommUuid = null;
+
+ public Builder() {}
+
+ /**
+ * Sets the socket type.
+ *
+ * <p>Must be one of:
+ *
+ * <ul>
+ * <li>{@link BluetoothSocket#TYPE_RFCOMM}
+ * <li>{@link BluetoothSocket#TYPE_LE}
+ * </ul>
+ *
+ * <p>Defaults to {@code BluetoothSocket#TYPE_RFCOMM}.
+ *
+ * @param socketType The type of socket.
+ * @return This builder.
+ * @throws IllegalArgumentException If {@code socketType} is invalid.
+ */
+ @NonNull
+ @RequiresNoPermission
+ public Builder setSocketType(@SocketType int socketType) {
+ if (socketType != BluetoothSocket.TYPE_RFCOMM
+ && socketType != BluetoothSocket.TYPE_LE) {
+ throw new IllegalArgumentException("invalid socketType - " + socketType);
+ }
+ mSocketType = socketType;
+ return this;
+ }
+
+ /**
+ * Sets the L2CAP PSM (Protocol/Service Multiplexer) for the Bluetooth socket.
+ *
+ * <p>This is only used for {@link BluetoothSocket#TYPE_LE} sockets.
+ *
+ * <p>Valid PSM values for {@link BluetoothSocket#TYPE_LE} sockets is ranging from 128
+ * (0x80) to 255 (0xFF).
+ *
+ * <p>Application using this API is responsible for obtaining protocol/service multiplexer
+ * (PSM) value from remote device.
+ *
+ * @param l2capPsm The L2CAP PSM value.
+ * @return This builder.
+ * @throws IllegalArgumentException If l2cap PSM is not in given range.
+ */
+ @NonNull
+ @RequiresNoPermission
+ public Builder setL2capPsm(@IntRange(from = 128, to = 255) int l2capPsm) {
+ if (l2capPsm < 128 || l2capPsm > 255) {
+ throw new IllegalArgumentException("invalid L2cap PSM - " + l2capPsm);
+ }
+ mL2capPsm = l2capPsm;
+ return this;
+ }
+
+ /**
+ * Sets the encryption requirement for the Bluetooth socket.
+ *
+ * <p>Defaults to {@code false}.
+ *
+ * @param encryptionRequired {@code true} if encryption is required for this socket, {@code
+ * false} otherwise.
+ * @return This builder.
+ */
+ @NonNull
+ @RequiresNoPermission
+ public Builder setEncryptionRequired(boolean encryptionRequired) {
+ mEncryptionRequired = encryptionRequired;
+ return this;
+ }
+
+ /**
+ * Sets the authentication requirement for the Bluetooth socket.
+ *
+ * <p>Defaults to {@code false}.
+ *
+ * @param authenticationRequired {@code true} if authentication is required for this socket,
+ * {@code false} otherwise.
+ * @return This builder.
+ */
+ @NonNull
+ @RequiresNoPermission
+ public Builder setAuthenticationRequired(boolean authenticationRequired) {
+ mAuthenticationRequired = authenticationRequired;
+ return this;
+ }
+
+ /**
+ * Sets the RFCOMM service name associated with the Bluetooth socket.
+ *
+ * <p>This name is used to identify the service when a remote device searches for it using
+ * SDP.
+ *
+ * <p>This is only used for {@link BluetoothSocket#TYPE_RFCOMM} sockets.
+ *
+ * <p>Defaults to {@code null}.
+ *
+ * @param rfcommServiceName The RFCOMM service name.
+ * @return This builder.
+ */
+ @NonNull
+ @RequiresNoPermission
+ public Builder setRfcommServiceName(@NonNull String rfcommServiceName) {
+ mRfcommServiceName = rfcommServiceName;
+ return this;
+ }
+
+ /**
+ * Sets the RFCOMM service UUID associated with the Bluetooth socket.
+ *
+ * <p>This UUID is used to uniquely identify the service when a remote device searches for
+ * it using SDP.
+ *
+ * <p>This is only used for {@link BluetoothSocket#TYPE_RFCOMM} sockets.
+ *
+ * <p>Defaults to {@code null}.
+ *
+ * @param rfcommUuid The RFCOMM service UUID.
+ * @return This builder.
+ */
+ @NonNull
+ @RequiresNoPermission
+ public Builder setRfcommUuid(@NonNull UUID rfcommUuid) {
+ mRfcommUuid = rfcommUuid;
+ return this;
+ }
+
+ /**
+ * Builds a {@link BluetoothSocketSettings} object.
+ *
+ * @return A new {@link BluetoothSocketSettings} object with the configured parameters.
+ * @throws IllegalArgumentException on invalid parameters
+ */
+ @NonNull
+ @RequiresNoPermission
+ public BluetoothSocketSettings build() {
+ if (mSocketType == BluetoothSocket.TYPE_RFCOMM) {
+ if (mRfcommUuid == null) {
+ throw new IllegalArgumentException("RFCOMM socket with missing uuid");
+ }
+ if (mL2capPsm != L2CAP_PSM_UNSPECIFIED) {
+ throw new IllegalArgumentException(
+ "Invalid Socket config: "
+ + " Socket type: "
+ + mSocketType
+ + " L2cap PSM: "
+ + mL2capPsm);
+ }
+ }
+ if (mSocketType == BluetoothSocket.TYPE_LE) {
+ if (mRfcommUuid != null) {
+ throw new IllegalArgumentException(
+ "Invalid Socket config: "
+ + "Socket type: "
+ + mSocketType
+ + " Rfcomm Service Name: "
+ + mRfcommServiceName
+ + " Rfcomm Uuid: "
+ + mRfcommUuid);
+ }
+ }
+
+ return new BluetoothSocketSettings(
+ mSocketType,
+ mL2capPsm,
+ mEncryptionRequired,
+ mAuthenticationRequired,
+ mRfcommServiceName,
+ mRfcommUuid);
+ }
+ }
+}