diff options
| author | 2024-11-08 00:16:04 +0000 | |
|---|---|---|
| committer | 2024-11-08 00:16:04 +0000 | |
| commit | 891fea7286c9e0ebe376e1554afcb288eed366eb (patch) | |
| tree | 6d992fb78e7e5f814baddeae96479814023166d1 /framework | |
| parent | 635ddf506f7799301aca728e4db554b1e2b54eda (diff) | |
| parent | af3a017be782f21896c101264b31af55a5cf0314 (diff) | |
Merge "Add socket settings interface" into main
Diffstat (limited to 'framework')
| -rw-r--r-- | framework/api/current.txt | 23 | ||||
| -rw-r--r-- | framework/java/android/bluetooth/BluetoothAdapter.java | 81 | ||||
| -rw-r--r-- | framework/java/android/bluetooth/BluetoothDevice.java | 54 | ||||
| -rw-r--r-- | framework/java/android/bluetooth/BluetoothSocket.java | 30 | ||||
| -rw-r--r-- | framework/java/android/bluetooth/BluetoothSocketSettings.java | 349 |
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); + } + } +} |