Merge commit '460965c2d944d046d00a65dc8675802a10d8aba3' into eclair-mr2-plus-aosp * commit '460965c2d944d046d00a65dc8675802a10d8aba3': Provide an API for apps to use a dynamic RFCOMM channel and SDP record.
This commit is contained in:
@@ -25618,7 +25618,7 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</method>
|
</method>
|
||||||
<method name="listenUsingRfcommOn"
|
<method name="listenUsingRfcomm"
|
||||||
return="android.bluetooth.BluetoothServerSocket"
|
return="android.bluetooth.BluetoothServerSocket"
|
||||||
abstract="false"
|
abstract="false"
|
||||||
native="false"
|
native="false"
|
||||||
@@ -25628,7 +25628,9 @@
|
|||||||
deprecated="not deprecated"
|
deprecated="not deprecated"
|
||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
<parameter name="channel" type="int">
|
<parameter name="name" type="java.lang.String">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="uuid" type="android.os.ParcelUuid">
|
||||||
</parameter>
|
</parameter>
|
||||||
<exception name="IOException" type="java.io.IOException">
|
<exception name="IOException" type="java.io.IOException">
|
||||||
</exception>
|
</exception>
|
||||||
|
|||||||
@@ -18,14 +18,19 @@ package android.bluetooth;
|
|||||||
|
|
||||||
import android.annotation.SdkConstant;
|
import android.annotation.SdkConstant;
|
||||||
import android.annotation.SdkConstant.SdkConstantType;
|
import android.annotation.SdkConstant.SdkConstantType;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the local Bluetooth adapter.
|
* Represents the local Bluetooth adapter.
|
||||||
@@ -40,6 +45,7 @@ import java.util.HashSet;
|
|||||||
*/
|
*/
|
||||||
public final class BluetoothAdapter {
|
public final class BluetoothAdapter {
|
||||||
private static final String TAG = "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
|
* Sentinel error value for this class. Guaranteed to not equal any other
|
||||||
@@ -557,33 +563,142 @@ public final class BluetoothAdapter {
|
|||||||
return null;
|
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.
|
* Create a listening, secure RFCOMM Bluetooth socket.
|
||||||
* <p>A remote device connecting to this socket will be authenticated and
|
* <p>A remote device connecting to this socket will be authenticated and
|
||||||
* communication on this socket will be encrypted.
|
* communication on this socket will be encrypted.
|
||||||
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
|
* <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>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
|
* @param channel RFCOMM channel to listen on
|
||||||
* @return a listening RFCOMM BluetoothServerSocket
|
* @return a listening RFCOMM BluetoothServerSocket
|
||||||
* @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.
|
||||||
|
* @hide
|
||||||
*/
|
*/
|
||||||
public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
|
public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
|
||||||
BluetoothServerSocket socket = new BluetoothServerSocket(
|
BluetoothServerSocket socket = new BluetoothServerSocket(
|
||||||
BluetoothSocket.TYPE_RFCOMM, true, true, channel);
|
BluetoothSocket.TYPE_RFCOMM, true, true, channel);
|
||||||
try {
|
int errno = socket.mSocket.bindListen();
|
||||||
socket.mSocket.bindListen();
|
if (errno != 0) {
|
||||||
} catch (IOException e) {
|
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException e2) { }
|
} catch (IOException e) {}
|
||||||
throw e;
|
socket.mSocket.throwErrnoNative(errno);
|
||||||
}
|
}
|
||||||
return socket;
|
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 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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an unencrypted, unauthenticated, RFCOMM server socket.
|
* Construct an unencrypted, unauthenticated, RFCOMM server socket.
|
||||||
* Call #accept to retrieve connections to this socket.
|
* Call #accept to retrieve connections to this socket.
|
||||||
@@ -595,13 +710,12 @@ public final class BluetoothAdapter {
|
|||||||
public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
|
public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
|
||||||
BluetoothServerSocket socket = new BluetoothServerSocket(
|
BluetoothServerSocket socket = new BluetoothServerSocket(
|
||||||
BluetoothSocket.TYPE_RFCOMM, false, false, port);
|
BluetoothSocket.TYPE_RFCOMM, false, false, port);
|
||||||
try {
|
int errno = socket.mSocket.bindListen();
|
||||||
socket.mSocket.bindListen();
|
if (errno != 0) {
|
||||||
} catch (IOException e) {
|
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException e2) { }
|
} catch (IOException e) {}
|
||||||
throw e;
|
socket.mSocket.throwErrnoNative(errno);
|
||||||
}
|
}
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
@@ -617,13 +731,12 @@ public final class BluetoothAdapter {
|
|||||||
public static BluetoothServerSocket listenUsingScoOn() throws IOException {
|
public static BluetoothServerSocket listenUsingScoOn() throws IOException {
|
||||||
BluetoothServerSocket socket = new BluetoothServerSocket(
|
BluetoothServerSocket socket = new BluetoothServerSocket(
|
||||||
BluetoothSocket.TYPE_SCO, false, false, -1);
|
BluetoothSocket.TYPE_SCO, false, false, -1);
|
||||||
try {
|
int errno = socket.mSocket.bindListen();
|
||||||
socket.mSocket.bindListen();
|
if (errno != 0) {
|
||||||
} catch (IOException e) {
|
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException e2) { }
|
} catch (IOException e) {}
|
||||||
throw e;
|
socket.mSocket.throwErrnoNative(errno);
|
||||||
}
|
}
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
@@ -636,6 +749,17 @@ public final class BluetoothAdapter {
|
|||||||
return Collections.unmodifiableSet(devices);
|
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"
|
* Validate a Bluetooth address, such as "00:43:A8:23:10:F0"
|
||||||
* <p>Alphabetic characters must be uppercase to be valid.
|
* <p>Alphabetic characters must be uppercase to be valid.
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package android.bluetooth;
|
package android.bluetooth;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -52,6 +54,8 @@ import java.io.IOException;
|
|||||||
public final class BluetoothServerSocket implements Closeable {
|
public final class BluetoothServerSocket implements Closeable {
|
||||||
|
|
||||||
/*package*/ final BluetoothSocket mSocket;
|
/*package*/ final BluetoothSocket mSocket;
|
||||||
|
private Handler mHandler;
|
||||||
|
private int mMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a socket for incoming connections.
|
* Construct a socket for incoming connections.
|
||||||
@@ -101,6 +105,16 @@ public final class BluetoothServerSocket implements Closeable {
|
|||||||
* throw an IOException.
|
* throw an IOException.
|
||||||
*/
|
*/
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
synchronized (this) {
|
||||||
|
if (mHandler != null) {
|
||||||
|
mHandler.obtainMessage(mMessage).sendToTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
mSocket.close();
|
mSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ synchronized void setCloseHandler(Handler handler, int message) {
|
||||||
|
mHandler = handler;
|
||||||
|
mMessage = message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,11 +54,17 @@ 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 {
|
||||||
|
/** @hide */
|
||||||
|
public static final int MAX_RFCOMM_CHANNEL = 30;
|
||||||
|
|
||||||
/** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
|
/** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
|
||||||
/*package*/ static final int TYPE_RFCOMM = 1;
|
/*package*/ static final int TYPE_RFCOMM = 1;
|
||||||
/*package*/ static final int TYPE_SCO = 2;
|
/*package*/ static final int TYPE_SCO = 2;
|
||||||
/*package*/ static final int TYPE_L2CAP = 3;
|
/*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 mType; /* one of TYPE_RFCOMM etc */
|
||||||
private final int mPort; /* RFCOMM channel or L2CAP psm */
|
private final int mPort; /* RFCOMM channel or L2CAP psm */
|
||||||
private final BluetoothDevice mDevice; /* remote device */
|
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,
|
/*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
|
||||||
BluetoothDevice device, int port) throws IOException {
|
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;
|
mType = type;
|
||||||
mAuth = auth;
|
mAuth = auth;
|
||||||
mEncrypt = encrypt;
|
mEncrypt = encrypt;
|
||||||
@@ -211,11 +222,15 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
return mOutputStream;
|
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();
|
mLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if (mClosed) throw new IOException("socket closed");
|
if (mClosed) return EBADFD;
|
||||||
bindListenNative();
|
return bindListenNative();
|
||||||
} finally {
|
} finally {
|
||||||
mLock.readLock().unlock();
|
mLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
@@ -264,11 +279,16 @@ public final class BluetoothSocket implements Closeable {
|
|||||||
private native void initSocketNative() throws IOException;
|
private native void initSocketNative() throws IOException;
|
||||||
private native void initSocketFromFdNative(int fd) throws IOException;
|
private native void initSocketFromFdNative(int fd) throws IOException;
|
||||||
private native void connectNative() 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 BluetoothSocket acceptNative(int timeout) throws IOException;
|
||||||
private native int availableNative() throws IOException;
|
private native int availableNative() throws IOException;
|
||||||
private native int readNative(byte[] b, int offset, int length) 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 int writeNative(byte[] b, int offset, int length) throws IOException;
|
||||||
private native void abortNative() throws IOException;
|
private native void abortNative() throws IOException;
|
||||||
private native void destroyNative() 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ public final class BluetoothUuid {
|
|||||||
public static final ParcelUuid ObexObjectPush =
|
public static final ParcelUuid ObexObjectPush =
|
||||||
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
|
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) {
|
public static boolean isAudioSource(ParcelUuid uuid) {
|
||||||
return uuid.equals(AudioSource);
|
return uuid.equals(AudioSource);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,4 +63,7 @@ interface IBluetooth
|
|||||||
|
|
||||||
boolean setTrust(in String address, in boolean value);
|
boolean setTrust(in String address, in boolean value);
|
||||||
boolean getTrustState(in String address);
|
boolean getTrustState(in String address);
|
||||||
|
|
||||||
|
int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b);
|
||||||
|
void removeServiceRecord(int handle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.bluetooth.BluetoothAdapter;
|
|||||||
import android.bluetooth.BluetoothClass;
|
import android.bluetooth.BluetoothClass;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothHeadset;
|
import android.bluetooth.BluetoothHeadset;
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
import android.bluetooth.BluetoothUuid;
|
import android.bluetooth.BluetoothUuid;
|
||||||
import android.bluetooth.IBluetooth;
|
import android.bluetooth.IBluetooth;
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
@@ -37,6 +38,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@@ -90,6 +92,8 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
private final HashMap <String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
|
private final HashMap <String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
|
||||||
private final ArrayList <String> mUuidIntentTracker;
|
private final ArrayList <String> mUuidIntentTracker;
|
||||||
|
|
||||||
|
private final HashMap<Integer, Integer> mServiceRecordToPid;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
classInitNative();
|
classInitNative();
|
||||||
}
|
}
|
||||||
@@ -117,6 +121,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>();
|
||||||
|
mServiceRecordToPid = new HashMap<Integer, Integer>();
|
||||||
registerForAirplaneMode();
|
registerForAirplaneMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +211,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
|
|
||||||
mIsDiscovering = false;
|
mIsDiscovering = false;
|
||||||
mAdapterProperties.clear();
|
mAdapterProperties.clear();
|
||||||
|
mServiceRecordToPid.clear();
|
||||||
|
|
||||||
if (saveSetting) {
|
if (saveSetting) {
|
||||||
persistBluetoothOnSetting(false);
|
persistBluetoothOnSetting(false);
|
||||||
@@ -1211,6 +1217,71 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
mDeviceServiceChannelCache.put(address, value);
|
mDeviceServiceChannelCache.put(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b is a handle to a Binder instance, so that this service can be notified
|
||||||
|
* for Applications that terminate unexpectedly, to clean there service
|
||||||
|
* records
|
||||||
|
*/
|
||||||
|
public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
|
||||||
|
int channel, IBinder b) {
|
||||||
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
|
||||||
|
"Need BLUETOOTH permission");
|
||||||
|
if (serviceName == null || uuid == null || channel < 1 ||
|
||||||
|
channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
|
||||||
|
Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int handle = addRfcommServiceRecordNative(serviceName,
|
||||||
|
uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
|
||||||
|
(short)channel);
|
||||||
|
if (DBG) log("new handle " + Integer.toHexString(handle));
|
||||||
|
if (handle == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pid = Binder.getCallingPid();
|
||||||
|
mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
|
||||||
|
try {
|
||||||
|
b.linkToDeath(new Reaper(handle, pid), 0);
|
||||||
|
} catch (RemoteException e) {}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeServiceRecord(int handle) {
|
||||||
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
|
||||||
|
"Need BLUETOOTH permission");
|
||||||
|
checkAndRemoveRecord(handle, Binder.getCallingPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void checkAndRemoveRecord(int handle, int pid) {
|
||||||
|
Integer handleInt = new Integer(handle);
|
||||||
|
Integer owner = mServiceRecordToPid.get(handleInt);
|
||||||
|
if (owner != null && pid == owner.intValue()) {
|
||||||
|
if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
|
||||||
|
pid);
|
||||||
|
mServiceRecordToPid.remove(handleInt);
|
||||||
|
removeServiceRecordNative(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Reaper implements IBinder.DeathRecipient {
|
||||||
|
int pid;
|
||||||
|
int handle;
|
||||||
|
Reaper(int handle, int pid) {
|
||||||
|
this.pid = pid;
|
||||||
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
public void binderDied() {
|
||||||
|
synchronized (BluetoothService.this) {
|
||||||
|
if (DBG) log("Tracked app " + pid + " died");
|
||||||
|
checkAndRemoveRecord(handle, pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@@ -1263,25 +1334,25 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
|
|
||||||
|
|
||||||
switch(mBluetoothState) {
|
switch(mBluetoothState) {
|
||||||
case BluetoothAdapter.STATE_OFF:
|
case BluetoothAdapter.STATE_OFF:
|
||||||
pw.println("\nBluetooth OFF\n");
|
pw.println("Bluetooth OFF\n");
|
||||||
return;
|
return;
|
||||||
case BluetoothAdapter.STATE_TURNING_ON:
|
case BluetoothAdapter.STATE_TURNING_ON:
|
||||||
pw.println("\nBluetooth TURNING ON\n");
|
pw.println("Bluetooth TURNING ON\n");
|
||||||
return;
|
return;
|
||||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||||
pw.println("\nBluetooth TURNING OFF\n");
|
pw.println("Bluetooth TURNING OFF\n");
|
||||||
return;
|
return;
|
||||||
case BluetoothAdapter.STATE_ON:
|
case BluetoothAdapter.STATE_ON:
|
||||||
pw.println("\nBluetooth ON\n");
|
pw.println("Bluetooth ON\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pw.println("\nLocal address = " + getAddress());
|
pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
|
||||||
pw.println("\nLocal name = " + getName());
|
|
||||||
pw.println("\nisDiscovering() = " + isDiscovering());
|
pw.println("Local address = " + getAddress());
|
||||||
|
pw.println("Local name = " + getName());
|
||||||
|
pw.println("isDiscovering() = " + isDiscovering());
|
||||||
|
|
||||||
BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
|
BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
|
||||||
|
|
||||||
@@ -1292,13 +1363,17 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
toBondStateString(bondState),
|
toBondStateString(bondState),
|
||||||
mBondState.getAttempt(address),
|
mBondState.getAttempt(address),
|
||||||
getRemoteName(address));
|
getRemoteName(address));
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
|
||||||
ParcelUuid[] uuids = getRemoteUuids(address);
|
Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
|
||||||
if (uuids == null) {
|
if (uuidChannels == null) {
|
||||||
pw.printf("\tuuids = null\n");
|
pw.println("\tuuids = null");
|
||||||
} else {
|
} else {
|
||||||
for (ParcelUuid uuid : uuids) {
|
for (ParcelUuid uuid : uuidChannels.keySet()) {
|
||||||
pw.printf("\t" + uuid + "\n");
|
Integer channel = uuidChannels.get(uuid);
|
||||||
|
if (channel == null) {
|
||||||
|
pw.println("\t" + uuid);
|
||||||
|
} else {
|
||||||
|
pw.println("\t" + uuid + " RFCOMM channel = " + channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1310,8 +1385,10 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
devicesObjectPath = value.split(",");
|
devicesObjectPath = value.split(",");
|
||||||
}
|
}
|
||||||
pw.println("\n--ACL connected devices--");
|
pw.println("\n--ACL connected devices--");
|
||||||
for (String device : devicesObjectPath) {
|
if (devicesObjectPath != null) {
|
||||||
pw.println(getAddressFromObjectPath(device));
|
for (String device : devicesObjectPath) {
|
||||||
|
pw.println(getAddressFromObjectPath(device));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rather not do this from here, but no-where else and I need this
|
// Rather not do this from here, but no-where else and I need this
|
||||||
@@ -1331,10 +1408,15 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
pw.println("getState() = STATE_ERROR");
|
pw.println("getState() = STATE_ERROR");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset());
|
|
||||||
pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
|
|
||||||
|
|
||||||
|
pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
|
||||||
|
pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
|
||||||
headset.close();
|
headset.close();
|
||||||
|
pw.println("\n--Application Service Records--");
|
||||||
|
for (Integer handle : mServiceRecordToPid.keySet()) {
|
||||||
|
Integer pid = mServiceRecordToPid.get(handle);
|
||||||
|
pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
|
/* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
|
||||||
@@ -1423,8 +1505,12 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
private native boolean setPasskeyNative(String address, int passkey, int nativeData);
|
private native boolean setPasskeyNative(String address, int passkey, int nativeData);
|
||||||
private native boolean setPairingConfirmationNative(String address, boolean confirm,
|
private native boolean setPairingConfirmationNative(String address, boolean confirm,
|
||||||
int nativeData);
|
int nativeData);
|
||||||
private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value);
|
private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
|
||||||
|
int value);
|
||||||
private native boolean createDeviceNative(String address);
|
private native boolean createDeviceNative(String address);
|
||||||
private native boolean discoverServicesNative(String objectPath, String pattern);
|
private native boolean discoverServicesNative(String objectPath, String pattern);
|
||||||
|
|
||||||
|
private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
|
||||||
|
short channel);
|
||||||
|
private native boolean removeServiceRecordNative(int handle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,7 +237,8 @@ static void connectNative(JNIEnv *env, jobject obj) {
|
|||||||
jniThrowIOException(env, ENOSYS);
|
jniThrowIOException(env, ENOSYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bindListenNative(JNIEnv *env, jobject obj) {
|
/* Returns errno instead of throwing, so java can check errno */
|
||||||
|
static int bindListenNative(JNIEnv *env, jobject obj) {
|
||||||
#ifdef HAVE_BLUETOOTH
|
#ifdef HAVE_BLUETOOTH
|
||||||
LOGV(__FUNCTION__);
|
LOGV(__FUNCTION__);
|
||||||
|
|
||||||
@@ -248,7 +249,7 @@ static void bindListenNative(JNIEnv *env, jobject obj) {
|
|||||||
struct asocket *s = get_socketData(env, obj);
|
struct asocket *s = get_socketData(env, obj);
|
||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
return;
|
return EINVAL;
|
||||||
|
|
||||||
type = env->GetIntField(obj, field_mType);
|
type = env->GetIntField(obj, field_mType);
|
||||||
|
|
||||||
@@ -283,28 +284,25 @@ static void bindListenNative(JNIEnv *env, jobject obj) {
|
|||||||
memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
|
memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
jniThrowIOException(env, ENOSYS);
|
return ENOSYS;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bind(s->fd, addr, addr_sz)) {
|
if (bind(s->fd, addr, addr_sz)) {
|
||||||
LOGV("...bind(%d) gave errno %d", s->fd, errno);
|
LOGV("...bind(%d) gave errno %d", s->fd, errno);
|
||||||
jniThrowIOException(env, errno);
|
return errno;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen(s->fd, 1)) {
|
if (listen(s->fd, 1)) {
|
||||||
LOGV("...listen(%d) gave errno %d", s->fd, errno);
|
LOGV("...listen(%d) gave errno %d", s->fd, errno);
|
||||||
jniThrowIOException(env, errno);
|
return errno;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGV("...bindListenNative(%d) success", s->fd);
|
LOGV("...bindListenNative(%d) success", s->fd);
|
||||||
|
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
jniThrowIOException(env, ENOSYS);
|
return ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
|
static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
|
||||||
@@ -521,17 +519,22 @@ static void destroyNative(JNIEnv *env, jobject obj) {
|
|||||||
jniThrowIOException(env, ENOSYS);
|
jniThrowIOException(env, ENOSYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) {
|
||||||
|
jniThrowIOException(env, err);
|
||||||
|
}
|
||||||
|
|
||||||
static JNINativeMethod sMethods[] = {
|
static JNINativeMethod sMethods[] = {
|
||||||
{"initSocketNative", "()V", (void*) initSocketNative},
|
{"initSocketNative", "()V", (void*) initSocketNative},
|
||||||
{"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative},
|
{"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative},
|
||||||
{"connectNative", "()V", (void *) connectNative},
|
{"connectNative", "()V", (void *) connectNative},
|
||||||
{"bindListenNative", "()V", (void *) bindListenNative},
|
{"bindListenNative", "()I", (void *) bindListenNative},
|
||||||
{"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
|
{"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
|
||||||
{"availableNative", "()I", (void *) availableNative},
|
{"availableNative", "()I", (void *) availableNative},
|
||||||
{"readNative", "([BII)I", (void *) readNative},
|
{"readNative", "([BII)I", (void *) readNative},
|
||||||
{"writeNative", "([BII)I", (void *) writeNative},
|
{"writeNative", "([BII)I", (void *) writeNative},
|
||||||
{"abortNative", "()V", (void *) abortNative},
|
{"abortNative", "()V", (void *) abortNative},
|
||||||
{"destroyNative", "()V", (void *) destroyNative},
|
{"destroyNative", "()V", (void *) destroyNative},
|
||||||
|
{"throwErrnoNative", "(I)V", (void *) throwErrnoNative},
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
|
int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
|
||||||
|
|||||||
@@ -818,6 +818,48 @@ static jboolean discoverServicesNative(JNIEnv *env, jobject object,
|
|||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jint addRfcommServiceRecordNative(JNIEnv *env, jobject object,
|
||||||
|
jstring name, jlong uuidMsb, jlong uuidLsb, jshort channel) {
|
||||||
|
LOGV(__FUNCTION__);
|
||||||
|
#ifdef HAVE_BLUETOOTH
|
||||||
|
native_data_t *nat = get_native_data(env, object);
|
||||||
|
if (nat) {
|
||||||
|
const char *c_name = env->GetStringUTFChars(name, NULL);
|
||||||
|
LOGV("... name = %s", c_name);
|
||||||
|
LOGV("... uuid1 = %llX", uuidMsb);
|
||||||
|
LOGV("... uuid2 = %llX", uuidLsb);
|
||||||
|
LOGV("... channel = %d", channel);
|
||||||
|
DBusMessage *reply = dbus_func_args(env, nat->conn,
|
||||||
|
get_adapter_path(env, object),
|
||||||
|
DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord",
|
||||||
|
DBUS_TYPE_STRING, &c_name,
|
||||||
|
DBUS_TYPE_UINT64, &uuidMsb,
|
||||||
|
DBUS_TYPE_UINT64, &uuidLsb,
|
||||||
|
DBUS_TYPE_UINT16, &channel,
|
||||||
|
DBUS_TYPE_INVALID);
|
||||||
|
env->ReleaseStringUTFChars(name, c_name);
|
||||||
|
return reply ? dbus_returns_uint32(env, reply) : -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jboolean removeServiceRecordNative(JNIEnv *env, jobject object, jint handle) {
|
||||||
|
LOGV(__FUNCTION__);
|
||||||
|
#ifdef HAVE_BLUETOOTH
|
||||||
|
native_data_t *nat = get_native_data(env, object);
|
||||||
|
if (nat) {
|
||||||
|
LOGV("... handle = %X", handle);
|
||||||
|
DBusMessage *reply = dbus_func_args(env, nat->conn,
|
||||||
|
get_adapter_path(env, object),
|
||||||
|
DBUS_ADAPTER_IFACE, "RemoveServiceRecord",
|
||||||
|
DBUS_TYPE_UINT32, &handle,
|
||||||
|
DBUS_TYPE_INVALID);
|
||||||
|
return reply ? JNI_TRUE : JNI_FALSE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static JNINativeMethod sMethods[] = {
|
static JNINativeMethod sMethods[] = {
|
||||||
/* name, signature, funcPtr */
|
/* name, signature, funcPtr */
|
||||||
@@ -861,6 +903,8 @@ static JNINativeMethod sMethods[] = {
|
|||||||
(void *)setDevicePropertyBooleanNative},
|
(void *)setDevicePropertyBooleanNative},
|
||||||
{"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative},
|
{"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative},
|
||||||
{"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative},
|
{"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative},
|
||||||
|
{"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative},
|
||||||
|
{"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative},
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_android_server_BluetoothService(JNIEnv *env) {
|
int register_android_server_BluetoothService(JNIEnv *env) {
|
||||||
|
|||||||
Reference in New Issue
Block a user