diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2022-01-28 01:44:00 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-01-28 01:44:00 +0000 |
commit | a4195e47e41972a8308a66abb8b9995daf6edbcc (patch) | |
tree | fed65680473b8e007e981805b18ea0ea073e02e7 /framework/java | |
parent | 6ca88d122bd794a1e432f2dc1ae4b785dd4eaa63 (diff) | |
parent | d6f8dbbcfe6fe54c90d9deabe60b1644698f9cd0 (diff) |
Merge "Introduces mechanism for background rfcomm servers" am: 0662f5b93c am: d6f8dbbcfe
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/1885648
Change-Id: Iee0e7a37f422586e200c151655c4c3a552afdada
Diffstat (limited to 'framework/java')
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; |