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

Commit 460965c2 authored by Nick Pelly's avatar Nick Pelly Committed by Android Git Automerger
Browse files

am 24bb9b8a: Provide an API for apps to use a dynamic RFCOMM channel and SDP record.

Merge commit '24bb9b8a' into eclair-mr2

* commit '24bb9b8a':
  Provide an API for apps to use a dynamic RFCOMM channel and SDP record.
parents 0daf3e91 24bb9b8a
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -25618,7 +25618,7 @@
 visibility="public"
>
</method>
<method name="listenUsingRfcommOn"
<method name="listenUsingRfcomm"
 return="android.bluetooth.BluetoothServerSocket"
 abstract="false"
 native="false"
@@ -25628,7 +25628,9 @@
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="channel" type="int">
<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="uuid" type="android.os.ParcelUuid">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
+141 −17
Original line number Diff line number Diff line
@@ -18,14 +18,19 @@ package android.bluetooth;

import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;

import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Random;
import java.util.Set;

/**
 * Represents the local Bluetooth adapter.
@@ -40,6 +45,7 @@ import java.util.HashSet;
 */
public final class BluetoothAdapter {
    private static final String TAG = "BluetoothAdapter";
    private static final boolean DBG = true;  //STOPSHIP: Remove excess logging

    /**
     * Sentinel error value for this class. Guaranteed to not equal any other
@@ -557,30 +563,139 @@ public final class BluetoothAdapter {
        return null;
    }

    /**
     * Randomly picks RFCOMM channels until none are left.
     * Avoids reserved channels.
     */
    private static class RfcommChannelPicker {
        private static final int[] RESERVED_RFCOMM_CHANNELS =  new int[] {
            10,  // HFAG
            11,  // HSAG
            12,  // OPUSH
            19,  // PBAP
        };
        private static LinkedList<Integer> sChannels;  // master list of non-reserved channels
        private static Random sRandom;

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

        public RfcommChannelPicker() {
            synchronized (RfcommChannelPicker.class) {
                if (sChannels == null) {
                    // lazy initialization of non-reserved rfcomm channels
                    sChannels = new LinkedList<Integer>();
                    for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) {
                        sChannels.addLast(new Integer(i));
                    }
                    for (int reserved : RESERVED_RFCOMM_CHANNELS) {
                        sChannels.remove(new Integer(reserved));
                    }
                    sRandom = new Random();
                }
                mChannels = (LinkedList<Integer>)sChannels.clone();
            }
        }
        /* Returns next random channel, or -1 if we're out */
        public int nextChannel() {
            if (mChannels.size() == 0) {
                return -1;
            }
            return mChannels.remove(sRandom.nextInt(mChannels.size()));
        }
    }

    /**
     * Create a listening, secure RFCOMM Bluetooth socket.
     * <p>A remote device connecting to this socket will be authenticated and
     * communication on this socket will be encrypted.
     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
     * connections to listening {@link BluetoothServerSocket}.
     * connections from a listening {@link BluetoothServerSocket}.
     * <p>Valid RFCOMM channels are in range 1 to 30.
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
     * @param channel RFCOMM channel to listen on
     * @return a listening RFCOMM BluetoothServerSocket
     * @throws IOException on error, for example Bluetooth not available, or
     *                     insufficient permissions, or channel in use.
     * @hide
     */
    public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
        BluetoothServerSocket socket = new BluetoothServerSocket(
                BluetoothSocket.TYPE_RFCOMM, true, true, channel);
        int errno = socket.mSocket.bindListen();
        if (errno != 0) {
            try {
            socket.mSocket.bindListen();
        } catch (IOException e) {
                socket.close();
            } catch (IOException e) {}
            socket.mSocket.throwErrnoNative(errno);
        }
        return socket;
    }

    /**
     * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
     * <p>A remote device connecting to this socket will be authenticated and
     * communication on this socket will be encrypted.
     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
     * connections from a listening {@link BluetoothServerSocket}.
     * <p>The system will assign an unused RFCOMM channel to listen on.
     * <p>The system will also register a Service Discovery
     * Protocol (SDP) record with the local SDP server containing the specified
     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
     * 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
     * closed, or if this application closes unexpectedly.
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
     * @param name service name for SDP record
     * @param uuid uuid for SDP record
     * @return a listening RFCOMM BluetoothServerSocket
     * @throws IOException on error, for example Bluetooth not available, or
     *                     insufficient permissions, or channel in use.
     */
    public BluetoothServerSocket listenUsingRfcomm(String name, ParcelUuid uuid)
            throws IOException {
        RfcommChannelPicker picker = new RfcommChannelPicker();

        BluetoothServerSocket socket;
        int channel;
        int errno;
        while (true) {
            channel = picker.nextChannel();

            if (channel == -1) {
                throw new IOException("No available channels");
            }

            socket = new BluetoothServerSocket(
                    BluetoothSocket.TYPE_RFCOMM, true, true, channel);
            errno = socket.mSocket.bindListen();
            if (errno == 0) {
                if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel);
                break;  // success
            } else if (errno == BluetoothSocket.EADDRINUSE) {
                if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use");
                try {
                    socket.close();
                } catch (IOException e) {}
                continue;  // try another channel
            } else {
                try {
                    socket.close();
            } catch (IOException e2) { }
            throw e;
                } catch (IOException e) {}
                socket.mSocket.throwErrnoNative(errno);  // Exception as a result of bindListen()
            }
        }

        int handle = -1;
        try {
            handle = mService.addRfcommServiceRecord(name, uuid, channel, new Binder());
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        if (handle == -1) {
            try {
                socket.close();
            } catch (IOException e) {}
            throw new IOException("Not able to register SDP record for " + name);
        }
        socket.setCloseHandler(mHandler, handle);
        return socket;
    }

@@ -595,13 +710,12 @@ public final class BluetoothAdapter {
    public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
        BluetoothServerSocket socket = new BluetoothServerSocket(
                BluetoothSocket.TYPE_RFCOMM, false, false, port);
        try {
            socket.mSocket.bindListen();
        } catch (IOException e) {
        int errno = socket.mSocket.bindListen();
        if (errno != 0) {
            try {
                socket.close();
            } catch (IOException e2) { }
            throw e;
            } catch (IOException e) {}
            socket.mSocket.throwErrnoNative(errno);
        }
        return socket;
    }
@@ -617,13 +731,12 @@ public final class BluetoothAdapter {
    public static BluetoothServerSocket listenUsingScoOn() throws IOException {
        BluetoothServerSocket socket = new BluetoothServerSocket(
                BluetoothSocket.TYPE_SCO, false, false, -1);
        try {
            socket.mSocket.bindListen();
        } catch (IOException e) {
        int errno = socket.mSocket.bindListen();
        if (errno != 0) {
            try {
                socket.close();
            } catch (IOException e2) { }
            throw e;
            } catch (IOException e) {}
            socket.mSocket.throwErrnoNative(errno);
        }
        return socket;
    }
@@ -636,6 +749,17 @@ public final class BluetoothAdapter {
        return Collections.unmodifiableSet(devices);
    }

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            /* handle socket closing */
            int handle = msg.what;
            try {
                if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle));
                mService.removeServiceRecord(handle);
            } catch (RemoteException e) {Log.e(TAG, "", e);}
        }
    };

    /**
     * Validate a Bluetooth address, such as "00:43:A8:23:10:F0"
     * <p>Alphabetic characters must be uppercase to be valid.
+14 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.bluetooth;

import android.os.Handler;

import java.io.Closeable;
import java.io.IOException;

@@ -52,6 +54,8 @@ import java.io.IOException;
public final class BluetoothServerSocket implements Closeable {

    /*package*/ final BluetoothSocket mSocket;
    private Handler mHandler;
    private int mMessage;

    /**
     * Construct a socket for incoming connections.
@@ -101,6 +105,16 @@ public final class BluetoothServerSocket implements Closeable {
     * throw an IOException.
     */
    public void close() throws IOException {
        synchronized (this) {
            if (mHandler != null) {
                mHandler.obtainMessage(mMessage).sendToTarget();
            }
        }
        mSocket.close();
    }

    /*package*/ synchronized void setCloseHandler(Handler handler, int message) {
        mHandler = handler;
        mMessage = message;
    }
}
+24 −4
Original line number Diff line number Diff line
@@ -54,11 +54,17 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 * {@link android.Manifest.permission#BLUETOOTH}
 */
public final class BluetoothSocket implements Closeable {
    /** @hide */
    public static final int MAX_RFCOMM_CHANNEL = 30;

    /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
    /*package*/ static final int TYPE_RFCOMM = 1;
    /*package*/ static final int TYPE_SCO = 2;
    /*package*/ static final int TYPE_L2CAP = 3;

    /*package*/ static final int EBADFD = 77;
    /*package*/ static final int EADDRINUSE = 98;

    private final int mType;  /* one of TYPE_RFCOMM etc */
    private final int mPort;  /* RFCOMM channel or L2CAP psm */
    private final BluetoothDevice mDevice;    /* remote device */
@@ -90,6 +96,11 @@ public final class BluetoothSocket implements Closeable {
     */
    /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
            BluetoothDevice device, int port) throws IOException {
        if (type == BluetoothSocket.TYPE_RFCOMM) {
            if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
                throw new IOException("Invalid RFCOMM channel: " + port);
            }
        }
        mType = type;
        mAuth = auth;
        mEncrypt = encrypt;
@@ -211,11 +222,15 @@ public final class BluetoothSocket implements Closeable {
        return mOutputStream;
    }

    /*package*/ void bindListen() throws IOException {
    /**
     * Currently returns unix errno instead of throwing IOException,
     * so that BluetoothAdapter can check the error code for EADDRINUSE
     */
    /*package*/ int bindListen() {
        mLock.readLock().lock();
        try {
            if (mClosed) throw new IOException("socket closed");
            bindListenNative();
            if (mClosed) return EBADFD;
            return bindListenNative();
        } finally {
            mLock.readLock().unlock();
        }
@@ -264,11 +279,16 @@ public final class BluetoothSocket implements Closeable {
    private native void initSocketNative() throws IOException;
    private native void initSocketFromFdNative(int fd) throws IOException;
    private native void connectNative() throws IOException;
    private native void bindListenNative() throws IOException;
    private native int bindListenNative();
    private native BluetoothSocket acceptNative(int timeout) throws IOException;
    private native int availableNative() throws IOException;
    private native int readNative(byte[] b, int offset, int length) throws IOException;
    private native int writeNative(byte[] b, int offset, int length) throws IOException;
    private native void abortNative() throws IOException;
    private native void destroyNative() throws IOException;
    /**
     * Throws an IOException for given posix errno. Done natively so we can
     * use strerr to convert to string error.
     */
    /*package*/ native void throwErrnoNative(int errno) throws IOException;
}
+4 −0
Original line number Diff line number Diff line
@@ -50,6 +50,10 @@ public final class BluetoothUuid {
    public static final ParcelUuid ObexObjectPush =
            ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");

    public static final ParcelUuid[] RESERVED_UUIDS = {
        AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
        ObexObjectPush};

    public static boolean isAudioSource(ParcelUuid uuid) {
        return uuid.equals(AudioSource);
    }
Loading