am dc2ccb82: Merge change Ia4879943 into eclair
Merge commit 'dc2ccb82f96b6a7be7a7b262b81d3aa1422f46fa' into eclair-mr2 * commit 'dc2ccb82f96b6a7be7a7b262b81d3aa1422f46fa': Encourage developers to connect RFCOMM by UUID instead of Channel.
This commit is contained in:
@@ -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 \
|
||||||
|
|||||||
@@ -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,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 {
|
||||||
|
|||||||
@@ -316,11 +316,28 @@ 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() {
|
||||||
|
synchronized (BluetoothDevice.class) {
|
||||||
|
if (sService == null) {
|
||||||
|
IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE);
|
||||||
|
if (b == null) {
|
||||||
|
throw new RuntimeException("Bluetooth service not available");
|
||||||
|
}
|
||||||
|
sService = IBluetooth.Stub.asInterface(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new BluetoothDevice
|
* Create a new BluetoothDevice
|
||||||
* Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
|
* Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
|
||||||
@@ -331,16 +348,7 @@ public final class BluetoothDevice implements Parcelable {
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
/*package*/ BluetoothDevice(String address) {
|
/*package*/ BluetoothDevice(String address) {
|
||||||
synchronized (BluetoothDevice.class) {
|
getService(); // ensures sService is initialized
|
||||||
if (sService == null) {
|
|
||||||
IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE);
|
|
||||||
if (b == null) {
|
|
||||||
throw new RuntimeException("Bluetooth service not available");
|
|
||||||
}
|
|
||||||
sService = IBluetooth.Stub.asInterface(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package android.bluetooth;
|
package android.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.IBluetoothCallback;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,13 +42,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|||||||
* 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
|
||||||
@@ -54,6 +58,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|||||||
* {@link android.Manifest.permission#BLUETOOTH}
|
* {@link android.Manifest.permission#BLUETOOTH}
|
||||||
*/
|
*/
|
||||||
public final class BluetoothSocket implements Closeable {
|
public final class BluetoothSocket implements Closeable {
|
||||||
|
private static final String TAG = "BluetoothSocket";
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int MAX_RFCOMM_CHANNEL = 30;
|
public static final int MAX_RFCOMM_CHANNEL = 30;
|
||||||
|
|
||||||
@@ -66,13 +72,15 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
/*package*/ static final int EADDRINUSE = 98;
|
/*package*/ static final int EADDRINUSE = 98;
|
||||||
|
|
||||||
private final int mType; /* one of TYPE_RFCOMM etc */
|
private final int mType; /* one of TYPE_RFCOMM etc */
|
||||||
private final int mPort; /* RFCOMM channel or L2CAP psm */
|
|
||||||
private final BluetoothDevice mDevice; /* remote device */
|
private final BluetoothDevice mDevice; /* remote device */
|
||||||
private final String mAddress; /* remote address */
|
private final String mAddress; /* remote address */
|
||||||
private final boolean mAuth;
|
private final boolean mAuth;
|
||||||
private final boolean mEncrypt;
|
private final boolean mEncrypt;
|
||||||
private final BluetoothInputStream mInputStream;
|
private final BluetoothInputStream mInputStream;
|
||||||
private final BluetoothOutputStream mOutputStream;
|
private final BluetoothOutputStream mOutputStream;
|
||||||
|
private final SdpHelper mSdp;
|
||||||
|
|
||||||
|
private int mPort; /* RFCOMM channel or L2CAP psm */
|
||||||
|
|
||||||
/** prevents all native calls after destroyNative() */
|
/** prevents all native calls after destroyNative() */
|
||||||
private boolean mClosed;
|
private boolean mClosed;
|
||||||
@@ -91,16 +99,24 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
* @param encrypt require the connection to be encrypted
|
* @param encrypt require the connection to be encrypted
|
||||||
* @param device remote device that this socket can connect to
|
* @param device remote device that this socket can connect to
|
||||||
* @param port remote port
|
* @param port remote port
|
||||||
|
* @param uuid SDP uuid
|
||||||
* @throws IOException On error, for example Bluetooth not available, or
|
* @throws IOException On error, for example Bluetooth not available, or
|
||||||
* insufficient priveleges
|
* insufficient priveleges
|
||||||
*/
|
*/
|
||||||
/*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
|
/*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
|
||||||
BluetoothDevice device, int port) throws IOException {
|
BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
|
||||||
if (type == BluetoothSocket.TYPE_RFCOMM) {
|
if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) {
|
||||||
if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
|
if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
|
||||||
throw new IOException("Invalid RFCOMM channel: " + port);
|
throw new IOException("Invalid RFCOMM channel: " + port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (uuid == null) {
|
||||||
|
mPort = port;
|
||||||
|
mSdp = null;
|
||||||
|
} else {
|
||||||
|
mSdp = new SdpHelper(device, uuid);
|
||||||
|
mPort = -1;
|
||||||
|
}
|
||||||
mType = type;
|
mType = type;
|
||||||
mAuth = auth;
|
mAuth = auth;
|
||||||
mEncrypt = encrypt;
|
mEncrypt = encrypt;
|
||||||
@@ -110,7 +126,6 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
} else {
|
} else {
|
||||||
mAddress = device.getAddress();
|
mAddress = device.getAddress();
|
||||||
}
|
}
|
||||||
mPort = port;
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
initSocketNative();
|
initSocketNative();
|
||||||
} else {
|
} else {
|
||||||
@@ -123,7 +138,7 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a BluetoothSocket from address.
|
* Construct a BluetoothSocket from address. Used by native code.
|
||||||
* @param type type of socket
|
* @param type type of socket
|
||||||
* @param fd fd to use for connected socket, or -1 for a new socket
|
* @param fd fd to use for connected socket, or -1 for a new socket
|
||||||
* @param auth require the remote device to be authenticated
|
* @param auth require the remote device to be authenticated
|
||||||
@@ -135,7 +150,7 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
*/
|
*/
|
||||||
private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
|
private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
|
||||||
int port) throws IOException {
|
int port) throws IOException {
|
||||||
this(type, fd, auth, encrypt, new BluetoothDevice(address), port);
|
this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -160,7 +175,12 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
mLock.readLock().lock();
|
mLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if (mClosed) throw new IOException("socket closed");
|
if (mClosed) throw new IOException("socket closed");
|
||||||
connectNative();
|
|
||||||
|
if (mSdp != null) {
|
||||||
|
mPort = mSdp.doSdp(); // blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
connectNative(); // blocks
|
||||||
} finally {
|
} finally {
|
||||||
mLock.readLock().unlock();
|
mLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
@@ -176,12 +196,15 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
mLock.readLock().lock();
|
mLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if (mClosed) return;
|
if (mClosed) return;
|
||||||
|
if (mSdp != null) {
|
||||||
|
mSdp.cancel();
|
||||||
|
}
|
||||||
abortNative();
|
abortNative();
|
||||||
} finally {
|
} finally {
|
||||||
mLock.readLock().unlock();
|
mLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// all native calls are guarenteed to immediately return after
|
// all native calls are guaranteed to immediately return after
|
||||||
// abortNative(), so this lock should immediatley acquire
|
// abortNative(), so this lock should immediatley acquire
|
||||||
mLock.writeLock().lock();
|
mLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
@@ -291,4 +314,62 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
* use strerr to convert to string error.
|
* use strerr to convert to string error.
|
||||||
*/
|
*/
|
||||||
/*package*/ native void throwErrnoNative(int errno) throws IOException;
|
/*package*/ native void throwErrnoNative(int errno) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to perform blocking SDP lookup.
|
||||||
|
*/
|
||||||
|
private static class SdpHelper extends IBluetoothCallback.Stub {
|
||||||
|
private final IBluetooth service;
|
||||||
|
private final ParcelUuid uuid;
|
||||||
|
private final BluetoothDevice device;
|
||||||
|
private int channel;
|
||||||
|
private boolean canceled;
|
||||||
|
public SdpHelper(BluetoothDevice device, ParcelUuid uuid) {
|
||||||
|
service = BluetoothDevice.getService();
|
||||||
|
this.device = device;
|
||||||
|
this.uuid = uuid;
|
||||||
|
canceled = false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the RFCOMM channel for the UUID, or throws IOException
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
public synchronized int doSdp() throws IOException {
|
||||||
|
if (canceled) throw new IOException("Service discovery canceled");
|
||||||
|
channel = -1;
|
||||||
|
|
||||||
|
boolean inProgress = false;
|
||||||
|
try {
|
||||||
|
inProgress = service.fetchRemoteUuids(device.getAddress(), uuid, this);
|
||||||
|
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||||
|
|
||||||
|
if (!inProgress) throw new IOException("Unable to start Service Discovery");
|
||||||
|
|
||||||
|
try {
|
||||||
|
/* 12 second timeout as a precaution - onRfcommChannelFound
|
||||||
|
* should always occur before the timeout */
|
||||||
|
wait(12000); // block
|
||||||
|
|
||||||
|
} catch (InterruptedException e) {}
|
||||||
|
|
||||||
|
if (canceled) throw new IOException("Service discovery canceled");
|
||||||
|
if (channel < 1) throw new IOException("Service discovery failed");
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
/** Object cannot be re-used after calling cancel() */
|
||||||
|
public synchronized void cancel() {
|
||||||
|
if (!canceled) {
|
||||||
|
canceled = true;
|
||||||
|
channel = -1;
|
||||||
|
notifyAll(); // unblock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public synchronized void onRfcommChannelFound(int channel) {
|
||||||
|
if (!canceled) {
|
||||||
|
this.channel = channel;
|
||||||
|
notifyAll(); // unblock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.bluetooth;
|
package android.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.IBluetoothCallback;
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,8 +54,8 @@ interface IBluetooth
|
|||||||
String getRemoteName(in String address);
|
String getRemoteName(in String address);
|
||||||
int getRemoteClass(in String address);
|
int getRemoteClass(in String address);
|
||||||
ParcelUuid[] getRemoteUuids(in String address);
|
ParcelUuid[] getRemoteUuids(in String address);
|
||||||
boolean fetchRemoteUuidsWithSdp(in String address);
|
boolean fetchRemoteUuids(in String address, in ParcelUuid uuid, in IBluetoothCallback callback);
|
||||||
int getRemoteServiceChannel(in String address,in ParcelUuid uuid);
|
int getRemoteServiceChannel(in String address, in ParcelUuid uuid);
|
||||||
|
|
||||||
boolean setPin(in String address, in byte[] pin);
|
boolean setPin(in String address, in byte[] pin);
|
||||||
boolean setPasskey(in String address, int passkey);
|
boolean setPasskey(in String address, int passkey);
|
||||||
|
|||||||
27
core/java/android/bluetooth/IBluetoothCallback.aidl
Normal file
27
core/java/android/bluetooth/IBluetoothCallback.aidl
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.bluetooth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System private API for Bluetooth service callbacks.
|
||||||
|
*
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
interface IBluetoothCallback
|
||||||
|
{
|
||||||
|
void onRfcommChannelFound(int channel);
|
||||||
|
}
|
||||||
@@ -55,6 +55,10 @@ class BluetoothEventLoop {
|
|||||||
private static final int EVENT_RESTART_BLUETOOTH = 2;
|
private static final int EVENT_RESTART_BLUETOOTH = 2;
|
||||||
private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 3;
|
private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 3;
|
||||||
|
|
||||||
|
private static final int CREATE_DEVICE_ALREADY_EXISTS = 1;
|
||||||
|
private static final int CREATE_DEVICE_SUCCESS = 0;
|
||||||
|
private static final int CREATE_DEVICE_FAILED = -1;
|
||||||
|
|
||||||
// The time (in millisecs) to delay the pairing attempt after the first
|
// The time (in millisecs) to delay the pairing attempt after the first
|
||||||
// auto pairing attempt fails. We use an exponential delay with
|
// auto pairing attempt fails. We use an exponential delay with
|
||||||
// INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
|
// INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
|
||||||
@@ -550,14 +554,27 @@ class BluetoothEventLoop {
|
|||||||
mBluetoothService.updateRemoteDevicePropertiesCache(address);
|
mBluetoothService.updateRemoteDevicePropertiesCache(address);
|
||||||
}
|
}
|
||||||
mBluetoothService.sendUuidIntent(address);
|
mBluetoothService.sendUuidIntent(address);
|
||||||
|
mBluetoothService.makeServiceChannelCallbacks(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCreateDeviceResult(String address, boolean result) {
|
private void onCreateDeviceResult(String address, int result) {
|
||||||
if (DBG) {
|
if (DBG) log("Result of onCreateDeviceResult:" + result);
|
||||||
log("Result of onCreateDeviceResult:" + result);
|
|
||||||
}
|
switch (result) {
|
||||||
if (!result) {
|
case CREATE_DEVICE_ALREADY_EXISTS:
|
||||||
|
String path = mBluetoothService.getObjectPathFromAddress(address);
|
||||||
|
if (path != null) {
|
||||||
|
mBluetoothService.discoverServicesNative(path, "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Log.w(TAG, "Device exists, but we dont have the bluez path, failing");
|
||||||
|
// fall-through
|
||||||
|
case CREATE_DEVICE_FAILED:
|
||||||
mBluetoothService.sendUuidIntent(address);
|
mBluetoothService.sendUuidIntent(address);
|
||||||
|
mBluetoothService.makeServiceChannelCallbacks(address);
|
||||||
|
break;
|
||||||
|
case CREATE_DEVICE_SUCCESS:
|
||||||
|
// nothing to do, UUID intent's will be sent via property changed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.bluetooth.BluetoothHeadset;
|
|||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
import android.bluetooth.BluetoothUuid;
|
import android.bluetooth.BluetoothUuid;
|
||||||
import android.bluetooth.IBluetooth;
|
import android.bluetooth.IBluetooth;
|
||||||
|
import android.bluetooth.IBluetoothCallback;
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@@ -55,6 +56,7 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class BluetoothService extends IBluetooth.Stub {
|
public class BluetoothService extends IBluetooth.Stub {
|
||||||
@@ -86,14 +88,39 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
// This timeout should be greater than the page timeout
|
// This timeout should be greater than the page timeout
|
||||||
private static final int UUID_INTENT_DELAY = 6000;
|
private static final int UUID_INTENT_DELAY = 6000;
|
||||||
|
|
||||||
private final Map<String, String> mAdapterProperties;
|
/** Always retrieve RFCOMM channel for these SDP UUIDs */
|
||||||
private final HashMap <String, Map<String, String>> mDeviceProperties;
|
private static final ParcelUuid[] RFCOMM_UUIDS = {
|
||||||
|
BluetoothUuid.Handsfree,
|
||||||
|
BluetoothUuid.HSP,
|
||||||
|
BluetoothUuid.ObexObjectPush };
|
||||||
|
|
||||||
private final HashMap <String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
|
|
||||||
private final ArrayList <String> mUuidIntentTracker;
|
private final Map<String, String> mAdapterProperties;
|
||||||
|
private final HashMap<String, Map<String, String>> mDeviceProperties;
|
||||||
|
|
||||||
|
private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
|
||||||
|
private final ArrayList<String> mUuidIntentTracker;
|
||||||
|
private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
|
||||||
|
|
||||||
private final HashMap<Integer, Integer> mServiceRecordToPid;
|
private final HashMap<Integer, Integer> mServiceRecordToPid;
|
||||||
|
|
||||||
|
private static class RemoteService {
|
||||||
|
public String address;
|
||||||
|
public ParcelUuid uuid;
|
||||||
|
public RemoteService(String address, ParcelUuid uuid) {
|
||||||
|
this.address = address;
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o instanceof RemoteService) {
|
||||||
|
RemoteService service = (RemoteService)o;
|
||||||
|
return address.equals(service.address) && uuid.equals(service.uuid);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
classInitNative();
|
classInitNative();
|
||||||
}
|
}
|
||||||
@@ -121,6 +148,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
|
|
||||||
mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
|
mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
|
||||||
mUuidIntentTracker = new ArrayList<String>();
|
mUuidIntentTracker = new ArrayList<String>();
|
||||||
|
mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
|
||||||
mServiceRecordToPid = new HashMap<Integer, Integer>();
|
mServiceRecordToPid = new HashMap<Integer, Integer>();
|
||||||
registerForAirplaneMode();
|
registerForAirplaneMode();
|
||||||
}
|
}
|
||||||
@@ -312,8 +340,10 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
break;
|
break;
|
||||||
case MESSAGE_UUID_INTENT:
|
case MESSAGE_UUID_INTENT:
|
||||||
String address = (String)msg.obj;
|
String address = (String)msg.obj;
|
||||||
if (address != null)
|
if (address != null) {
|
||||||
sendUuidIntent(address);
|
sendUuidIntent(address);
|
||||||
|
makeServiceChannelCallbacks(address);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MESSAGE_DISCOVERABLE_TIMEOUT:
|
case MESSAGE_DISCOVERABLE_TIMEOUT:
|
||||||
int mode = msg.arg1;
|
int mode = msg.arg1;
|
||||||
@@ -1064,14 +1094,35 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
return uuids;
|
return uuids;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean fetchRemoteUuidsWithSdp(String address) {
|
/**
|
||||||
|
* Connect and fetch new UUID's using SDP.
|
||||||
|
* The UUID's found are broadcast as intents.
|
||||||
|
* Optionally takes a uuid and callback to fetch the RFCOMM channel for the
|
||||||
|
* a given uuid.
|
||||||
|
* TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
|
||||||
|
* TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
|
||||||
|
* callback and broadcast intents.
|
||||||
|
*/
|
||||||
|
public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
|
||||||
|
IBluetoothCallback callback) {
|
||||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
|
||||||
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoteService service = new RemoteService(address, uuid);
|
||||||
|
if (uuid != null && mUuidCallbackTracker.get(service) != null) {
|
||||||
|
// An SDP query for this address & uuid is already in progress
|
||||||
|
// Do not add this callback for the uuid
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (mUuidIntentTracker.contains(address)) {
|
if (mUuidIntentTracker.contains(address)) {
|
||||||
// An SDP query for this address is already in progress
|
// An SDP query for this address is already in progress
|
||||||
|
// Add this uuid onto the in-progress SDP query
|
||||||
|
if (uuid != null) {
|
||||||
|
mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1087,6 +1138,9 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mUuidIntentTracker.add(address);
|
mUuidIntentTracker.add(address);
|
||||||
|
if (uuid != null) {
|
||||||
|
mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
|
||||||
|
}
|
||||||
|
|
||||||
Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
|
Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
|
||||||
message.obj = address;
|
message.obj = address;
|
||||||
@@ -1096,6 +1150,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the rfcomm channel associated with the UUID.
|
* Gets the rfcomm channel associated with the UUID.
|
||||||
|
* Pulls records from the cache only.
|
||||||
*
|
*
|
||||||
* @param address Address of the remote device
|
* @param address Address of the remote device
|
||||||
* @param uuid ParcelUuid of the service attribute
|
* @param uuid ParcelUuid of the service attribute
|
||||||
@@ -1201,20 +1256,67 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
// We are storing the rfcomm channel numbers only for the uuids
|
// We are storing the rfcomm channel numbers only for the uuids
|
||||||
// we are interested in.
|
// we are interested in.
|
||||||
int channel;
|
int channel;
|
||||||
ParcelUuid[] interestedUuids = {BluetoothUuid.Handsfree,
|
if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
|
||||||
BluetoothUuid.HSP,
|
|
||||||
BluetoothUuid.ObexObjectPush};
|
ArrayList<ParcelUuid> applicationUuids = new ArrayList();
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
for (RemoteService service : mUuidCallbackTracker.keySet()) {
|
||||||
|
if (service.address.equals(address)) {
|
||||||
|
applicationUuids.add(service.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
|
Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
|
||||||
for (ParcelUuid uuid: interestedUuids) {
|
|
||||||
|
// Retrieve RFCOMM channel for default uuids
|
||||||
|
for (ParcelUuid uuid : RFCOMM_UUIDS) {
|
||||||
if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
|
if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
|
||||||
channel =
|
channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
|
||||||
getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(),
|
uuid.toString(), 0x0004);
|
||||||
0x0004);
|
if (DBG) log("\tuuid(system): " + uuid + " " + channel);
|
||||||
value.put(uuid, channel);
|
value.put(uuid, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mDeviceServiceChannelCache.put(address, value);
|
// Retrieve RFCOMM channel for application requested uuids
|
||||||
|
for (ParcelUuid uuid : applicationUuids) {
|
||||||
|
if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
|
||||||
|
channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
|
||||||
|
uuid.toString(), 0x0004);
|
||||||
|
if (DBG) log("\tuuid(application): " + uuid + " " + channel);
|
||||||
|
value.put(uuid, channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
// Make application callbacks
|
||||||
|
for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
|
||||||
|
iter.hasNext();) {
|
||||||
|
RemoteService service = iter.next();
|
||||||
|
if (service.address.equals(address)) {
|
||||||
|
channel = -1;
|
||||||
|
if (value.get(service.uuid) != null) {
|
||||||
|
channel = value.get(service.uuid);
|
||||||
|
}
|
||||||
|
if (channel != -1) {
|
||||||
|
if (DBG) log("Making callback for " + service.uuid + " with result " +
|
||||||
|
channel);
|
||||||
|
IBluetoothCallback callback = mUuidCallbackTracker.get(service);
|
||||||
|
if (callback != null) {
|
||||||
|
try {
|
||||||
|
callback.onRfcommChannelFound(channel);
|
||||||
|
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||||
|
}
|
||||||
|
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
mDeviceServiceChannelCache.put(address, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1330,6 +1432,26 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
|
|
||||||
if (mUuidIntentTracker.contains(address))
|
if (mUuidIntentTracker.contains(address))
|
||||||
mUuidIntentTracker.remove(address);
|
mUuidIntentTracker.remove(address);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ synchronized void makeServiceChannelCallbacks(String address) {
|
||||||
|
for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
|
||||||
|
iter.hasNext();) {
|
||||||
|
RemoteService service = iter.next();
|
||||||
|
if (service.address.equals(address)) {
|
||||||
|
if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
|
||||||
|
" " + service.uuid);
|
||||||
|
IBluetoothCallback callback = mUuidCallbackTracker.get(service);
|
||||||
|
if (callback != null) {
|
||||||
|
try {
|
||||||
|
callback.onRfcommChannelFound(-1);
|
||||||
|
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||||
|
}
|
||||||
|
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1377,6 +1499,11 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (RemoteService service : mUuidCallbackTracker.keySet()) {
|
||||||
|
if (service.address.equals(address)) {
|
||||||
|
pw.println("\tPENDING CALLBACK: " + service.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String value = getProperty("Devices");
|
String value = getProperty("Devices");
|
||||||
@@ -1508,7 +1635,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
|
private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
|
||||||
int value);
|
int value);
|
||||||
private native boolean createDeviceNative(String address);
|
private native boolean createDeviceNative(String address);
|
||||||
private native boolean discoverServicesNative(String objectPath, String pattern);
|
/*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
|
||||||
|
|
||||||
private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
|
private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
|
||||||
short channel);
|
short channel);
|
||||||
|
|||||||
@@ -36,6 +36,10 @@
|
|||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
|
#define CREATE_DEVICE_ALREADY_EXISTS 1
|
||||||
|
#define CREATE_DEVICE_SUCCESS 0
|
||||||
|
#define CREATE_DEVICE_FAILED -1
|
||||||
|
|
||||||
#ifdef HAVE_BLUETOOTH
|
#ifdef HAVE_BLUETOOTH
|
||||||
static jfieldID field_mNativeData;
|
static jfieldID field_mNativeData;
|
||||||
|
|
||||||
@@ -95,7 +99,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
|||||||
method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
|
method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
|
||||||
"(Ljava/lang/String;I)V");
|
"(Ljava/lang/String;I)V");
|
||||||
method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult",
|
method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult",
|
||||||
"(Ljava/lang/String;Z)V");
|
"(Ljava/lang/String;I)V");
|
||||||
method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult",
|
method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult",
|
||||||
"(Ljava/lang/String;Z)V");
|
"(Ljava/lang/String;Z)V");
|
||||||
|
|
||||||
@@ -1115,10 +1119,13 @@ void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) {
|
|||||||
|
|
||||||
LOGV("... Address = %s", address);
|
LOGV("... Address = %s", address);
|
||||||
|
|
||||||
bool result = JNI_TRUE;
|
jint result = CREATE_DEVICE_SUCCESS;
|
||||||
if (dbus_set_error_from_message(&err, msg)) {
|
if (dbus_set_error_from_message(&err, msg)) {
|
||||||
|
if (dbus_error_has_name(&err, "org.bluez.Error.AlreadyExists")) {
|
||||||
|
result = CREATE_DEVICE_ALREADY_EXISTS;
|
||||||
|
}
|
||||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||||
result = JNI_FALSE;
|
result = CREATE_DEVICE_FAILED;
|
||||||
}
|
}
|
||||||
env->CallVoidMethod(nat->me,
|
env->CallVoidMethod(nat->me,
|
||||||
method_onCreateDeviceResult,
|
method_onCreateDeviceResult,
|
||||||
|
|||||||
Reference in New Issue
Block a user