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

Commit 16fb88a6 authored by Nick Pelly's avatar Nick Pelly
Browse files

Encourage developers to connect RFCOMM by UUID instead of Channel.

Hide createRfcommSocket(int channel)
Add createRfcommSocketWithServiceRecord(UUID uuid)

Rename listenUsingRfcomm(String,UUID) -> listenUsingRfcommWithServiceRecord(..)

Now we have a complete API for developers to make peer-peer RFCOMM connections
with hard-coding the limited (30) RFCOMM channels, instead using SDP lookup
of an UUID.

This commit addresses two serious bugs:
- Do not throw IOException on accepting an incoming RFCOMM connection with
  BluetoothSocket. This was a regression from commit 24bb9b8a
- Workaround failure of bluez to update SDP cache when channel changes by
  trying to use the same RFCOMM channel on the server every time, instead
  of picking server channels randomly. This is a pretty ugly workaround,
  and we are still trying to fix the caching issue - but with this
  workaround we are at least shippable and apps will work at least until
  they start colliding on the 30 RFCOMM channels.

DrNo: eastham
Bug: 2158900
Joke: What did the digital watch say to his mom? "Look mom no hands."
Change-Id: Ia4879943b83afac06b6f1a3f2391cf1628afce7d
parent 64dd5be5
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -90,6 +90,7 @@ LOCAL_SRC_FILES += \
	core/java/android/backup/IRestoreSession.aidl \
	core/java/android/backup/IRestoreSession.aidl \
	core/java/android/bluetooth/IBluetooth.aidl \
	core/java/android/bluetooth/IBluetooth.aidl \
	core/java/android/bluetooth/IBluetoothA2dp.aidl \
	core/java/android/bluetooth/IBluetoothA2dp.aidl \
	core/java/android/bluetooth/IBluetoothCallback.aidl \
	core/java/android/bluetooth/IBluetoothHeadset.aidl \
	core/java/android/bluetooth/IBluetoothHeadset.aidl \
	core/java/android/bluetooth/IBluetoothPbap.aidl \
	core/java/android/bluetooth/IBluetoothPbap.aidl \
	core/java/android/content/IContentService.aidl \
	core/java/android/content/IContentService.aidl \
+4 −4
Original line number Original line Diff line number Diff line
@@ -25618,7 +25618,7 @@
 visibility="public"
 visibility="public"
>
>
</method>
</method>
<method name="listenUsingRfcomm"
<method name="listenUsingRfcommWithServiceRecord"
 return="android.bluetooth.BluetoothServerSocket"
 return="android.bluetooth.BluetoothServerSocket"
 abstract="false"
 abstract="false"
 native="false"
 native="false"
@@ -25630,7 +25630,7 @@
>
>
<parameter name="name" type="java.lang.String">
<parameter name="name" type="java.lang.String">
</parameter>
</parameter>
<parameter name="uuid" type="android.os.ParcelUuid">
<parameter name="uuid" type="java.util.UUID">
</parameter>
</parameter>
<exception name="IOException" type="java.io.IOException">
<exception name="IOException" type="java.io.IOException">
</exception>
</exception>
@@ -26804,7 +26804,7 @@
>
>
<implements name="android.os.Parcelable">
<implements name="android.os.Parcelable">
</implements>
</implements>
<method name="createRfcommSocket"
<method name="createRfcommSocketToServiceRecord"
 return="android.bluetooth.BluetoothSocket"
 return="android.bluetooth.BluetoothSocket"
 abstract="false"
 abstract="false"
 native="false"
 native="false"
@@ -26814,7 +26814,7 @@
 deprecated="not deprecated"
 deprecated="not deprecated"
 visibility="public"
 visibility="public"
>
>
<parameter name="channel" type="int">
<parameter name="uuid" type="java.util.UUID">
</parameter>
</parameter>
<exception name="IOException" type="java.io.IOException">
<exception name="IOException" type="java.io.IOException">
</exception>
</exception>
+31 −9
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.LinkedList;
import java.util.Random;
import java.util.Random;
import java.util.Set;
import java.util.Set;
import java.util.UUID;


/**
/**
 * Represents the local Bluetooth adapter.
 * Represents the local Bluetooth adapter.
@@ -564,8 +565,16 @@ public final class BluetoothAdapter {
    }
    }


    /**
    /**
     * Randomly picks RFCOMM channels until none are left.
     * Picks RFCOMM channels until none are left.
     * Avoids reserved channels.
     * Avoids reserved channels.
     * Ideally we would pick random channels, but in the current implementation
     * we start with the channel that is the hash of the UUID, and try every
     * available channel from there. This means that in most cases a given
     * uuid will use the same channel. This is a workaround for a Bluez SDP
     * bug where we are not updating the cache when the channel changes for a
     * uuid.
     * TODO: Fix the Bluez SDP caching bug, and go back to random channel
     * selection
     */
     */
    private static class RfcommChannelPicker {
    private static class RfcommChannelPicker {
        private static final int[] RESERVED_RFCOMM_CHANNELS =  new int[] {
        private static final int[] RESERVED_RFCOMM_CHANNELS =  new int[] {
@@ -579,7 +588,9 @@ public final class BluetoothAdapter {


        private final LinkedList<Integer> mChannels;  // local list of channels left to try
        private final LinkedList<Integer> mChannels;  // local list of channels left to try


        public RfcommChannelPicker() {
        private final UUID mUuid;

        public RfcommChannelPicker(UUID uuid) {
            synchronized (RfcommChannelPicker.class) {
            synchronized (RfcommChannelPicker.class) {
                if (sChannels == null) {
                if (sChannels == null) {
                    // lazy initialization of non-reserved rfcomm channels
                    // lazy initialization of non-reserved rfcomm channels
@@ -594,13 +605,21 @@ public final class BluetoothAdapter {
                }
                }
                mChannels = (LinkedList<Integer>)sChannels.clone();
                mChannels = (LinkedList<Integer>)sChannels.clone();
            }
            }
            mUuid = uuid;
        }
        }
        /* Returns next random channel, or -1 if we're out */
        /* Returns next channel, or -1 if we're out */
        public int nextChannel() {
        public int nextChannel() {
            if (mChannels.size() == 0) {
            int channel = mUuid.hashCode();  // always pick the same channel to try first
                return -1;
            Integer channelInt;
            while (mChannels.size() > 0) {
                channelInt = new Integer(channel);
                if (mChannels.remove(channelInt)) {
                    return channel;
                }
                channel = (channel % BluetoothSocket.MAX_RFCOMM_CHANNEL) + 1;
            }
            }
            return mChannels.remove(sRandom.nextInt(mChannels.size()));

            return -1;
        }
        }
    }
    }


@@ -644,6 +663,8 @@ public final class BluetoothAdapter {
     * can use the same UUID to query our SDP server and discover which channel
     * can use the same UUID to query our SDP server and discover which channel
     * to connect to. This SDP record will be removed when this socket is
     * to connect to. This SDP record will be removed when this socket is
     * closed, or if this application closes unexpectedly.
     * closed, or if this application closes unexpectedly.
     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
     * connect to this socket from another device using the same {@link UUID}.
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
     * @param name service name for SDP record
     * @param name service name for SDP record
     * @param uuid uuid for SDP record
     * @param uuid uuid for SDP record
@@ -651,9 +672,9 @@ public final class BluetoothAdapter {
     * @throws IOException on error, for example Bluetooth not available, or
     * @throws IOException on error, for example Bluetooth not available, or
     *                     insufficient permissions, or channel in use.
     *                     insufficient permissions, or channel in use.
     */
     */
    public BluetoothServerSocket listenUsingRfcomm(String name, ParcelUuid uuid)
    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
            throws IOException {
            throws IOException {
        RfcommChannelPicker picker = new RfcommChannelPicker();
        RfcommChannelPicker picker = new RfcommChannelPicker(uuid);


        BluetoothServerSocket socket;
        BluetoothServerSocket socket;
        int channel;
        int channel;
@@ -687,7 +708,8 @@ public final class BluetoothAdapter {


        int handle = -1;
        int handle = -1;
        try {
        try {
            handle = mService.addRfcommServiceRecord(name, uuid, channel, new Binder());
            handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel,
                    new Binder());
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        if (handle == -1) {
        if (handle == -1) {
            try {
            try {
+51 −17
Original line number Original line Diff line number Diff line
@@ -316,21 +316,16 @@ public final class BluetoothDevice implements Parcelable {
     */
     */
    public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
    public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";



    /**
    private static IBluetooth sService;  /* Guarenteed constant after first object constructed */
     * Lazy initialization. Guaranteed final after first object constructed, or
     * getService() called.
     * TODO: Unify implementation of sService amongst BluetoothFoo API's
     */
    private static IBluetooth sService;


    private final String mAddress;
    private final String mAddress;


    /**
    /*package*/ static IBluetooth getService() {
     * Create a new BluetoothDevice
     * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
     * and is validated in this constructor.
     * @param address valid Bluetooth MAC address
     * @throws RuntimeException Bluetooth is not available on this platform
     * @throws IllegalArgumentException address is invalid
     * @hide
     */
    /*package*/ BluetoothDevice(String address) {
        synchronized (BluetoothDevice.class) {
        synchronized (BluetoothDevice.class) {
            if (sService == null) {
            if (sService == null) {
                IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE);
                IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE);
@@ -340,7 +335,20 @@ public final class BluetoothDevice implements Parcelable {
                sService = IBluetooth.Stub.asInterface(b);
                sService = IBluetooth.Stub.asInterface(b);
            }
            }
        }
        }
        return sService;
    }


    /**
     * Create a new BluetoothDevice
     * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
     * and is validated in this constructor.
     * @param address valid Bluetooth MAC address
     * @throws RuntimeException Bluetooth is not available on this platform
     * @throws IllegalArgumentException address is invalid
     * @hide
     */
    /*package*/ BluetoothDevice(String address) {
        getService();  // ensures sService is initialized
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
            throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
            throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
        }
        }
@@ -551,7 +559,7 @@ public final class BluetoothDevice implements Parcelable {
      */
      */
     public boolean fetchUuidsWithSdp() {
     public boolean fetchUuidsWithSdp() {
        try {
        try {
            return sService.fetchRemoteUuidsWithSdp(mAddress);
            return sService.fetchRemoteUuids(mAddress, null, null);
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        return false;
        return false;
    }
    }
@@ -598,7 +606,7 @@ public final class BluetoothDevice implements Parcelable {


    /**
    /**
     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
     * outgoing connection to this remote device.
     * outgoing connection to this remote device on given channel.
     * <p>The remote device will be authenticated and communication on this
     * <p>The remote device will be authenticated and communication on this
     * socket will be encrypted.
     * socket will be encrypted.
     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
@@ -610,9 +618,34 @@ public final class BluetoothDevice implements Parcelable {
     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
     * @throws IOException on error, for example Bluetooth not available, or
     * @throws IOException on error, for example Bluetooth not available, or
     *                     insufficient permissions
     *                     insufficient permissions
     * @hide
     */
     */
    public BluetoothSocket createRfcommSocket(int channel) throws IOException {
    public BluetoothSocket createRfcommSocket(int channel) throws IOException {
        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel);
        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
                null);
    }

    /**
     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
     * outgoing connection to this remote device using SDP lookup of uuid.
     * <p>This is designed to be used with {@link
     * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
     * Bluetooth applications.
     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
     * connection. This will also perform an SDP lookup of the given uuid to
     * determine which channel to connect to.
     * <p>The remote device will be authenticated and communication on this
     * socket will be encrypted.
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
     *
     * @param uuid service record uuid to lookup RFCOMM channel
     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
     * @throws IOException on error, for example Bluetooth not available, or
     *                     insufficient permissions
     */
    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
                new ParcelUuid(uuid));
    }
    }


    /**
    /**
@@ -628,7 +661,8 @@ public final class BluetoothDevice implements Parcelable {
     * @hide
     * @hide
     */
     */
    public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
    public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port);
        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
                null);
    }
    }


    /**
    /**
@@ -640,7 +674,7 @@ public final class BluetoothDevice implements Parcelable {
     * @hide
     * @hide
     */
     */
    public BluetoothSocket createScoSocket() throws IOException {
    public BluetoothSocket createScoSocket() throws IOException {
        return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1);
        return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
    }
    }


    /**
    /**
+6 −6
Original line number Original line Diff line number Diff line
@@ -36,13 +36,13 @@ import java.io.IOException;
 * connection orientated, streaming transport over Bluetooth. It is also known
 * connection orientated, streaming transport over Bluetooth. It is also known
 * as the Serial Port Profile (SPP).
 * as the Serial Port Profile (SPP).
 *
 *
 * <p>Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link
 * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create
 * BluetoothSocket} ready for an outgoing connection to a remote
 * a new {@link BluetoothSocket} ready for an outgoing connection to a remote
 * {@link BluetoothDevice}.
 * {@link BluetoothDevice}.
 *
 *
 * <p>Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening
 * <p>Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to
 * {@link BluetoothServerSocket} ready for incoming connections to the local
 * create a listening {@link BluetoothServerSocket} ready for incoming
 * {@link BluetoothAdapter}.
 * connections to the local {@link BluetoothAdapter}.
 *
 *
 * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
 * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
 * safe. In particular, {@link #close} will always immediately abort ongoing
 * safe. In particular, {@link #close} will always immediately abort ongoing
@@ -68,7 +68,7 @@ public final class BluetoothServerSocket implements Closeable {
     */
     */
    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
            throws IOException {
            throws IOException {
        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port);
        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
    }
    }


    /**
    /**
Loading