Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d6f8dbbc authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Introduces mechanism for background rfcomm servers" am: 0662f5b9

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/1885648

Change-Id: I4073bde2f8845d616f5a4a0fa9173db484f5ba92
parents cf7c2303 0662f5b9
Loading
Loading
Loading
Loading
+252 −0
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.BufferConstraints;
@@ -57,6 +59,7 @@ import android.bluetooth.IBluetoothConnectionCallback;
import android.bluetooth.IBluetoothMetadataListener;
import android.bluetooth.IBluetoothOobDataCallback;
import android.bluetooth.IBluetoothSocketManager;
import android.bluetooth.IncomingRfcommSocketInfo;
import android.bluetooth.OobData;
import android.bluetooth.UidTraffic;
import android.companion.CompanionDeviceManager;
@@ -92,6 +95,7 @@ import android.sysprop.BluetoothProperties;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;

import com.android.bluetooth.BluetoothMetricsProto;
@@ -137,13 +141,19 @@ import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
import java.util.regex.Pattern;

@@ -154,6 +164,7 @@ public class AdapterService extends Service {
    private static final int MIN_ADVT_INSTANCES_FOR_MA = 5;
    private static final int MIN_OFFLOADED_FILTERS = 10;
    private static final int MIN_OFFLOADED_SCAN_STORAGE_BYTES = 1024;
    private static final Duration PENDING_SOCKET_HANDOFF_TIMEOUT = Duration.ofMinutes(1);

    private final Object mEnergyInfoLock = new Object();
    private int mStackReportedState;
@@ -250,6 +261,7 @@ public class AdapterService extends Service {
        }
    }

    private BluetoothAdapter mAdapter;
    private AdapterProperties mAdapterProperties;
    private AdapterState mAdapterStateMachine;
    private BondStateMachine mBondStateMachine;
@@ -271,6 +283,10 @@ public class AdapterService extends Service {
    private boolean mQuietmode = false;
    private HashMap<String, CallerInfo> mBondAttemptCallerInfo = new HashMap<>();

    private final Map<UUID, RfcommListenerData> mBluetoothServerSockets =
            Collections.synchronizedMap(new HashMap<>());
    private final Executor mSocketServersExecutor = r -> new Thread(r).start();

    private AlarmManager mAlarmManager;
    private PendingIntent mPendingAlarm;
    private BatteryStatsManager mBatteryStatsManager;
@@ -460,6 +476,7 @@ public class AdapterService extends Service {
        mRemoteDevices.init();
        clearDiscoveringPackages();
        mBinder = new AdapterServiceBinder(this);
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mAdapterProperties = new AdapterProperties(this);
        mAdapterStateMachine = AdapterState.make(this);
        mJniCallbacks = new JniCallbacks(this, mAdapterProperties);
@@ -855,6 +872,8 @@ public class AdapterService extends Service {

        unregisterReceiver(mAlarmBroadcastReceiver);

        stopRfcommServerSockets();

        if (mPendingAlarm != null) {
            mAlarmManager.cancel(mPendingAlarm);
            mPendingAlarm = null;
@@ -1256,6 +1275,219 @@ public class AdapterService extends Service {
        mLeAudioService = LeAudioService.getLeAudioService();
    }

    @BluetoothAdapter.RfcommListenerResult
    private int startRfcommListener(
            String name,
            ParcelUuid uuid,
            PendingIntent pendingIntent,
            AttributionSource attributionSource) {
        if (mBluetoothServerSockets.containsKey(uuid.getUuid())) {
            Slog.d(TAG,
                    String.format(
                            "Cannot start RFCOMM listener: UUID %s already in use.",
                            uuid.getUuid()));
            return BluetoothStatusCodes.RFCOMM_LISTENER_START_FAILED_UUID_IN_USE;
        }

        try {
            startRfcommListenerInternal(name, uuid.getUuid(), pendingIntent, attributionSource);
        } catch (IOException e) {
            return BluetoothStatusCodes.RFCOMM_LISTENER_FAILED_TO_CREATE_SERVER_SOCKET;
        }

        return BluetoothStatusCodes.SUCCESS;
    }

    @BluetoothAdapter.RfcommListenerResult
    private int stopRfcommListener(ParcelUuid uuid, AttributionSource attributionSource) {
        RfcommListenerData listenerData = mBluetoothServerSockets.get(uuid.getUuid());

        if (listenerData == null) {
            Slog.d(TAG,
                    String.format(
                            "Cannot stop RFCOMM listener: UUID %s is not registered.",
                            uuid.getUuid()));
            return BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_NO_MATCHING_SERVICE_RECORD;
        }

        if (attributionSource.getUid() != listenerData.mAttributionSource.getUid()) {
            return BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP;
        }

        // Remove the entry so that it does not try and restart the server socket.
        mBluetoothServerSockets.remove(uuid.getUuid());

        return listenerData.closeServerAndPendingSockets(mHandler);
    }

    private IncomingRfcommSocketInfo retrievePendingSocketForServiceRecord(
            ParcelUuid uuid, AttributionSource attributionSource) {
        IncomingRfcommSocketInfo socketInfo = new IncomingRfcommSocketInfo();

        RfcommListenerData listenerData = mBluetoothServerSockets.get(uuid.getUuid());

        if (listenerData == null) {
            socketInfo.status =
                    BluetoothStatusCodes
                            .RFCOMM_LISTENER_OPERATION_FAILED_NO_MATCHING_SERVICE_RECORD;
            return socketInfo;
        }

        if (attributionSource.getUid() != listenerData.mAttributionSource.getUid()) {
            socketInfo.status = BluetoothStatusCodes.RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP;
            return socketInfo;
        }

        BluetoothSocket socket = listenerData.mPendingSockets.poll();

        if (socket == null) {
            socketInfo.status = BluetoothStatusCodes.RFCOMM_LISTENER_NO_SOCKET_AVAILABLE;
            return socketInfo;
        }

        mHandler.removeCallbacksAndEqualMessages(socket);

        socketInfo.bluetoothDevice = socket.getRemoteDevice();
        socketInfo.pfd = socket.getParcelFileDescriptor();
        socketInfo.status = BluetoothStatusCodes.SUCCESS;

        return socketInfo;
    }

    private void handleIncomingRfcommConnections(UUID uuid) {
        RfcommListenerData listenerData = mBluetoothServerSockets.get(uuid);
        for (;;) {
            BluetoothSocket socket;
            try {
                socket = listenerData.mServerSocket.accept();
            } catch (IOException e) {
                if (mBluetoothServerSockets.containsKey(uuid)) {
                    // The uuid still being in the map indicates that the accept failure is
                    // unexpected. Try and restart the listener.
                    Slog.e(TAG, "Failed to accept socket on " + listenerData.mServerSocket, e);
                    restartRfcommListener(listenerData, uuid);
                }
                return;
            }

            listenerData.mPendingSockets.add(socket);
            try {
                listenerData.mPendingIntent.send();
            } catch (PendingIntent.CanceledException e) {
                Slog.e(TAG, "PendingIntent for RFCOMM socket notifications cancelled.", e);
                // The pending intent was cancelled, close the server as there is no longer any way
                // to notify the app that registered the listener.
                listenerData.closeServerAndPendingSockets(mHandler);
                mBluetoothServerSockets.remove(uuid);
                return;
            }
            mHandler.postDelayed(
                    () -> pendingSocketTimeoutRunnable(listenerData, socket),
                    socket,
                    PENDING_SOCKET_HANDOFF_TIMEOUT.toMillis());
        }
    }

    // Tries to restart the rfcomm listener for the given UUID
    private void restartRfcommListener(RfcommListenerData listenerData, UUID uuid) {
        listenerData.closeServerAndPendingSockets(mHandler);
        try {
            startRfcommListenerInternal(
                    listenerData.mName,
                    uuid,
                    listenerData.mPendingIntent,
                    listenerData.mAttributionSource);
        } catch (IOException e) {
            Slog.e(TAG, "Failed to recreate rfcomm server socket", e);

            mBluetoothServerSockets.remove(uuid);
        }
    }

    private void pendingSocketTimeoutRunnable(
            RfcommListenerData listenerData, BluetoothSocket socket) {
        boolean socketFound = listenerData.mPendingSockets.remove(socket);
        if (socketFound) {
            try {
                socket.close();
            } catch (IOException e) {
                Slog.e(TAG, "Failed to close bt socket", e);
                // We don't care if closing the socket failed, just continue on.
            }
        }
    }

    private void startRfcommListenerInternal(
            String name, UUID uuid, PendingIntent intent, AttributionSource attributionSource)
            throws IOException {
        BluetoothServerSocket bluetoothServerSocket =
                mAdapter.listenUsingRfcommWithServiceRecord(name, uuid);

        RfcommListenerData listenerData =
                new RfcommListenerData(bluetoothServerSocket, name, intent, attributionSource);

        mBluetoothServerSockets.put(uuid, listenerData);

        mSocketServersExecutor.execute(() -> handleIncomingRfcommConnections(uuid));
    }

    private void stopRfcommServerSockets() {
        synchronized (mBluetoothServerSockets) {
            mBluetoothServerSockets.forEach((key, value) -> {
                mBluetoothServerSockets.remove(key);
                value.closeServerAndPendingSockets(mHandler);
            });
        }
    }

    private static class RfcommListenerData {
        final BluetoothServerSocket mServerSocket;
        // Service record name
        final String mName;
        // The Intent which contains the Service info to which the incoming socket connections are
        // handed off to.
        final PendingIntent mPendingIntent;
        // AttributionSource for the requester of the RFCOMM listener
        final AttributionSource mAttributionSource;
        // Contains the connected sockets which are pending transfer to the app which requested the
        // listener.
        final ConcurrentLinkedQueue<BluetoothSocket> mPendingSockets =
                new ConcurrentLinkedQueue<>();

        RfcommListenerData(
                BluetoothServerSocket serverSocket,
                String name,
                PendingIntent pendingIntent,
                AttributionSource attributionSource) {
            mServerSocket = serverSocket;
            mName = name;
            mPendingIntent = pendingIntent;
            mAttributionSource = attributionSource;
        }

        int closeServerAndPendingSockets(Handler handler) {
            int result = BluetoothStatusCodes.SUCCESS;
            try {
                mServerSocket.close();
            } catch (IOException e) {
                Slog.e(TAG, "Failed to call close on rfcomm server socket", e);
                result = BluetoothStatusCodes.RFCOMM_LISTENER_FAILED_TO_CLOSE_SERVER_SOCKET;
            }
            mPendingSockets.forEach(
                    pendingSocket -> {
                        handler.removeCallbacksAndEqualMessages(pendingSocket);
                        try {
                            pendingSocket.close();
                        } catch (IOException e) {
                            Slog.e(TAG, "Failed to close socket", e);
                        }
                    });
            mPendingSockets.clear();

            return result;
        }
    }

    private boolean isAvailable() {
        return !mCleaningUp;
    }
@@ -2674,6 +2906,26 @@ public class AdapterService extends Service {
            enforceBluetoothPrivilegedPermission(service);
            return service.allowLowLatencyAudio(allowed, device);
        }

        @Override
        public int startRfcommListener(
                String name,
                ParcelUuid uuid,
                PendingIntent pendingIntent,
                AttributionSource attributionSource) {
            return mService.startRfcommListener(name, uuid, pendingIntent, attributionSource);
        }

        @Override
        public int stopRfcommListener(ParcelUuid uuid, AttributionSource attributionSource) {
            return mService.stopRfcommListener(uuid, attributionSource);
        }

        @Override
        public IncomingRfcommSocketInfo retrievePendingSocketForServiceRecord(
                ParcelUuid uuid, AttributionSource attributionSource) {
            return mService.retrievePendingSocketForServiceRecord(uuid, attributionSource);
        }
    }

    // ----API Methods--------
+9 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ package android.bluetooth {

  public final class BluetoothAdapter {
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int closeRfcommServer(@NonNull java.util.UUID);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean disable(boolean);
    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disableBLE();
    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableBLE();
@@ -56,7 +57,9 @@ package android.bluetooth {
    method public boolean isLeEnabled();
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean removeActiveDevice(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothSocket retrieveConnectedRfcommSocket(@NonNull java.util.UUID);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int startRfcommServer(@NonNull String, @NonNull java.util.UUID, @NonNull android.app.PendingIntent);
    field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
    field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
    field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
@@ -260,6 +263,12 @@ package android.bluetooth {
    field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
    field public static final int ERROR_TIMEOUT = 15; // 0xf
    field public static final int NOT_ALLOWED = 401; // 0x191
    field public static final int RFCOMM_LISTENER_FAILED_TO_CLOSE_SERVER_SOCKET = 2004; // 0x7d4
    field public static final int RFCOMM_LISTENER_FAILED_TO_CREATE_SERVER_SOCKET = 2003; // 0x7d3
    field public static final int RFCOMM_LISTENER_NO_SOCKET_AVAILABLE = 2005; // 0x7d5
    field public static final int RFCOMM_LISTENER_OPERATION_FAILED_DIFFERENT_APP = 2002; // 0x7d2
    field public static final int RFCOMM_LISTENER_OPERATION_FAILED_NO_MATCHING_SERVICE_RECORD = 2001; // 0x7d1
    field public static final int RFCOMM_LISTENER_START_FAILED_UUID_IN_USE = 2000; // 0x7d0
  }

  public final class BluetoothUuid {
+155 −0
Original line number Diff line number Diff line
@@ -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;
@@ -233,6 +234,31 @@ public final class BluetoothAdapter {
    public static final UUID LE_PSM_CHARACTERISTIC_UUID =
            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
     *
@@ -2844,6 +2870,135 @@ public final class BluetoothAdapter {
        return createNewRfcommSocketAndRecord(name, uuid, true, true);
    }

    /**
     * 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
+32 −0

File changed.

Preview size limit exceeded, changes collapsed.

+51 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading