From af3a017be782f21896c101264b31af55a5cf0314 Mon Sep 17 00:00:00 2001 From: Bhakthavatsala Raghavendra Date: Fri, 18 Oct 2024 21:33:33 +0000 Subject: Add socket settings interface Add socket interface which allows users to control the various security requirements for the sockets. This also helps in extending socket for offload socket related parameters Bug: 366639787 Bug: 374358112 Test: mmm packages/module/Bluetooth Change-Id: If09796368e76b8375b879b1e3cc7a1c365b67799 --- framework/api/current.txt | 23 ++ .../java/android/bluetooth/BluetoothAdapter.java | 82 ++++- .../java/android/bluetooth/BluetoothDevice.java | 54 ++++ .../java/android/bluetooth/BluetoothSocket.java | 30 +- .../android/bluetooth/BluetoothSocketSettings.java | 349 +++++++++++++++++++++ 5 files changed, 528 insertions(+), 10 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothSocketSettings.java (limited to 'framework') 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 3477bed73f..dfd170641e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -31,6 +31,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.BroadcastBehavior; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -4538,8 +4539,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 @@ -4594,8 +4594,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 @@ -4626,6 +4625,81 @@ public final class BluetoothAdapter { return socket; } + /** + * Creates a listening server channel for Bluetooth connections with the specified socket + * settings {@link BluetoothSocketSettings}. + * + *

Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening + * {@link BluetoothServerSocket}. + * + *

This API supports {@link BluetoothSocket#TYPE_RFCOMM} and {{@link BluetoothSocket#TYPE_LE} + * only, which can be set using {@link BluetoothSocketSettings#setSocketType()}. + *

  • For `BluetoothSocket.TYPE_RFCOMM`: The RFCOMM UUID must be provided using {@link + * BluetoothSocketSettings#setRfcommUuid()}. + *
  • 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. + * + *

    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 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 @@ -3217,6 +3217,60 @@ public final class BluetoothDevice implements Parcelable, Attributable { return new BluetoothSocket(this, BluetoothSocket.TYPE_L2CAP_LE, false, false, psm, null); } + /** + * 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}. + * + *

    + * + *

    Application using this API is responsible for obtaining protocol/service multiplexer (psm) + * value from remote device. + * + *

    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 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 * + *

    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 * + *

    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. + * + *

    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. + * + *

    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. + * + *

    Defaults to {@code null}. + */ + @Nullable + @RequiresNoPermission + public String getRfcommServiceName() { + return mRfcommServiceName; + } + + /** + * Returns the RFCOMM service UUID used for a BluetoothSocket#TYPE_RFCOMM socket. + * + *

    Defaults to {@code null}. + */ + @Nullable + @RequiresNoPermission + public UUID getRfcommUuid() { + return mRfcommUuid; + } + + /** + * Checks if encryption is enabled for the Bluetooth socket. + * + *

    Defaults to {@code false}. + */ + @RequiresNoPermission + public boolean isEncryptionRequired() { + return mEncryptionRequired; + } + + /** + * Checks if authentication is enabled for the Bluetooth socket. + * + *

    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. + * + *

    Must be one of: + * + *

    + * + *

    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. + * + *

    This is only used for {@link BluetoothSocket#TYPE_LE} sockets. + * + *

    Valid PSM values for {@link BluetoothSocket#TYPE_LE} sockets is ranging from 128 + * (0x80) to 255 (0xFF). + * + *

    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. + * + *

    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. + * + *

    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. + * + *

    This name is used to identify the service when a remote device searches for it using + * SDP. + * + *

    This is only used for {@link BluetoothSocket#TYPE_RFCOMM} sockets. + * + *

    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. + * + *

    This UUID is used to uniquely identify the service when a remote device searches for + * it using SDP. + * + *

    This is only used for {@link BluetoothSocket#TYPE_RFCOMM} sockets. + * + *

    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); + } + } +} -- cgit v1.2.3-59-g8ed1b