diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/bluetooth/BluetoothAdapter.java | 158 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothServerSocket.java | 14 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothSocket.java | 28 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothUuid.java | 4 | ||||
-rw-r--r-- | core/java/android/bluetooth/IBluetooth.aidl | 3 | ||||
-rw-r--r-- | core/java/android/server/BluetoothService.java | 128 | ||||
-rw-r--r-- | core/jni/android_bluetooth_BluetoothSocket.cpp | 25 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothService.cpp | 44 |
8 files changed, 351 insertions, 53 deletions
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index e3ec2cc..c6a0619 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -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 @@ -558,29 +564,138 @@ public final class BluetoothAdapter { } /** + * 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.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 e) {} + socket.mSocket.throwErrnoNative(errno); // Exception as a result of bindListen() + } + } + + int handle = -1; try { - socket.mSocket.bindListen(); - } catch (IOException e) { + handle = mService.addRfcommServiceRecord(name, uuid, channel, new Binder()); + } catch (RemoteException e) {Log.e(TAG, "", e);} + if (handle == -1) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } 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. diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 45dc432..c14e4c0 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -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; + } } diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index e462ea6..573cb3d 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -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; } diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index da0564a..4164a3d 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -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); } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 2f77ba4..e54abec 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -63,4 +63,7 @@ interface IBluetooth boolean setTrust(in String address, in boolean value); boolean getTrustState(in String address); + + int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); + void removeServiceRecord(int handle); } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index edf6d10..93133d7 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.os.ParcelUuid; @@ -37,6 +38,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; +import android.os.IBinder; import android.os.Handler; import android.os.Message; import android.os.RemoteException; @@ -90,6 +92,8 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap <String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache; private final ArrayList <String> mUuidIntentTracker; + private final HashMap<Integer, Integer> mServiceRecordToPid; + static { classInitNative(); } @@ -117,6 +121,7 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>(); mUuidIntentTracker = new ArrayList<String>(); + mServiceRecordToPid = new HashMap<Integer, Integer>(); registerForAirplaneMode(); } @@ -206,6 +211,7 @@ public class BluetoothService extends IBluetooth.Stub { mIsDiscovering = false; mAdapterProperties.clear(); + mServiceRecordToPid.clear(); if (saveSetting) { persistBluetoothOnSetting(false); @@ -1211,6 +1217,71 @@ public class BluetoothService extends IBluetooth.Stub { 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() { @Override public void onReceive(Context context, Intent intent) { @@ -1263,25 +1334,25 @@ public class BluetoothService extends IBluetooth.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n"); - switch(mBluetoothState) { case BluetoothAdapter.STATE_OFF: - pw.println("\nBluetooth OFF\n"); + pw.println("Bluetooth OFF\n"); return; case BluetoothAdapter.STATE_TURNING_ON: - pw.println("\nBluetooth TURNING ON\n"); + pw.println("Bluetooth TURNING ON\n"); return; case BluetoothAdapter.STATE_TURNING_OFF: - pw.println("\nBluetooth TURNING OFF\n"); + pw.println("Bluetooth TURNING OFF\n"); return; case BluetoothAdapter.STATE_ON: - pw.println("\nBluetooth ON\n"); + pw.println("Bluetooth ON\n"); } - pw.println("\nLocal address = " + getAddress()); - pw.println("\nLocal name = " + getName()); - pw.println("\nisDiscovering() = " + isDiscovering()); + pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive); + + pw.println("Local address = " + getAddress()); + pw.println("Local name = " + getName()); + pw.println("isDiscovering() = " + isDiscovering()); BluetoothHeadset headset = new BluetoothHeadset(mContext, null); @@ -1292,13 +1363,17 @@ public class BluetoothService extends IBluetooth.Stub { toBondStateString(bondState), mBondState.getAttempt(address), getRemoteName(address)); - if (bondState == BluetoothDevice.BOND_BONDED) { - ParcelUuid[] uuids = getRemoteUuids(address); - if (uuids == null) { - pw.printf("\tuuids = null\n"); - } else { - for (ParcelUuid uuid : uuids) { - pw.printf("\t" + uuid + "\n"); + + Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address); + if (uuidChannels == null) { + pw.println("\tuuids = null"); + } else { + for (ParcelUuid uuid : uuidChannels.keySet()) { + 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(","); } pw.println("\n--ACL connected devices--"); - for (String device : devicesObjectPath) { - pw.println(getAddressFromObjectPath(device)); + if (devicesObjectPath != null) { + for (String device : devicesObjectPath) { + pw.println(getAddressFromObjectPath(device)); + } } // 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"); break; } - pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset()); - pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); + pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset()); + pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); 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) { @@ -1423,8 +1505,12 @@ public class BluetoothService extends IBluetooth.Stub { private native boolean setPasskeyNative(String address, int passkey, int nativeData); private native boolean setPairingConfirmationNative(String address, boolean confirm, 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 discoverServicesNative(String objectPath, String pattern); + private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb, + short channel); + private native boolean removeServiceRecordNative(int handle); } diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp index 2532eff..31ebf8c 100644 --- a/core/jni/android_bluetooth_BluetoothSocket.cpp +++ b/core/jni/android_bluetooth_BluetoothSocket.cpp @@ -237,7 +237,8 @@ static void connectNative(JNIEnv *env, jobject obj) { 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 LOGV(__FUNCTION__); @@ -248,7 +249,7 @@ static void bindListenNative(JNIEnv *env, jobject obj) { struct asocket *s = get_socketData(env, obj); if (!s) - return; + return EINVAL; 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)); break; default: - jniThrowIOException(env, ENOSYS); - return; + return ENOSYS; } if (bind(s->fd, addr, addr_sz)) { LOGV("...bind(%d) gave errno %d", s->fd, errno); - jniThrowIOException(env, errno); - return; + return errno; } if (listen(s->fd, 1)) { LOGV("...listen(%d) gave errno %d", s->fd, errno); - jniThrowIOException(env, errno); - return; + return errno; } LOGV("...bindListenNative(%d) success", s->fd); - return; + return 0; #endif - jniThrowIOException(env, ENOSYS); + return ENOSYS; } static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) { @@ -521,17 +519,22 @@ static void destroyNative(JNIEnv *env, jobject obj) { jniThrowIOException(env, ENOSYS); } +static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) { + jniThrowIOException(env, err); +} + static JNINativeMethod sMethods[] = { {"initSocketNative", "()V", (void*) initSocketNative}, {"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative}, {"connectNative", "()V", (void *) connectNative}, - {"bindListenNative", "()V", (void *) bindListenNative}, + {"bindListenNative", "()I", (void *) bindListenNative}, {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative}, {"availableNative", "()I", (void *) availableNative}, {"readNative", "([BII)I", (void *) readNative}, {"writeNative", "([BII)I", (void *) writeNative}, {"abortNative", "()V", (void *) abortNative}, {"destroyNative", "()V", (void *) destroyNative}, + {"throwErrnoNative", "(I)V", (void *) throwErrnoNative}, }; int register_android_bluetooth_BluetoothSocket(JNIEnv *env) { diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index c2f93eea..ea64305 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -818,6 +818,48 @@ static jboolean discoverServicesNative(JNIEnv *env, jobject object, 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[] = { /* name, signature, funcPtr */ @@ -861,6 +903,8 @@ static JNINativeMethod sMethods[] = { (void *)setDevicePropertyBooleanNative}, {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative}, {"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) { |