summaryrefslogtreecommitdiff
path: root/framework/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'framework/java/android')
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java155
-rw-r--r--framework/java/android/bluetooth/BluetoothSocket.java32
-rw-r--r--framework/java/android/bluetooth/BluetoothStatusCodes.java51
3 files changed, 238 insertions, 0 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index f420b94db7..ff22c7614a 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -29,6 +29,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice.Transport;
import android.bluetooth.BluetoothProfile.ConnectionPolicy;
import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
@@ -234,6 +235,31 @@ public final class BluetoothAdapter {
UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
/**
+ * Used as an optional extra field for the {@link PendingIntent} provided to {@link
+ * #startRfcommServer(String, UUID, PendingIntent)}. This is useful for when an
+ * application registers multiple RFCOMM listeners, and needs a way to determine which service
+ * record the incoming {@link BluetoothSocket} is using.
+ *
+ * @hide
+ */
+ public static final String EXTRA_RFCOMM_LISTENER_ID =
+ "android.bluetooth.adapter.extra.RFCOMM_LISTENER_ID";
+
+ /** @hide */
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_TIMEOUT,
+ BluetoothStatusCodes.RFCOMM_LISTENER_START_FAILED_UUID_IN_USE,
+ BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_NO_MATCHING_SERVICE_RECORD,
+ BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP,
+ BluetoothStatusCodes.RFCOMM_LISTENER_FAILED_TO_CREATE_SERVER_SOCKET,
+ BluetoothStatusCodes.RFCOMM_LISTENER_FAILED_TO_CLOSE_SERVER_SOCKET,
+ BluetoothStatusCodes.RFCOMM_LISTENER_NO_SOCKET_AVAILABLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RfcommListenerResult {}
+
+ /**
* Human-readable string helper for AdapterState
*
* @hide
@@ -2845,6 +2871,135 @@ public final class BluetoothAdapter {
}
/**
+ * Requests the framework to start an RFCOMM socket server which listens based on the provided
+ * {@code name} and {@code uuid}.
+ * <p>
+ * Incoming connections will cause the system to start the component described in the {@link
+ * PendingIntent}, {@code pendingIntent}. After the component is started, it should obtain a
+ * {@link BluetoothAdapter} and retrieve the {@link BluetoothSocket} via {@link
+ * #retrieveConnectedRfcommSocket(UUID)}.
+ * <p>
+ * An application may register multiple RFCOMM listeners. It is recommended to set the extra
+ * field {@link #EXTRA_RFCOMM_LISTENER_ID} to help determine which service record the incoming
+ * {@link BluetoothSocket} is using.
+ * <p>
+ * The provided {@link PendingIntent} must be created with the {@link
+ * PendingIntent#FLAG_IMMUTABLE} flag.
+ *
+ * @param name service name for SDP record
+ * @param uuid uuid for SDP record
+ * @param pendingIntent component which is called when a new RFCOMM connection is available
+ * @return a status code from {@link BluetoothStatusCodes}
+ * @throws IllegalArgumentException if {@code pendingIntent} is not created with the {@link
+ * PendingIntent#FLAG_IMMUTABLE} flag.
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ @RfcommListenerResult
+ public int startRfcommServer(@NonNull String name, @NonNull UUID uuid,
+ @NonNull PendingIntent pendingIntent) {
+ if (!pendingIntent.isImmutable()) {
+ throw new IllegalArgumentException("The provided PendingIntent is not immutable");
+ }
+ try {
+ return mService.startRfcommListener(
+ name, new ParcelUuid(uuid), pendingIntent, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to transact RFCOMM listener start request", e);
+ return BluetoothStatusCodes.ERROR_TIMEOUT;
+ }
+ }
+
+ /**
+ * Closes the RFCOMM socket server listening on the given SDP record name and UUID. This can be
+ * called by applications after calling {@link #startRfcommServer(String, UUID,
+ * PendingIntent)} to stop listening for incoming RFCOMM connections.
+ *
+ * @param uuid uuid for SDP record
+ * @return a status code from {@link BluetoothStatusCodes}
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ @RfcommListenerResult
+ public int closeRfcommServer(@NonNull UUID uuid) {
+ try {
+ return mService.stopRfcommListener(new ParcelUuid(uuid), mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to transact RFCOMM listener stop request", e);
+ return BluetoothStatusCodes.ERROR_TIMEOUT;
+ }
+ }
+
+ /**
+ * Retrieves a connected {@link BluetoothSocket} for the given service record from a RFCOMM
+ * listener which was registered with {@link #startRfcommServer(String, UUID, PendingIntent)}.
+ * <p>
+ * This method should be called by the component started by the {@link PendingIntent} which was
+ * registered during the call to {@link #startRfcommServer(String, UUID, PendingIntent)} in
+ * order to retrieve the socket.
+ *
+ * @param uuid the same UUID used to register the listener previously
+ * @return a connected {@link BluetoothSocket} or {@code null} if no socket is available
+ * @throws IllegalStateException if the socket could not be retrieved because the application is
+ * trying to obtain a socket for a listener it did not register (incorrect {@code
+ * uuid}).
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull BluetoothSocket retrieveConnectedRfcommSocket(@NonNull UUID uuid) {
+ IncomingRfcommSocketInfo socketInfo;
+
+ try {
+ socketInfo =
+ mService.retrievePendingSocketForServiceRecord(
+ new ParcelUuid(uuid), mAttributionSource);
+ } catch (RemoteException e) {
+ return null;
+ }
+
+ switch (socketInfo.status) {
+ case BluetoothStatusCodes.SUCCESS:
+ try {
+ return BluetoothSocket.createSocketFromOpenFd(
+ socketInfo.pfd,
+ socketInfo.bluetoothDevice,
+ new ParcelUuid(uuid));
+ } catch (IOException e) {
+ return null;
+ }
+ case BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP:
+ throw new IllegalStateException(
+ String.format(
+ "RFCOMM listener for UUID %s was not registered by this app",
+ uuid));
+ case BluetoothStatusCodes.RFCOMM_LISTENER_NO_SOCKET_AVAILABLE:
+ return null;
+ default:
+ Log.e(TAG,
+ String.format(
+ "Unexpected result: (%d), from the adapter service while retrieving"
+ + " an rfcomm socket",
+ socketInfo.status));
+ return null;
+ }
+ }
+
+ /**
* Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
* <p>The link key is not required to be authenticated, i.e the communication may be
* vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices,
diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java
index 9d58447607..032f9df4f7 100644
--- a/framework/java/android/bluetooth/BluetoothSocket.java
+++ b/framework/java/android/bluetooth/BluetoothSocket.java
@@ -236,6 +236,33 @@ public final class BluetoothSocket implements Closeable {
mOutputStream = new BluetoothOutputStream(this);
}
+ /**
+ * Creates a BluetoothSocket from a {@link ParcelFileDescriptor}. This is used for when the
+ * underlying mPfd is transferred to a separate process (e.g. over a binder), and the socket
+ * must be reconstructed.
+ * <p>
+ * The socket should already be connected in this case, so {@link #connect()} should not be
+ * called.
+ *
+ * @param pfd is the {@link ParcelFileDescriptor} for an already connected BluetoothSocket
+ * @param device is the remote {@link BluetoothDevice} that this socket is connected to
+ * @param uuid is the service ID that this RFCOMM connection is using
+ * @throws IOException if socket creation fails.
+ */
+ /*package*/ static BluetoothSocket createSocketFromOpenFd(
+ ParcelFileDescriptor pfd, BluetoothDevice device, ParcelUuid uuid) throws IOException {
+ BluetoothSocket bluetoothSocket =
+ new BluetoothSocket(TYPE_RFCOMM, pfd.getFd(), true, true, device, -1, uuid);
+
+ bluetoothSocket.mPfd = pfd;
+ bluetoothSocket.mSocket = new LocalSocket(pfd.getFileDescriptor());
+ bluetoothSocket.mSocketIS = bluetoothSocket.mSocket.getInputStream();
+ bluetoothSocket.mSocketOS = bluetoothSocket.mSocket.getOutputStream();
+ bluetoothSocket.mSocketState = SocketState.CONNECTED;
+
+ return bluetoothSocket;
+ }
+
private BluetoothSocket(BluetoothSocket s) {
if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType);
mUuid = s.mUuid;
@@ -718,6 +745,11 @@ public final class BluetoothSocket implements Closeable {
}
}
+ /** @hide */
+ public ParcelFileDescriptor getParcelFileDescriptor() {
+ return mPfd;
+ }
+
private String convertAddr(final byte[] addr) {
return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java
index c6f6cde44e..0425220c8d 100644
--- a/framework/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java
@@ -310,6 +310,57 @@ public final class BluetoothStatusCodes {
public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115;
/**
+ * Indicates that the RFCOMM listener could not be started due to the requested UUID already
+ * being in use.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RFCOMM_LISTENER_START_FAILED_UUID_IN_USE = 2000;
+
+ /**
+ * Indicates that the operation could not be competed because the service record on which the
+ * operation was requested on does not exist.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RFCOMM_LISTENER_OPERATION_FAILED_NO_MATCHING_SERVICE_RECORD = 2001;
+
+ /**
+ * Indicates that the operation could not be completed because the application requesting the
+ * operation on the RFCOMM listener was not the one which registered it.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP = 2002;
+
+ /**
+ * Indicates that the creation of the underlying BluetoothServerSocket failed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RFCOMM_LISTENER_FAILED_TO_CREATE_SERVER_SOCKET = 2003;
+
+ /**
+ * Indicates that closing the underlying BluetoothServerSocket failed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RFCOMM_LISTENER_FAILED_TO_CLOSE_SERVER_SOCKET = 2004;
+
+ /**
+ * Indicates that there is no socket available to retrieve from the given listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RFCOMM_LISTENER_NO_SOCKET_AVAILABLE = 2005;
+
+ /**
* Indicates that an unknown error has occurred has occurred.
*/
public static final int ERROR_UNKNOWN = Integer.MAX_VALUE;