From d5ac1ae36b4e096eb97984334f86d0c68abea2f7 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 5 May 2009 22:26:12 -0700 Subject: Framework changes for bluez4. Changes in the Bluetooth JNI calls and framework functions for Bluez4. --- core/java/android/bluetooth/BluetoothDevice.java | 192 +---- core/java/android/bluetooth/BluetoothUuid.java | 62 ++ core/java/android/bluetooth/IBluetoothDevice.aidl | 26 +- .../bluetooth/IBluetoothDeviceCallback.aidl | 25 - core/java/android/server/BluetoothA2dpService.java | 403 ++++----- .../android/server/BluetoothDeviceService.java | 698 ++++++--------- core/java/android/server/BluetoothEventLoop.java | 266 +++--- core/jni/android_bluetooth_common.cpp | 363 +++++++- core/jni/android_bluetooth_common.h | 17 + core/jni/android_server_BluetoothA2dpService.cpp | 324 ++----- core/jni/android_server_BluetoothDeviceService.cpp | 944 +++++++-------------- core/jni/android_server_BluetoothEventLoop.cpp | 633 ++++++-------- 12 files changed, 1658 insertions(+), 2295 deletions(-) create mode 100644 core/java/android/bluetooth/BluetoothUuid.java delete mode 100644 core/java/android/bluetooth/IBluetoothDeviceCallback.aidl (limited to 'core') diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 951b4b0..c942a27 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -75,7 +75,7 @@ public class BluetoothDevice { public static final int UNBOND_REASON_REMOVED = 6; private static final String TAG = "BluetoothDevice"; - + private final IBluetoothDevice mService; /** * @hide - hide this because it takes a parameter of type @@ -180,31 +180,6 @@ public class BluetoothDevice { return false; } - public String getVersion() { - try { - return mService.getVersion(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRevision() { - try { - return mService.getRevision(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getManufacturer() { - try { - return mService.getManufacturer(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getCompany() { - try { - return mService.getCompany(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - /** * Get the current scan mode. * Used to determine if the local device is connectable and/or discoverable @@ -241,11 +216,8 @@ public class BluetoothDevice { } public boolean startDiscovery() { - return startDiscovery(true); - } - public boolean startDiscovery(boolean resolveNames) { try { - return mService.startDiscovery(resolveNames); + return mService.startDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -263,88 +235,17 @@ public class BluetoothDevice { return false; } - public boolean startPeriodicDiscovery() { - try { - return mService.startPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean stopPeriodicDiscovery() { - try { - return mService.stopPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean isPeriodicDiscovery() { - try { - return mService.isPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public String[] listRemoteDevices() { - try { - return mService.listRemoteDevices(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * List remote devices that have a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @return bluetooth hardware addresses of remote devices with a current - * ACL connection. Array size is 0 if no devices have a - * connection. Null on error. - */ - public String[] listAclConnections() { - try { - return mService.listAclConnections(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Check if a specified remote device has a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @param address the Bluetooth hardware address you want to check. - * @return true if there is an ACL connection, false otherwise and on - * error. - */ - public boolean isAclConnected(String address) { - try { - return mService.isAclConnected(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - /** - * Perform a low level (ACL) disconnection of a remote device. - * - * This forcably disconnects the ACL layer connection to a remote device, - * which will cause all RFCOMM, SDP and L2CAP connections to this remote - * device to close. + * Removes the remote device and the pairing information associated + * with it. * * @param address the Bluetooth hardware address you want to disconnect. * @return true if the device was disconnected, false otherwise and on * error. */ - public boolean disconnectRemoteDeviceAcl(String address) { + public boolean removeBond(String address) { try { - return mService.disconnectRemoteDeviceAcl(address); + return mService.removeBond(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -377,16 +278,6 @@ public class BluetoothDevice { } /** - * Remove an already exisiting bonding (delete the link key). - */ - public boolean removeBond(String address) { - try { - return mService.removeBond(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** * List remote devices that are bonded (paired) to the local device. * * Bonding (pairing) is the process by which the user enters a pin code for @@ -440,78 +331,25 @@ public class BluetoothDevice { return null; } - public String getRemoteVersion(String address) { - try { - return mService.getRemoteVersion(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteRevision(String address) { - try { - return mService.getRemoteRevision(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteManufacturer(String address) { - try { - return mService.getRemoteManufacturer(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteCompany(String address) { - try { - return mService.getRemoteCompany(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Returns the RFCOMM channel associated with the 16-byte UUID on - * the remote Bluetooth address. - * - * Performs a SDP ServiceSearchAttributeRequest transaction. The provided - * uuid is verified in the returned record. If there was a problem, or the - * specified uuid does not exist, -1 is returned. - */ - public boolean getRemoteServiceChannel(String address, short uuid16, - IBluetoothDeviceCallback callback) { - try { - return mService.getRemoteServiceChannel(address, uuid16, callback); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Get the major, minor and servics classes of a remote device. - * These classes are encoded as a 32-bit integer. See BluetoothClass. - * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass, or - * BluetoothClass.ERROR on error - */ public int getRemoteClass(String address) { try { return mService.getRemoteClass(address); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothClass.ERROR; + return BluetoothError.ERROR_IPC; } - public byte[] getRemoteFeatures(String address) { + public String[] getRemoteUuids(String address) { try { - return mService.getRemoteFeatures(address); + return mService.getRemoteUuids(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public String lastSeen(String address) { - try { - return mService.lastSeen(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String lastUsed(String address) { - try { - return mService.lastUsed(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + + public int getRemoteServiceChannel(String address, String uuid) { + try { + return mService.getRemoteServiceChannel(address, uuid); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR_IPC; } public boolean setPin(String address, byte[] pin) { diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java new file mode 100644 index 0000000..96b93f9 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -0,0 +1,62 @@ +/* + * 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; + +import java.util.UUID; + +/** +* Static helper methods and constants to decode the UUID of remote devices. +* @hide +*/ +public final class BluetoothUuid { + + /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs + * for the various services. + * + * The following 128 bit values are calculated as: + * uuid * 2^96 + BASE_UUID + */ + public static final UUID AudioSink = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + public static final UUID AudioSource = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB"); + public static final UUID HeadsetHS = UUID.fromString("00001131-0000-1000-8000-00805F9B34FB"); + public static final UUID Handsfree = UUID.fromString("0000111e-0000-1000-8000-00805F9B34FB"); + public static final UUID HandsfreeAudioGateway + = UUID.fromString("0000111f-0000-1000-8000-00805F9B34FB"); + + public static boolean isAudioSource(UUID uuid) { + return uuid.equals(AudioSource); + } + + public static boolean isAudioSink(UUID uuid) { + return uuid.equals(AudioSink); + } + + public static boolean isAdvAudioDist(UUID uuid) { + return uuid.equals(AdvAudioDist); + } + + public static boolean isHandsfree(UUID uuid) { + return uuid.equals(Handsfree) || uuid.equals(HandsfreeAudioGateway); + } + + public static boolean isHeadset(UUID uuid) { + return uuid.equals(HSP) || uuid.equals(HeadsetHS); + } +} + diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl index 6cd792e..c249c81 100644 --- a/core/java/android/bluetooth/IBluetoothDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothDevice.aidl @@ -16,8 +16,6 @@ package android.bluetooth; -import android.bluetooth.IBluetoothDeviceCallback; - /** * System private API for talking with the Bluetooth service. * @@ -33,10 +31,6 @@ interface IBluetoothDevice String getAddress(); String getName(); boolean setName(in String name); - String getVersion(); - String getRevision(); - String getManufacturer(); - String getCompany(); int getScanMode(); boolean setScanMode(int mode); @@ -44,17 +38,9 @@ interface IBluetoothDevice int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); - boolean startDiscovery(boolean resolveNames); + boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); - boolean startPeriodicDiscovery(); - boolean stopPeriodicDiscovery(); - boolean isPeriodicDiscovery(); - String[] listRemoteDevices(); - - String[] listAclConnections(); - boolean isAclConnected(in String address); - boolean disconnectRemoteDeviceAcl(in String address); boolean createBond(in String address); boolean cancelBondProcess(in String address); @@ -63,15 +49,9 @@ interface IBluetoothDevice int getBondState(in String address); String getRemoteName(in String address); - String getRemoteVersion(in String address); - String getRemoteRevision(in String address); int getRemoteClass(in String address); - String getRemoteManufacturer(in String address); - String getRemoteCompany(in String address); - boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); - byte[] getRemoteFeatures(in String adddress); - String lastSeen(in String address); - String lastUsed(in String address); + String[] getRemoteUuids(in String address); + int getRemoteServiceChannel(in String address, String uuid); boolean setPin(in String address, in byte[] pin); boolean cancelPin(in String address); diff --git a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl deleted file mode 100644 index d057093..0000000 --- a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2008, 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; - -/** - * {@hide} - */ -oneway interface IBluetoothDeviceCallback -{ - void onGetRemoteServiceChannelResult(in String address, int channel); -} diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 5c4e56d..1cf7be9 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -26,14 +26,13 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothIntent; +import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothA2dp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageManager; import android.media.AudioManager; -import android.os.Binder; import android.os.Handler; import android.os.Message; import android.provider.Settings; @@ -44,6 +43,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.UUID; public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String TAG = "BluetoothA2dpService"; @@ -58,57 +58,22 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String BLUETOOTH_ENABLED = "bluetooth_enabled"; private static final int MESSAGE_CONNECT_TO = 1; - private static final int MESSAGE_DISCONNECT = 2; - private final Context mContext; - private final IntentFilter mIntentFilter; - private HashMap mAudioDevices; - private final AudioManager mAudioManager; - private final BluetoothDevice mBluetooth; + private static final String PROPERTY_STATE = "State"; - // list of disconnected sinks to process after a delay - private final ArrayList mPendingDisconnects = new ArrayList(); - // number of active sinks - private int mSinkCount = 0; + private static final String SINK_STATE_DISCONNECTED = "disconnected"; + private static final String SINK_STATE_CONNECTING = "connecting"; + private static final String SINK_STATE_CONNECTED = "connected"; + private static final String SINK_STATE_PLAYING = "playing"; - private class SinkState { - public String address; - public int state; - public SinkState(String a, int s) {address = a; state = s;} - } + private static int mSinkCount; - public BluetoothA2dpService(Context context) { - mContext = context; - mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - - mBluetooth = (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE); - if (mBluetooth == null) { - throw new RuntimeException("Platform does not support Bluetooth"); - } - - if (!initNative()) { - throw new RuntimeException("Could not init BluetoothA2dpService"); - } - - mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION); - mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION); - mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); - mContext.registerReceiver(mReceiver, mIntentFilter); - - if (mBluetooth.isEnabled()) { - onBluetoothEnable(); - } - } - - @Override - protected void finalize() throws Throwable { - try { - cleanupNative(); - } finally { - super.finalize(); - } - } + private final Context mContext; + private final IntentFilter mIntentFilter; + private HashMap mAudioDevices; + private final AudioManager mAudioManager; + private final BluetoothDeviceService mBluetoothService; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -151,6 +116,40 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } }; + public BluetoothA2dpService(Context context, BluetoothDeviceService bluetoothService) { + mContext = context; + + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + + mBluetoothService = bluetoothService; + if (mBluetoothService == null) { + throw new RuntimeException("Platform does not support Bluetooth"); + } + + if (!initNative()) { + throw new RuntimeException("Could not init BluetoothA2dpService"); + } + + mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION); + mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION); + mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); + mContext.registerReceiver(mReceiver, mIntentFilter); + + mAudioDevices = new HashMap(); + + if (mBluetoothService.isEnabled()) + onBluetoothEnable(); + } + + @Override + protected void finalize() throws Throwable { + try { + cleanupNative(); + } finally { + super.finalize(); + } + } + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -159,7 +158,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { String address = (String)msg.obj; // check bluetooth is still on, device is still preferred, and // nothing is currently connected - if (mBluetooth.isEnabled() && + if (mBluetoothService.isEnabled() && getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF && lookupSinksMatchingStates(new int[] { BluetoothA2dp.STATE_CONNECTING, @@ -170,45 +169,85 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { connectSink(address); } break; - case MESSAGE_DISCONNECT: - handleDeferredDisconnect((String)msg.obj); - break; } } }; + private int convertBluezSinkStringtoState(String value) { + if (value.equalsIgnoreCase("disconnected")) + return BluetoothA2dp.STATE_DISCONNECTED; + if (value.equalsIgnoreCase("connecting")) + return BluetoothA2dp.STATE_CONNECTING; + if (value.equalsIgnoreCase("connected")) + return BluetoothA2dp.STATE_CONNECTED; + if (value.equalsIgnoreCase("playing")) + return BluetoothA2dp.STATE_PLAYING; + return -1; + } + + private synchronized boolean addAudioSink (String address) { + String path = mBluetoothService.getObjectPathFromAddress(address); + String propValues[] = (String []) getSinkPropertiesNative(path); + if (propValues == null) { + Log.e(TAG, "Error while getting AudioSink properties for device: " + address); + return false; + } + Integer state = null; + // Properties are name-value pairs + for (int i = 0; i < propValues.length; i+=2) { + if (propValues[i].equals(PROPERTY_STATE)) { + state = new Integer(convertBluezSinkStringtoState(propValues[i+1])); + break; + } + } + mAudioDevices.put(address, state); + handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTED, state); + return true; + } + private synchronized void onBluetoothEnable() { - mAudioDevices = new HashMap(); - String[] paths = (String[])listHeadsetsNative(); - if (paths != null) { - for (String path : paths) { - mAudioDevices.put(path, new SinkState(getAddressNative(path), - isSinkConnectedNative(path) ? BluetoothA2dp.STATE_CONNECTED : - BluetoothA2dp.STATE_DISCONNECTED)); + String devices = mBluetoothService.getProperty("Devices"); + mSinkCount = 0; + if (devices != null) { + String [] paths = devices.split(","); + for (String path: paths) { + String address = mBluetoothService.getAddressFromObjectPath(path); + String []uuids = mBluetoothService.getRemoteUuids(address); + if (uuids != null) + for (String uuid: uuids) { + UUID remoteUuid = UUID.fromString(uuid); + if (BluetoothUuid.isAudioSink(remoteUuid) || + BluetoothUuid.isAudioSource(remoteUuid) || + BluetoothUuid.isAdvAudioDist(remoteUuid)) { + addAudioSink(address); + break; + } + } } } mAudioManager.setParameter(BLUETOOTH_ENABLED, "true"); } private synchronized void onBluetoothDisable() { - if (mAudioDevices != null) { - // copy to allow modification during iteration - String[] paths = new String[mAudioDevices.size()]; - paths = mAudioDevices.keySet().toArray(paths); - for (String path : paths) { - switch (mAudioDevices.get(path).state) { + if (!mAudioDevices.isEmpty()) { + String [] addresses = new String[mAudioDevices.size()]; + addresses = mAudioDevices.keySet().toArray(addresses); + for (String address : addresses) { + int state = getSinkState(address); + switch (state) { case BluetoothA2dp.STATE_CONNECTING: case BluetoothA2dp.STATE_CONNECTED: case BluetoothA2dp.STATE_PLAYING: - disconnectSinkNative(path); - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + disconnectSinkNative(mBluetoothService.getObjectPathFromAddress(address)); + handleSinkStateChange(address,state, BluetoothA2dp.STATE_DISCONNECTED); break; case BluetoothA2dp.STATE_DISCONNECTING: - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + handleSinkStateChange(address, BluetoothA2dp.STATE_DISCONNECTING, + BluetoothA2dp.STATE_DISCONNECTED); break; } } - mAudioDevices = null; + mAudioDevices.clear(); } mAudioManager.setBluetoothA2dpOn(false); mAudioManager.setParameter(BLUETOOTH_ENABLED, "false"); @@ -221,9 +260,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return BluetoothError.ERROR; } - if (mAudioDevices == null) { - return BluetoothError.ERROR; - } + // ignore if there are any active sinks if (lookupSinksMatchingStates(new int[] { BluetoothA2dp.STATE_CONNECTING, @@ -233,20 +270,11 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return BluetoothError.ERROR; } - String path = lookupPath(address); - if (path == null) { - path = createHeadsetNative(address); - if (DBG) log("new bluez sink: " + address + " (" + path + ")"); - } - if (path == null) { + if (mAudioDevices.get(address) == null && !addAudioSink(address)) return BluetoothError.ERROR; - } - SinkState sink = mAudioDevices.get(path); - int state = BluetoothA2dp.STATE_DISCONNECTED; - if (sink != null) { - state = sink.state; - } + int state = mAudioDevices.get(address); + switch (state) { case BluetoothA2dp.STATE_CONNECTED: case BluetoothA2dp.STATE_PLAYING: @@ -256,11 +284,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return BluetoothError.SUCCESS; } + String path = mBluetoothService.getObjectPathFromAddress(address); + if (path == null) + return BluetoothError.ERROR; + // State is DISCONNECTED if (!connectSinkNative(path)) { return BluetoothError.ERROR; } - updateState(path, BluetoothA2dp.STATE_CONNECTING); return BluetoothError.SUCCESS; } @@ -271,14 +302,12 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return BluetoothError.ERROR; } - if (mAudioDevices == null) { - return BluetoothError.ERROR; - } - String path = lookupPath(address); + String path = mBluetoothService.getObjectPathFromAddress(address); if (path == null) { return BluetoothError.ERROR; } - switch (mAudioDevices.get(path).state) { + + switch (getSinkState(address)) { case BluetoothA2dp.STATE_DISCONNECTED: return BluetoothError.ERROR; case BluetoothA2dp.STATE_DISCONNECTING: @@ -289,7 +318,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!disconnectSinkNative(path)) { return BluetoothError.ERROR; } else { - updateState(path, BluetoothA2dp.STATE_DISCONNECTING); return BluetoothError.SUCCESS; } } @@ -305,15 +333,10 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return BluetoothError.ERROR; } - if (mAudioDevices == null) { + Integer state = mAudioDevices.get(address); + if (state == null) return BluetoothA2dp.STATE_DISCONNECTED; - } - for (SinkState sink : mAudioDevices.values()) { - if (address.equals(sink.address)) { - return sink.state; - } - } - return BluetoothA2dp.STATE_DISCONNECTED; + return state; } public synchronized int getSinkPriority(String address) { @@ -337,103 +360,68 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { BluetoothError.SUCCESS : BluetoothError.ERROR; } - private synchronized void onHeadsetCreated(String path) { - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); - } - - private synchronized void onHeadsetRemoved(String path) { - if (mAudioDevices == null) return; - mAudioDevices.remove(path); - } - - private synchronized void onSinkConnected(String path) { - // if we are reconnected, do not process previous disconnect event. - mPendingDisconnects.remove(path); - - if (mAudioDevices == null) return; - // bluez 3.36 quietly disconnects the previous sink when a new sink - // is connected, so we need to mark all previously connected sinks as - // disconnected - - // copy to allow modification during iteration - String[] paths = new String[mAudioDevices.size()]; - paths = mAudioDevices.keySet().toArray(paths); - for (String oldPath : paths) { - if (path.equals(oldPath)) { - continue; - } - int state = mAudioDevices.get(oldPath).state; - if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) { - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); - } + private synchronized void onSinkPropertyChanged(String path, String []propValues) { + String name = propValues[0]; + String address = mBluetoothService.getAddressFromObjectPath(path); + if (address == null) { + Log.e(TAG, "onSinkPropertyChanged: Address of the remote device in null"); + return; } - updateState(path, BluetoothA2dp.STATE_CONNECTING); - mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path)); - mAudioManager.setBluetoothA2dpOn(true); - updateState(path, BluetoothA2dp.STATE_CONNECTED); - } - - private synchronized void onSinkDisconnected(String path) { - // This is to work around a problem in bluez that results - // sink disconnect events being sent, immediately followed by a reconnect. - // To avoid unnecessary audio routing changes, we defer handling - // sink disconnects until after a short delay. - mPendingDisconnects.add(path); - Message msg = Message.obtain(mHandler, MESSAGE_DISCONNECT, path); - mHandler.sendMessageDelayed(msg, 2000); - } - - private synchronized void handleDeferredDisconnect(String path) { - if (mPendingDisconnects.contains(path)) { - mPendingDisconnects.remove(path); - if (mSinkCount == 1) { - mAudioManager.setBluetoothA2dpOn(false); - } - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); + if (mAudioDevices.get(address) == null) { + // Ignore this state change, since it means we have got it after + // bluetooth has been disabled. + return; + } + if (name.equals(PROPERTY_STATE)) { + int state = convertBluezSinkStringtoState(propValues[1]); + int prevState = mAudioDevices.get(address); + handleSinkStateChange(address, prevState, state); } } - private synchronized void onSinkPlaying(String path) { - updateState(path, BluetoothA2dp.STATE_PLAYING); - } - - private synchronized void onSinkStopped(String path) { - updateState(path, BluetoothA2dp.STATE_CONNECTED); - } + private void handleSinkStateChange(String address, int prevState, int state) { + if (state != prevState) { + if (state == BluetoothA2dp.STATE_DISCONNECTED || + state == BluetoothA2dp.STATE_DISCONNECTING) { + if (prevState == BluetoothA2dp.STATE_CONNECTED || + prevState == BluetoothA2dp.STATE_PLAYING) { + // disconnecting or disconnected + Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + mContext.sendBroadcast(intent); + } + if (--mSinkCount == 0) + mAudioManager.setBluetoothA2dpOn(false); + } else if (state == BluetoothA2dp.STATE_CONNECTED) { + mSinkCount ++; + } + mAudioDevices.put(address, state); - private synchronized final String lookupAddress(String path) { - if (mAudioDevices == null) return null; - SinkState sink = mAudioDevices.get(path); - if (sink == null) { - Log.w(TAG, "lookupAddress() called for unknown device " + path); - updateState(path, BluetoothA2dp.STATE_DISCONNECTED); - } - String address = mAudioDevices.get(path).address; - if (address == null) Log.e(TAG, "Can't find address for " + path); - return address; - } + Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothA2dp.SINK_STATE, state); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); - private synchronized final String lookupPath(String address) { - if (mAudioDevices == null) return null; + if (DBG) log("A2DP state : address: " + address + " State:" + prevState + "->" + state); - for (String path : mAudioDevices.keySet()) { - if (address.equals(mAudioDevices.get(path).address)) { - return path; + if (state == BluetoothA2dp.STATE_CONNECTED) { + mAudioManager.setParameter(A2DP_SINK_ADDRESS, address); + mAudioManager.setBluetoothA2dpOn(true); } } - return null; } private synchronized List lookupSinksMatchingStates(int[] states) { List sinks = new ArrayList(); - if (mAudioDevices == null) { + if (mAudioDevices.isEmpty()) { return sinks; } - for (SinkState sink : mAudioDevices.values()) { + for (String path: mAudioDevices.keySet()) { + int sinkState = getSinkState(path); for (int state : states) { - if (sink.state == state) { - sinks.add(sink.address); + if (state == sinkState) { + sinks.add(path); break; } } @@ -441,57 +429,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return sinks; } - private synchronized void updateState(String path, int state) { - if (mAudioDevices == null) return; - - SinkState s = mAudioDevices.get(path); - int prevState; - String address; - if (s == null) { - address = getAddressNative(path); - mAudioDevices.put(path, new SinkState(address, state)); - prevState = BluetoothA2dp.STATE_DISCONNECTED; - } else { - address = lookupAddress(path); - prevState = s.state; - s.state = state; - } - - if (state != prevState) { - if (DBG) log("state " + address + " (" + path + ") " + prevState + "->" + state); - - // keep track of the number of active sinks - if (prevState == BluetoothA2dp.STATE_DISCONNECTED) { - mSinkCount++; - } else if (state == BluetoothA2dp.STATE_DISCONNECTED) { - mSinkCount--; - } - - Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothA2dp.SINK_STATE, state); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - - if ((prevState == BluetoothA2dp.STATE_CONNECTED || - prevState == BluetoothA2dp.STATE_PLAYING) && - (state != BluetoothA2dp.STATE_CONNECTING && - state != BluetoothA2dp.STATE_CONNECTED && - state != BluetoothA2dp.STATE_PLAYING)) { - // disconnected - intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); - mContext.sendBroadcast(intent); - } - } - } @Override protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mAudioDevices == null) return; + if (mAudioDevices.isEmpty()) return; pw.println("Cached audio devices:"); - for (String path : mAudioDevices.keySet()) { - SinkState sink = mAudioDevices.get(path); - pw.println(path + " " + sink.address + " " + BluetoothA2dp.stateToString(sink.state)); + for (String address : mAudioDevices.keySet()) { + int state = mAudioDevices.get(address); + pw.println(address + " " + BluetoothA2dp.stateToString(state)); } } @@ -501,11 +446,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private native boolean initNative(); private native void cleanupNative(); - private synchronized native String[] listHeadsetsNative(); - private synchronized native String createHeadsetNative(String address); - private synchronized native boolean removeHeadsetNative(String path); - private synchronized native String getAddressNative(String path); private synchronized native boolean connectSinkNative(String path); private synchronized native boolean disconnectSinkNative(String path); - private synchronized native boolean isSinkConnectedNative(String path); + private synchronized native Object []getSinkPropertiesNative(String path); } diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 3a89abd..36c432b 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -30,7 +30,6 @@ import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothIntent; import android.bluetooth.IBluetoothDevice; -import android.bluetooth.IBluetoothDeviceCallback; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -38,7 +37,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; import android.os.Handler; -import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; @@ -46,6 +44,8 @@ import android.os.SystemService; import android.provider.Settings; import android.util.Log; +import com.android.internal.app.IBatteryStats; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; @@ -55,8 +55,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import com.android.internal.app.IBatteryStats; - public class BluetoothDeviceService extends IBluetoothDevice.Stub { private static final String TAG = "BluetoothDeviceService"; private static final boolean DBG = true; @@ -80,10 +78,12 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { private static final int MESSAGE_REGISTER_SDP_RECORDS = 1; private static final int MESSAGE_FINISH_DISABLE = 2; + private Map mProperties; + private HashMap > mRemoteDeviceProperties; + static { classInitNative(); } - private native static void classInitNative(); public BluetoothDeviceService(Context context) { mContext = context; @@ -109,8 +109,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mIsDiscovering = false; mEventLoop = new BluetoothEventLoop(mContext, this); registerForAirplaneMode(); + mProperties = new HashMap(); + mRemoteDeviceProperties = new HashMap>(); } - private native void initializeNativeDataNative(); @Override protected void finalize() throws Throwable { @@ -123,13 +124,11 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { super.finalize(); } } - private native void cleanupNativeDataNative(); public boolean isEnabled() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mBluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON; } - private native int isEnabledNative(); public int getBluetoothState() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); @@ -150,8 +149,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * @param saveSetting If true, disable BT in settings */ public synchronized boolean disable(boolean saveSetting) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); switch (mBluetoothState) { case BluetoothDevice.BLUETOOTH_STATE_OFF: @@ -180,6 +178,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return; } mEventLoop.stop(); + tearDownNativeDataNative(); disableNative(); // mark in progress bondings as cancelled @@ -188,25 +187,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); } - // Remove remoteServiceChannelCallbacks - HashMap callbacksMap = - mEventLoop.getRemoteServiceChannelCallbacks(); - - for (Iterator i = callbacksMap.keySet().iterator(); i.hasNext();) { - String address = i.next(); - IBluetoothDeviceCallback callback = callbacksMap.get(address); - try { - callback.onGetRemoteServiceChannelResult(address, BluetoothError.ERROR_DISABLED); - } catch (RemoteException e) {} - i.remove(); - } - // update mode Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE); mContext.sendBroadcast(intent, BLUETOOTH_PERM); mIsDiscovering = false; + mProperties.clear(); if (saveSetting) { persistBluetoothOnSetting(false); @@ -270,7 +257,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (!disable(false)) { mRestart = false; } - } + } private synchronized void setBluetoothState(int state) { if (state == mBluetoothState) { @@ -343,6 +330,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (res) { + if (!setupNativeDataNative()) { + return; + } if (mSaveSetting) { persistBluetoothOnSetting(true); } @@ -369,7 +359,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (res) { // Update mode - mEventLoop.onModeChanged(getModeNative()); + String[] propVal = {"Pairable", getProperty("Pairable")}; + mEventLoop.onPropertyChanged(propVal); } if (mIsAirplaneSensitive && isAirplaneModeOn()) { @@ -386,9 +377,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { Binder.restoreCallingIdentity(origCallerIdentityToken); } - private native int enableNative(); - private native int disableNative(); - /* package */ BondState getBondState() { return mBondState; } @@ -423,14 +411,19 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_ON) { return; } - String[] bonds = listBondingsNative(); + String []bonds = null; + String val = getProperty("Devices"); + if (val != null) { + bonds = val.split(","); + } if (bonds == null) { return; } mState.clear(); if (DBG) log("found " + bonds.length + " bonded devices"); - for (String address : bonds) { - mState.put(address.toUpperCase(), BluetoothDevice.BOND_BONDED); + for (String device : bonds) { + mState.put(getAddressFromObjectPath(device).toUpperCase(), + BluetoothDevice.BOND_BONDED); } } @@ -528,7 +521,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } } - private native String[] listBondingsNative(); private static String toBondStateString(int bondState) { switch (bondState) { @@ -543,17 +535,36 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } } - public synchronized String getAddress() { + /*package*/synchronized void getAllProperties() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getAddressNative(); + mProperties.clear(); + + String properties[] = (String [])getAdapterPropertiesNative(); + // The String Array consists of key-value pairs. + if (properties == null) { + Log.e(TAG, "*Error*: GetAdapterProperties returned NULL"); + return; + } + for (int i = 0; i < properties.length; i+=2) { + String value = null; + if (mProperties.containsKey(properties[i])) { + value = mProperties.get(properties[i]); + value = value + ',' + properties[i+1]; + } else + value = properties[i+1]; + + mProperties.put(properties[i], value); + } + + // Add adapter object path property. + String adapterPath = getAdapterPathNative(); + if (adapterPath != null) + mProperties.put("ObjectPath", adapterPath + "/dev_"); } - private native String getAddressNative(); - public synchronized String getName() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getNameNative(); + /* package */ synchronized void setProperty(String name, String value) { + mProperties.put(name, value); } - private native String getNameNative(); public synchronized boolean setName(String name) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -561,90 +572,101 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (name == null) { return false; } - // hcid handles persistance of the bluetooth name - return setNameNative(name); + return setPropertyString("Name", name); } - private native boolean setNameNative(String name); - /** - * Returns the user-friendly name of a remote device. This value is - * retrned from our local cache, which is updated during device discovery. - * Do not expect to retrieve the updated remote name immediately after - * changing the name on the remote device. - * - * @param address Bluetooth address of remote device. - * - * @return The user-friendly name of the specified remote device. - */ - public synchronized String getRemoteName(String address) { + //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean + // Either have a single property function with Object as the parameter + // or have a function for each property and then obfuscate in the JNI layer. + // The following looks dirty. + private boolean setPropertyString(String key, String value) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteNameNative(address); + return setAdapterPropertyStringNative(key, value); } - private native String getRemoteNameNative(String address); - /* pacakge */ native String getAdapterPathNative(); + private boolean setPropertyInteger(String key, int value) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return setAdapterPropertyIntegerNative(key, value); + } - public synchronized boolean startDiscovery(boolean resolveNames) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return startDiscoveryNative(resolveNames); + private boolean setPropertyBoolean(String key, boolean value) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return setAdapterPropertyBooleanNative(key, value ? 1 : 0); } - private native boolean startDiscoveryNative(boolean resolveNames); - public synchronized boolean cancelDiscovery() { + /** + * Set the discoverability window for the device. A timeout of zero + * makes the device permanently discoverable (if the device is + * discoverable). Setting the timeout to a nonzero value does not make + * a device discoverable; you need to call setMode() to make the device + * explicitly discoverable. + * + * @param timeout_s The discoverable timeout in seconds. + */ + public synchronized boolean setDiscoverableTimeout(int timeout) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - return cancelDiscoveryNative(); + return setPropertyInteger("DiscoverableTimeout", timeout); } - private native boolean cancelDiscoveryNative(); - public synchronized boolean isDiscovering() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mIsDiscovering; - } + public synchronized boolean setScanMode(int mode) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + boolean pairable = false, discoverable = false; + String modeString = scanModeToBluezString(mode); + if (modeString.equals("off")) { + pairable = false; + discoverable = false; + } else if (modeString.equals("pariable")) { + pairable = true; + discoverable = false; + } else if (modeString.equals("discoverable")) { + pairable = true; + discoverable = true; + } + setPropertyBoolean("Pairable", pairable); + setPropertyBoolean("Discoverable", discoverable); - /* package */ void setIsDiscovering(boolean isDiscovering) { - mIsDiscovering = isDiscovering; + return true; } - public synchronized boolean startPeriodicDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return startPeriodicDiscoveryNative(); + /*package*/ synchronized String getProperty (String name) { + if (!mProperties.isEmpty()) + return mProperties.get(name); + getAllProperties(); + return mProperties.get(name); } - private native boolean startPeriodicDiscoveryNative(); - public synchronized boolean stopPeriodicDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return stopPeriodicDiscoveryNative(); + public synchronized String getAddress() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return getProperty("Address"); } - private native boolean stopPeriodicDiscoveryNative(); - public synchronized boolean isPeriodicDiscovery() { + public synchronized String getName() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return isPeriodicDiscoveryNative(); + return getProperty("Name"); } - private native boolean isPeriodicDiscoveryNative(); /** - * Set the discoverability window for the device. A timeout of zero - * makes the device permanently discoverable (if the device is - * discoverable). Setting the timeout to a nonzero value does not make - * a device discoverable; you need to call setMode() to make the device - * explicitly discoverable. + * Returns the user-friendly name of a remote device. This value is + * returned from our local cache, which is updated when onPropertyChange + * event is received. + * Do not expect to retrieve the updated remote name immediately after + * changing the name on the remote device. * - * @param timeout_s The discoverable timeout in seconds. + * @param address Bluetooth address of remote device. + * + * @return The user-friendly name of the specified remote device. */ - public synchronized boolean setDiscoverableTimeout(int timeout) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return setDiscoverableTimeoutNative(timeout); + public synchronized String getRemoteName(String address) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!BluetoothDevice.checkBluetoothAddress(address)) { + return null; + } + Map properties = mRemoteDeviceProperties.get(address); + if (properties != null) return properties.get("Name"); + return null; } - private native boolean setDiscoverableTimeoutNative(int timeout_s); /** * Get the discoverability window for the device. A timeout of zero @@ -656,45 +678,46 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { */ public synchronized int getDiscoverableTimeout() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getDiscoverableTimeoutNative(); - } - private native int getDiscoverableTimeoutNative(); - - public synchronized boolean isAclConnected(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return isConnectedNative(address); + String timeout = getProperty("DiscoverableTimeout"); + if (timeout != null) + return Integer.valueOf(timeout); + else + return -1; } - private native boolean isConnectedNative(String address); public synchronized int getScanMode() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return bluezStringToScanMode(getModeNative()); + if (!isEnabled()) + return BluetoothError.ERROR; + + boolean pairable = getProperty("Pairable").equals("true"); + boolean discoverable = getProperty("Discoverable").equals("true"); + return bluezStringToScanMode (pairable, discoverable); } - private native String getModeNative(); - public synchronized boolean setScanMode(int mode) { + public synchronized boolean startDiscovery() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - String bluezMode = scanModeToBluezString(mode); - if (bluezMode != null) { - return setModeNative(bluezMode); + if (!isEnabled()) { + return false; } - return false; + return startDiscoveryNative(); } - private native boolean setModeNative(String mode); - public synchronized boolean disconnectRemoteDeviceAcl(String address) { + public synchronized boolean cancelDiscovery() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return disconnectRemoteDeviceNative(address); + return stopDiscoveryNative(); + } + + public synchronized boolean isDiscovering() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mIsDiscovering; + } + + /* package */ void setIsDiscovering(boolean isDiscovering) { + mIsDiscovering = isDiscovering; } - private native boolean disconnectRemoteDeviceNative(String address); public synchronized boolean createBond(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -719,14 +742,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return false; } - if (!createBondingNative(address, 60000 /* 1 minute */)) { + if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) { return false; } mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); return true; } - private native boolean createBondingNative(String address, int timeout_ms); public synchronized boolean cancelBondProcess(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -741,10 +763,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); - cancelBondingProcessNative(address); + cancelDeviceCreationNative(address); return true; } - private native boolean cancelBondingProcessNative(String address); public synchronized boolean removeBond(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -752,9 +773,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { if (!BluetoothDevice.checkBluetoothAddress(address)) { return false; } - return removeBondingNative(address); + return removeDeviceNative(getObjectPathFromAddress(address)); } - private native boolean removeBondingNative(String address); public synchronized String[] listBonds() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); @@ -769,198 +789,57 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return mBondState.getBondState(address.toUpperCase()); } - public synchronized String[] listAclConnections() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return listConnectionsNative(); - } - private native String[] listConnectionsNative(); - - /** - * This method lists all remote devices that this adapter is aware of. - * This is a list not only of all most-recently discovered devices, but of - * all devices discovered by this adapter up to some point in the past. - * Note that many of these devices may not be in the neighborhood anymore, - * and attempting to connect to them will result in an error. - * - * @return An array of strings representing the Bluetooth addresses of all - * remote devices that this adapter is aware of. - */ - public synchronized String[] listRemoteDevices() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return listRemoteDevicesNative(); - } - private native String[] listRemoteDevicesNative(); - - /** - * Returns the version of the Bluetooth chip. This version is compiled from - * the LMP version. In case of EDR the features attribute must be checked. - * Example: "Bluetooth 2.0 + EDR". - * - * @return a String representation of the this Adapter's underlying - * Bluetooth-chip version. - */ - public synchronized String getVersion() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getVersionNative(); - } - private native String getVersionNative(); - - /** - * Returns the revision of the Bluetooth chip. This is a vendor-specific - * value and in most cases it represents the firmware version. This might - * derive from the HCI revision and LMP subversion values or via extra - * vendord specific commands. - * In case the revision of a chip is not available. This method should - * return the LMP subversion value as a string. - * Example: "HCI 19.2" - * - * @return The HCI revision of this adapter. - */ - public synchronized String getRevision() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getRevisionNative(); - } - private native String getRevisionNative(); - - /** - * Returns the manufacturer of the Bluetooth chip. If the company id is not - * known the sting "Company ID %d" where %d should be replaced with the - * numeric value from the manufacturer field. - * Example: "Cambridge Silicon Radio" - * - * @return Manufacturer name. - */ - public synchronized String getManufacturer() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getManufacturerNative(); - } - private native String getManufacturerNative(); - - /** - * Returns the company name from the OUI database of the Bluetooth device - * address. This function will need a valid and up-to-date oui.txt from - * the IEEE. This value will be different from the manufacturer string in - * the most cases. - * If the oui.txt file is not present or the OUI part of the Bluetooth - * address is not listed, it should return the string "OUI %s" where %s is - * the actual OUI. - * - * Example: "Apple Computer" - * - * @return company name - */ - public synchronized String getCompany() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getCompanyNative(); - } - private native String getCompanyNative(); - - /** - * Like getVersion(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device Bluetooth version - * - * @see #getVersion - */ - public synchronized String getRemoteVersion(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteVersionNative(address); - } - private native String getRemoteVersionNative(String address); - - /** - * Like getRevision(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device HCI revision - * - * @see #getRevision - */ - public synchronized String getRemoteRevision(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteRevisionNative(address); - } - private native String getRemoteRevisionNative(String address); - - /** - * Like getManufacturer(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device Bluetooth chip manufacturer - * - * @see #getManufacturer - */ - public synchronized String getRemoteManufacturer(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; + /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) { + Map properties = mRemoteDeviceProperties.get(address); + if (properties != null) { + return properties.get(property); + } else { + // Query for remote device properties, again. + // We will need to reload the cache when we switch Bluetooth on / off + // or if we crash. + String objectPath = getObjectPathFromAddress(address); + String propValues[] = (String [])getDevicePropertiesNative(objectPath); + if (propValues != null) { + addRemoteDeviceProperties(address, propValues); + return getRemoteDeviceProperty(address, property); + } } - return getRemoteManufacturerNative(address); + Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address); + return null; } - private native String getRemoteManufacturerNative(String address); - /** - * Like getCompany(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device company - * - * @see #getCompany - */ - public synchronized String getRemoteCompany(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; + /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) { + Map propertyValues = new HashMap(); + for (int i = 0; i < properties.length; i+=2) { + String value = null; + if (propertyValues.containsKey(properties[i])) { + value = propertyValues.get(properties[i]); + value = value + ',' + properties[i+1]; + } else { + value = properties[i+1]; + } + propertyValues.put(properties[i], value); } - return getRemoteCompanyNative(address); + mRemoteDeviceProperties.put(address, propertyValues); } - private native String getRemoteCompanyNative(String address); - /** - * Returns the date and time when the specified remote device has been seen - * by a discover procedure. - * Example: "2006-02-08 12:00:00 GMT" - * - * @return a String with the timestamp. - */ - public synchronized String lastSeen(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return lastSeenNative(address); + /* package */ void removeRemoteDeviceProperties(String address) { + mRemoteDeviceProperties.remove(address); } - private native String lastSeenNative(String address); - /** - * Returns the date and time when the specified remote device has last been - * connected to - * Example: "2006-02-08 12:00:00 GMT" - * - * @return a String with the timestamp. - */ - public synchronized String lastUsed(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; + /* package */ synchronized void setRemoteDeviceProperty(String address, String name, + String value) { + Map propVal = mRemoteDeviceProperties.get(address); + if (propVal != null) { + propVal.put(name, value); + mRemoteDeviceProperties.put(address, propVal); + } else { + Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address); } - return lastUsedNative(address); } - private native String lastUsedNative(String address); /** - * Gets the remote major, minor, and service classes encoded as a 32-bit + * Gets the remote major, minor classes encoded as a 32-bit * integer. * * Note: this value is retrieved from cache, because we get it during @@ -968,120 +847,56 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * * @return 32-bit integer encoding the remote major, minor, and service * classes. - * - * @see #getRemoteMajorClass - * @see #getRemoteMinorClass - * @see #getRemoteServiceClasses */ public synchronized int getRemoteClass(String address) { if (!BluetoothDevice.checkBluetoothAddress(address)) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return BluetoothClass.ERROR; } - return getRemoteClassNative(address); + String val = getRemoteDeviceProperty(address, "Class"); + if (val == null) + return BluetoothClass.ERROR; + else { + return Integer.valueOf(val); + } } - private native int getRemoteClassNative(String address); + /** * Gets the remote features encoded as bit mask. * * Note: This method may be obsoleted soon. * - * @return byte array of features. - */ - public synchronized byte[] getRemoteFeatures(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteFeaturesNative(address); - } - private native byte[] getRemoteFeaturesNative(String address); - - /** - * This method and {@link #getRemoteServiceRecord} query the SDP service - * on a remote device. They do not interpret the data, but simply return - * it raw to the user. To read more about SDP service handles and records, - * consult the Bluetooth core documentation (www.bluetooth.com). - * - * @param address Bluetooth address of remote device. - * @param match a String match to narrow down the service-handle search. - * The only supported value currently is "hsp" for the headset - * profile. To retrieve all service handles, simply pass an empty - * match string. - * - * @return all service handles corresponding to the string match. - * - * @see #getRemoteServiceRecord + * @return String array of 128bit UUIDs */ - public synchronized int[] getRemoteServiceHandles(String address, String match) { + public synchronized String[] getRemoteUuids(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothDevice.checkBluetoothAddress(address)) { return null; } - if (match == null) { - match = ""; - } - return getRemoteServiceHandlesNative(address, match); + String value = getRemoteDeviceProperty(address, "UUIDs"); + String[] uuids = null; + // The UUIDs are stored as a "," separated string. + if (value != null) + uuids = value.split(","); + return uuids; } - private native int[] getRemoteServiceHandlesNative(String address, String match); /** - * This method retrieves the service records corresponding to a given - * service handle (method {@link #getRemoteServiceHandles} retrieves the - * service handles.) + * Gets the rfcomm channel associated with the UUID. * - * This method and {@link #getRemoteServiceHandles} do not interpret their - * data, but simply return it raw to the user. To read more about SDP - * service handles and records, consult the Bluetooth core documentation - * (www.bluetooth.com). + * @param address Address of the remote device + * @param uuid UUID of the service attribute * - * @param address Bluetooth address of remote device. - * @param handle Service handle returned by {@link #getRemoteServiceHandles} - * - * @return a byte array of all service records corresponding to the - * specified service handle. - * - * @see #getRemoteServiceHandles + * @return rfcomm channel associated with the service attribute */ - public synchronized byte[] getRemoteServiceRecord(String address, int handle) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteServiceRecordNative(address, handle); - } - private native byte[] getRemoteServiceRecordNative(String address, int handle); - - private static final int MAX_OUTSTANDING_ASYNC = 32; - - // AIDL does not yet support short's - public synchronized boolean getRemoteServiceChannel(String address, int uuid16, - IBluetoothDeviceCallback callback) { + public int getRemoteServiceChannel(String address, String uuid) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - HashMap callbacks = - mEventLoop.getRemoteServiceChannelCallbacks(); - if (callbacks.containsKey(address)) { - Log.w(TAG, "SDP request already in progress for " + address); - return false; + return BluetoothError.ERROR_IPC; } - // Protect from malicious clients - only allow 32 bonding requests per minute. - if (callbacks.size() > MAX_OUTSTANDING_ASYNC) { - Log.w(TAG, "Too many outstanding SDP requests, dropping request for " + address); - return false; - } - callbacks.put(address, callback); - - if (!getRemoteServiceChannelNative(address, (short)uuid16)) { - callbacks.remove(address); - return false; - } - return true; + return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004); } - private native boolean getRemoteServiceChannelNative(String address, short uuid16); public synchronized boolean setPin(String address, byte[] pin) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -1108,7 +923,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } return setPinNative(address, pinString, data.intValue()); } - private native boolean setPinNative(String address, String pin, int nativeData); public synchronized boolean cancelPin(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -1126,7 +940,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } return cancelPinNative(address, data.intValue()); } - private native boolean cancelPinNative(String address, int natveiData); private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -1190,20 +1003,22 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { BluetoothHeadset headset = new BluetoothHeadset(mContext, null); - String[] addresses = listRemoteDevices(); - pw.println("\n--Known devices--"); - for (String address : addresses) { + for (String address : mRemoteDeviceProperties.keySet()) { pw.printf("%s %10s (%d) %s\n", address, toBondStateString(mBondState.getBondState(address)), mBondState.getAttempt(address), getRemoteName(address)); } - addresses = listAclConnections(); + String value = getProperty("Devices"); + String []devicesObjectPath = null; + if (value != null) { + devicesObjectPath = value.split(","); + } pw.println("\n--ACL connected devices--"); - for (String address : addresses) { - pw.println(address); + for (String device : devicesObjectPath) { + pw.println(getAddressFromObjectPath(device)); } // Rather not do this from here, but no-where else and I need this @@ -1227,20 +1042,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { headset.close(); } - /* package */ static int bluezStringToScanMode(String mode) { - if (mode == null) { - return BluetoothError.ERROR; - } - mode = mode.toLowerCase(); - if (mode.equals("off")) { - return BluetoothDevice.SCAN_MODE_NONE; - } else if (mode.equals("connectable")) { - return BluetoothDevice.SCAN_MODE_CONNECTABLE; - } else if (mode.equals("discoverable")) { + /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) { + if (pairable && discoverable) return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE; - } else { - return BluetoothError.ERROR; - } + else if (pairable && !discoverable) + return BluetoothDevice.SCAN_MODE_CONNECTABLE; + else + return BluetoothDevice.SCAN_MODE_NONE; } /* package */ static String scanModeToBluezString(int mode) { @@ -1255,7 +1063,67 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { return null; } + /*package*/ String getAddressFromObjectPath(String objectPath) { + String adapterObjectPath = getProperty("ObjectPath"); + if (adapterObjectPath == null || objectPath == null) { + Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath + + " or deviceObjectPath:" + objectPath + " is null"); + return null; + } + if (!objectPath.startsWith(adapterObjectPath)) { + Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath + + " is not a prefix of deviceObjectPath:" + objectPath + + "bluetoothd crashed ?"); + return null; + } + String address = objectPath.substring(adapterObjectPath.length()); + if (address != null) return address.replace('_', ':'); + + Log.e(TAG, "getAddressFromObjectPath: Address being returned is null"); + return null; + } + + /*package*/ String getObjectPathFromAddress(String address) { + String path = getProperty("ObjectPath"); + if (path == null) { + Log.e(TAG, "Error: Object Path is null"); + return null; + } + path = path + address.replace(":", "_"); + return path; + } + private static void log(String msg) { Log.d(TAG, msg); } + + private native static void classInitNative(); + private native void initializeNativeDataNative(); + private native boolean setupNativeDataNative(); + private native boolean tearDownNativeDataNative(); + private native void cleanupNativeDataNative(); + private native String getAdapterPathNative(); + + private native int isEnabledNative(); + private native int enableNative(); + private native int disableNative(); + + private native Object[] getAdapterPropertiesNative(); + private native Object[] getDevicePropertiesNative(String objectPath); + private native boolean setAdapterPropertyStringNative(String key, String value); + private native boolean setAdapterPropertyIntegerNative(String key, int value); + private native boolean setAdapterPropertyBooleanNative(String key, int value); + + private native boolean startDiscoveryNative(); + private native boolean stopDiscoveryNative(); + + private native boolean createPairedDeviceNative(String address, int timeout_ms); + private native boolean cancelDeviceCreationNative(String address); + private native boolean removeDeviceNative(String objectPath); + private native int getDeviceServiceChannelNative(String objectPath, String uuid, + int attributeId); + + private native boolean cancelPinNative(String address, int nativeData); + private native boolean setPinNative(String address, String pin, int nativeData); + } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 8cc229b..00c13b7 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -21,15 +21,15 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothIntent; -import android.bluetooth.IBluetoothDeviceCallback; +import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Message; -import android.os.RemoteException; import android.util.Log; import java.util.HashMap; +import java.util.UUID; /** * TODO: Move this to @@ -47,7 +47,6 @@ class BluetoothEventLoop { private boolean mStarted; private boolean mInterrupted; private final HashMap mPasskeyAgentRequestData; - private final HashMap mGetRemoteServiceChannelCallbacks; private final BluetoothDeviceService mBluetoothService; private final Context mContext; @@ -89,10 +88,8 @@ class BluetoothEventLoop { mBluetoothService = bluetoothService; mContext = context; mPasskeyAgentRequestData = new HashMap(); - mGetRemoteServiceChannelCallbacks = new HashMap(); initializeNativeDataNative(); } - private native void initializeNativeDataNative(); protected void finalize() throws Throwable { try { @@ -101,20 +98,11 @@ class BluetoothEventLoop { super.finalize(); } } - private native void cleanupNativeDataNative(); - /* pacakge */ HashMap getRemoteServiceChannelCallbacks() { - return mGetRemoteServiceChannelCallbacks; - } - - /* pacakge */ HashMap getPasskeyAgentRequestData() { + /* package */ HashMap getPasskeyAgentRequestData() { return mPasskeyAgentRequestData; } - private native void startEventLoopNative(); - private native void stopEventLoopNative(); - private native boolean isEventLoopRunningNative(); - /* package */ void start() { if (!isEventLoopRunningNative()) { @@ -134,79 +122,37 @@ class BluetoothEventLoop { return isEventLoopRunningNative(); } - /*package*/ void onModeChanged(String bluezMode) { - int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode); - if (mode >= 0) { - Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.SCAN_MODE, mode); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + private void onDeviceFound(String address, String[] properties) { + if (properties == null) { + Log.e(TAG, "ERROR: Remote device properties are null"); + return; + } + mBluetoothService.addRemoteDeviceProperties(address, properties); + String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI"); + String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class"); + + if (rssi != null && classValue != null) { + Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.CLASS, classValue); + intent.putExtra(BluetoothIntent.RSSI, (short)Integer.valueOf(rssi).intValue()); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + } else { + log ("RSSI: " + rssi + " or ClassValue: " + classValue + + " for remote device: " + address + " is null"); } } - private void onDiscoveryStarted() { - mBluetoothService.setIsDiscovering(true); - Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onDiscoveryCompleted() { - mBluetoothService.setIsDiscovering(false); - Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } + private void onDeviceDisappeared(String address) { + mBluetoothService.removeRemoteDeviceProperties(address); - private void onRemoteDeviceFound(String address, int deviceClass, short rssi) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.CLASS, deviceClass); - intent.putExtra(BluetoothIntent.RSSI, rssi); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceDisappeared(String address) { Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_PERM); } - private void onRemoteClassUpdated(String address, int deviceClass) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.CLASS, deviceClass); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceConnected(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceDisconnectRequested(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteDeviceDisconnected(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteNameUpdated(String address, String name) { - Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.NAME, name); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteNameFailed(String address) { - Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onRemoteNameChanged(String address, String name) { - Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.NAME, name); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - private void onCreateBondingResult(String address, int result) { + private void onCreatePairedDeviceResult(String address, int result) { address = address.toUpperCase(); if (result == BluetoothError.SUCCESS) { mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED); @@ -259,23 +205,118 @@ class BluetoothEventLoop { mBluetoothService.getBondState().attempt(address); } - private void onBondingCreated(String address) { - mBluetoothService.getBondState().setBondState(address.toUpperCase(), - BluetoothDevice.BOND_BONDED); + private void onDeviceCreated(String deviceObjectPath) { + // do nothing. + return; } - private void onBondingRemoved(String address) { - mBluetoothService.getBondState().setBondState(address.toUpperCase(), - BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED); + private void onDeviceRemoved(String deviceObjectPath) { + String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); + if (address != null) + mBluetoothService.getBondState().setBondState(address.toUpperCase(), + BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED); } - private void onNameChanged(String name) { - Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.NAME, name); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); + /*package*/ void onPropertyChanged(String[] propValues) { + String name = propValues[0]; + if (name.equals("Name")) { + Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.NAME, propValues[1]); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setProperty(name, propValues[1]); + } else if (name.equals("Pairable") || name.equals("Discoverable")) { + String pairable = name.equals("Pairable") ? propValues[1] : + mBluetoothService.getProperty("Pairable"); + String discoverable = name.equals("Discoverable") ? propValues[1] : + mBluetoothService.getProperty("Discoverable"); + + // This shouldn't happen, unless Adapter Properties are null. + if (pairable == null || discoverable == null) + return; + + int mode = BluetoothDeviceService.bluezStringToScanMode( + pairable.equals("true"), + discoverable.equals("true")); + if (mode >= 0) { + Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); + intent.putExtra(BluetoothIntent.SCAN_MODE, mode); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + } + mBluetoothService.setProperty(name, propValues[1]); + } else if (name.equals("Discovering")) { + Intent intent; + if (propValues[1].equals("true")) { + mBluetoothService.setIsDiscovering(true); + intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION); + } else { + // Stop the discovery. + mBluetoothService.cancelDiscovery(); + mBluetoothService.setIsDiscovering(false); + intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION); + } + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setProperty(name, propValues[1]); + } else if (name.equals("Devices")) { + String value = ""; + for (int i = 1; i < propValues.length; i++) { + value = value + propValues[i] + ','; + } + mBluetoothService.setProperty(name, value.equals("") ? null : value); + } else if (name.equals("Powered")) { + // bluetoothd has restarted, re-read all our properties. + // Note: bluez only sends this property change when it restarts. + if (propValues[1].equals("true")) + onRestartRequired(); + } + } + + private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) { + String name = propValues[0]; + String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); + if (address == null) { + Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null"); + return; + } + if (name.equals("Name")) { + Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.NAME, propValues[1]); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); + } else if (name.equals("Class")) { + Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION); + intent.putExtra(BluetoothIntent.ADDRESS, address); + intent.putExtra(BluetoothIntent.CLASS, propValues[1]); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); + } else if (name.equals("Connected")) { + Intent intent = null; + if (propValues[1].equals("true")) { + intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); + } else { + intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION); + } + intent.putExtra(BluetoothIntent.ADDRESS, address); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]); + } else if (name.equals("UUIDs")) { + String uuid = "" ; + for (int i = 1; i < propValues.length; i++) { + uuid = uuid + propValues[i] + ","; + } + mBluetoothService.setRemoteDeviceProperty(address, name, + uuid.equals("") ? null : uuid); + } + } - private void onPasskeyAgentRequest(String address, int nativeData) { + private void onRequestPinCode(String objectPath, int nativeData) { + String address = mBluetoothService.getAddressFromObjectPath(objectPath); + if (address == null) { + Log.e(TAG, "Unable to get device address in onRequestPinCode, returning null"); + return; + } address = address.toUpperCase(); mPasskeyAgentRequestData.put(address, new Integer(nativeData)); @@ -309,21 +350,21 @@ class BluetoothEventLoop { Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + return; } - private void onPasskeyAgentCancel(String address) { - address = address.toUpperCase(); - mBluetoothService.cancelPin(address); - Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); - mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); - } + private boolean onAgentAuthorize(String objectPath, String deviceUuid) { + String address = mBluetoothService.getAddressFromObjectPath(objectPath); + if (address == null) { + Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize"); + return false; + } - private boolean onAuthAgentAuthorize(String address, String service, String uuid) { boolean authorized = false; - if (mBluetoothService.isEnabled() && service.endsWith("service_audio")) { + UUID uuid = UUID.fromString(deviceUuid); + if (mBluetoothService.isEnabled() && (BluetoothUuid.isAudioSink(uuid) || + BluetoothUuid.isAudioSource(uuid) || + BluetoothUuid.isAdvAudioDist(uuid))) { BluetoothA2dp a2dp = new BluetoothA2dp(mContext); authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF; if (authorized) { @@ -332,30 +373,21 @@ class BluetoothEventLoop { Log.i(TAG, "Rejecting incoming A2DP connection from " + address); } } else { - Log.i(TAG, "Rejecting incoming " + service + " connection from " + address); + Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address); } return authorized; } - private void onAuthAgentCancel(String address, String service, String uuid) { + private void onAgentCancel() { // We immediately response to DBUS Authorize() so this should not // usually happen - log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")"); - } - - private void onGetRemoteServiceChannelResult(String address, int channel) { - IBluetoothDeviceCallback callback = mGetRemoteServiceChannelCallbacks.get(address); - if (callback != null) { - mGetRemoteServiceChannelCallbacks.remove(address); - try { - callback.onGetRemoteServiceChannelResult(address, channel); - } catch (RemoteException e) {} - } + log("onAgentCancel"); } private void onRestartRequired() { if (mBluetoothService.isEnabled()) { - Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***"); + Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " + + "restarting Bluetooth ***"); mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH); } } @@ -363,4 +395,10 @@ class BluetoothEventLoop { private static void log(String msg) { Log.d(TAG, msg); } + + private native void initializeNativeDataNative(); + private native void startEventLoopNative(); + private native void stopEventLoopNative(); + private native boolean isEventLoopRunningNative(); + private native void cleanupNativeDataNative(); } diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp index ee672f7..8361212 100644 --- a/core/jni/android_bluetooth_common.cpp +++ b/core/jni/android_bluetooth_common.cpp @@ -36,6 +36,38 @@ namespace android { #ifdef HAVE_BLUETOOTH + +static Properties remote_device_properties[] = { + {"Address", DBUS_TYPE_STRING}, + {"Name", DBUS_TYPE_STRING}, + {"Icon", DBUS_TYPE_STRING}, + {"Class", DBUS_TYPE_UINT32}, + {"UUIDs", DBUS_TYPE_ARRAY}, + {"Paired", DBUS_TYPE_BOOLEAN}, + {"Connected", DBUS_TYPE_BOOLEAN}, + {"Trusted", DBUS_TYPE_BOOLEAN}, + {"Alias", DBUS_TYPE_STRING}, + {"Nodes", DBUS_TYPE_ARRAY}, + {"Adapter", DBUS_TYPE_OBJECT_PATH}, + {"LegacyPairing", DBUS_TYPE_BOOLEAN}, + {"RSSI", DBUS_TYPE_INT16}, + {"TX", DBUS_TYPE_UINT32} +}; + +static Properties adapter_properties[] = { + {"Address", DBUS_TYPE_STRING}, + {"Name", DBUS_TYPE_STRING}, + {"Class", DBUS_TYPE_UINT32}, + {"Powered", DBUS_TYPE_BOOLEAN}, + {"Discoverable", DBUS_TYPE_BOOLEAN}, + {"DiscoverableTimeout", DBUS_TYPE_UINT32}, + {"Pairable", DBUS_TYPE_BOOLEAN}, + {"PairableTimeout", DBUS_TYPE_UINT32}, + {"Discovering", DBUS_TYPE_BOOLEAN}, + {"Devices", DBUS_TYPE_ARRAY}, +}; + + jfieldID get_field(JNIEnv *env, jclass clazz, const char *member, const char *mtype) { jfieldID field = env->GetFieldID(clazz, member, mtype); @@ -332,6 +364,44 @@ jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply) { return ret; } +static void set_object_array_element(JNIEnv *env, jobjectArray strArray, + const char *value, int index) { + jstring obj; + obj = env->NewStringUTF(value); + env->SetObjectArrayElement(strArray, index, obj); + env->DeleteLocalRef(obj); +} + +jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, + DBusMessage *reply) { + + DBusError err; + char **list; + int i, len; + jobjectArray strArray = NULL; + + dbus_error_init(&err); + if (dbus_message_get_args (reply, + &err, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, + &list, &len, + DBUS_TYPE_INVALID)) { + jclass stringClass; + jstring classNameStr; + + stringClass = env->FindClass("java/lang/String"); + strArray = env->NewObjectArray(len, stringClass, NULL); + + for (i = 0; i < len; i++) + set_object_array_element(env, strArray, list[i], i); + } else { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + } + + dbus_message_unref(reply); + return strArray; +} + jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) { DBusError err; @@ -353,11 +423,8 @@ jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) { stringClass = env->FindClass("java/lang/String"); strArray = env->NewObjectArray(len, stringClass, NULL); - for (i = 0; i < len; i++) { - //LOGV("%s: array[%d] = [%s]", __FUNCTION__, i, list[i]); - env->SetObjectArrayElement(strArray, i, - env->NewStringUTF(list[i])); - } + for (i = 0; i < len; i++) + set_object_array_element(env, strArray, list[i], i); } else { LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); } @@ -390,6 +457,292 @@ jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply) { return byteArray; } +void append_variant(DBusMessageIter *iter, int type, void *val) +{ + DBusMessageIter value_iter; + char var_type[2] = { type, '\0'}; + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_type, &value_iter); + dbus_message_iter_append_basic(&value_iter, type, val); + dbus_message_iter_close_container(iter, &value_iter); +} + + +//TODO(): Remove code duplication between parse_properties and +//parse_property_change +jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, + const int max_num_properties) { + DBusMessageIter dict_entry, dict, prop_val, device_val, array_val_iter; + jobjectArray strArray = NULL; + char * property; + char values[max_num_properties][256]; + char **uuid_array = NULL; + char **device_path = NULL; + char **array_elements = NULL; + char *string_val; + uint32_t int_val, bool_val; + int i, j, k, type, array_type, num_array_elements = 0; + int ret, num_properties = 0, num_uuids = 0, num_devices = 0; + + + jclass stringClass = env->FindClass("java/lang/String"); + DBusError err; + dbus_error_init(&err); + + for (i = 0; i < max_num_properties; i++) + memset(values[i], '\0', 128 * sizeof(char)); + + if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + goto failure; + dbus_message_iter_recurse(iter, &dict); + do { + if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY) + goto failure; + dbus_message_iter_recurse(&dict, &dict_entry); + if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_STRING) + goto failure; + dbus_message_iter_get_basic(&dict_entry, &property); + if (!dbus_message_iter_next(&dict_entry)) + goto failure; + if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_VARIANT) + goto failure; + + for (i = 0; i < max_num_properties; i++) { + if (!strncmp(properties[i].name, property, strlen(property))) { + num_properties ++; + break; + } + } + if (i == max_num_properties) + goto failure; + + type = properties[i].type; + dbus_message_iter_recurse(&dict_entry, &prop_val); + if(dbus_message_iter_get_arg_type(&prop_val) != type) { + LOGE("Property type mismatch in parse_properties: %d, expected:%d", + dbus_message_iter_get_arg_type(&prop_val), type); + goto failure; + } + + switch(type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + dbus_message_iter_get_basic(&prop_val, &string_val); + strcpy(values[i], string_val); + break; + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT16: + dbus_message_iter_get_basic(&prop_val, &int_val); + sprintf(values[i], "%d", int_val); + break; + case DBUS_TYPE_BOOLEAN: + dbus_message_iter_get_basic(&prop_val, &bool_val); + sprintf(values[i], "%s", bool_val ? "true" : "false"); + break; + case DBUS_TYPE_ARRAY: + dbus_message_iter_recurse(&prop_val, &array_val_iter); + array_type = dbus_message_iter_get_arg_type(&array_val_iter); + num_array_elements = 0; + if (array_type == DBUS_TYPE_OBJECT_PATH || + array_type == DBUS_TYPE_STRING){ + do { + num_array_elements++; + } while(dbus_message_iter_next(&array_val_iter)); + dbus_message_iter_recurse(&prop_val, &array_val_iter); + // Allocate an array + array_elements = (char **)malloc(sizeof(char *) * + num_array_elements); + if (!array_elements) + goto failure; + + j = 0; + do { + dbus_message_iter_get_basic(&array_val_iter, &array_elements[j]); + j++; + } while(dbus_message_iter_next(&array_val_iter)); + if (!strncmp(property, "UUIDs", strlen("UUIDs"))) { + num_uuids = num_array_elements; + uuid_array = array_elements; + } else { + num_devices = num_array_elements; + device_path = array_elements; + } + } + break; + default: + goto failure; + } + } while(dbus_message_iter_next(&dict)); + + // Convert it to a array of strings. + strArray = env->NewObjectArray((num_properties + num_array_elements) * 2, + stringClass, NULL); + + j = 0; + for (i = 0; i < max_num_properties; i++) { + if (properties[i].type == DBUS_TYPE_ARRAY) { + if (!strncmp(properties[i].name, "UUIDs", strlen("UUIDs"))) { + num_array_elements = num_uuids; + array_elements = uuid_array; + } else { + num_array_elements = num_devices; + array_elements = device_path; + } + + for (k = 0; k < num_array_elements; k++) { + set_object_array_element(env, strArray, properties[i].name, j++); + set_object_array_element(env, strArray, array_elements[k], j++); + } + } else if (values[i][0] != '\0') { + set_object_array_element(env, strArray, properties[i].name, j++); + set_object_array_element(env, strArray, values[i], j++); + } + } + + if (uuid_array) + free(uuid_array); + if (device_path) + free(device_path); + return strArray; + +failure: + if (dbus_error_is_set(&err)) + LOG_AND_FREE_DBUS_ERROR(&err); + if (uuid_array) + free(uuid_array); + if (device_path) + free(device_path); + return NULL; +} + +jobjectArray create_prop_array(JNIEnv *env, Properties *properties, + int prop_index, void *value, int len ) { + jclass stringClass= env->FindClass("java/lang/String"); + char **prop_val = NULL; + char buf[32] = {'\0'}; + int i, j; + + jobjectArray strArray = env->NewObjectArray(1 + len, stringClass, NULL); + j = 0; + set_object_array_element(env, strArray, properties[prop_index].name, j++); + + if (properties[prop_index].type == DBUS_TYPE_UINT32) { + sprintf(buf, "%d", *(int *) value); + set_object_array_element(env, strArray, buf, j++); + } else if (properties[prop_index].type == DBUS_TYPE_BOOLEAN) { + sprintf(buf, "%s", *(int *) value ? "true" : "false"); + set_object_array_element(env, strArray, buf, j++); + } else if (properties[prop_index].type == DBUS_TYPE_ARRAY) { + prop_val = (char **) value; + for (i = 0; i < len; i++) + set_object_array_element(env, strArray, prop_val[i], j++); + } else { + set_object_array_element(env, strArray, (const char *) value, j++); + } + if (prop_val) + free (prop_val); + return strArray; +} + +jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, + Properties *properties, int max_num_properties) { + DBusMessageIter iter, prop_val, array_val_iter; + DBusError err; + void *value; + char *property; + uint32_t array_type; + int i, j, type, len, prop_index; + + dbus_error_init(&err); + if (!dbus_message_iter_init(msg, &iter)) + goto failure; + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto failure; + dbus_message_iter_get_basic(&iter, &property); + if (!dbus_message_iter_next(&iter)) + goto failure; + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + goto failure; + for (i = 0; i < max_num_properties; i++) { + if (!strncmp(property, properties[i].name, strlen(properties[i].name))) + break; + } + prop_index = i; + if (i == max_num_properties) + goto failure; + + dbus_message_iter_recurse(&iter, &prop_val); + type = properties[prop_index].type; + if (dbus_message_iter_get_arg_type(&prop_val) != type) { + LOGE("Property type mismatch in parse_properties: %d, expected:%d", + dbus_message_iter_get_arg_type(&prop_val), type); + goto failure; + } + + switch(type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + dbus_message_iter_get_basic(&prop_val, &value); + len = 1; + break; + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT16: + case DBUS_TYPE_BOOLEAN: + uint32_t int_val; + dbus_message_iter_get_basic(&prop_val, &int_val); + value = &int_val; + len = 1; + break; + case DBUS_TYPE_ARRAY: + dbus_message_iter_recurse(&prop_val, &array_val_iter); + array_type = dbus_message_iter_get_arg_type(&array_val_iter); + len = 0; + if (array_type == DBUS_TYPE_OBJECT_PATH || + array_type == DBUS_TYPE_STRING){ + do { + len ++; + } while(dbus_message_iter_next(&array_val_iter)); + dbus_message_iter_recurse(&prop_val, &array_val_iter); + // Allocate an array of char * + char **tmp = (char **)malloc(sizeof(char *) * len); + if (!tmp) + goto failure; + j = 0; + do { + dbus_message_iter_get_basic(&array_val_iter, &tmp[j]); + j ++; + } while(dbus_message_iter_next(&array_val_iter)); + value = (char **) tmp; + } + break; + default: + goto failure; + } + return create_prop_array(env, properties, prop_index, value, len); +failure: + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return NULL; +} + +jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) { + return parse_property_change(env, msg, (Properties *) &adapter_properties, + sizeof(adapter_properties) / sizeof(Properties)); +} + +jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg) { + return parse_property_change(env, msg, (Properties *) &remote_device_properties, + sizeof(remote_device_properties) / sizeof(Properties)); +} + +jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) { + return parse_properties(env, iter, (Properties *) &adapter_properties, + sizeof(adapter_properties) / sizeof(Properties)); +} + +jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter) { + return parse_properties(env, iter, (Properties *) &remote_device_properties, + sizeof(remote_device_properties) / sizeof(Properties)); +} + int get_bdaddr(const char *str, bdaddr_t *ba) { char *d = ((char *)ba) + 5, *endp; int i; diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h index e5b8813..ef9b66b 100644 --- a/core/jni/android_bluetooth_common.h +++ b/core/jni/android_bluetooth_common.h @@ -68,6 +68,7 @@ jfieldID get_field(JNIEnv *env, struct event_loop_native_data_t { DBusConnection *conn; + const char *adapter; /* protects the thread */ pthread_mutex_t thread_mutex; @@ -89,6 +90,12 @@ struct event_loop_native_data_t { jobject me; }; +struct _Properties { + char name[32]; + int type; +}; +typedef struct _Properties Properties; + dbus_bool_t dbus_func_args_async(JNIEnv *env, DBusConnection *conn, int timeout_ms, @@ -142,8 +149,18 @@ jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply); jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply); jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply); jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply); +jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, DBusMessage *reply); jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply); +jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, + const int max_num_properties); +jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, + Properties *properties, int max_num_properties); +jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter); +jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter); +jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg); +jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg); +void append_variant(DBusMessageIter *iter, int type, void *val); int get_bdaddr(const char *str, bdaddr_t *ba); void get_bdaddr_as_string(const bdaddr_t *ba, char *str); diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index fe94642..d85d8c1 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -37,12 +37,7 @@ namespace android { #ifdef HAVE_BLUETOOTH -static jmethodID method_onHeadsetCreated; -static jmethodID method_onHeadsetRemoved; -static jmethodID method_onSinkConnected; -static jmethodID method_onSinkDisconnected; -static jmethodID method_onSinkPlaying; -static jmethodID method_onSinkStopped; +static jmethodID method_onSinkPropertyChanged; typedef struct { JavaVM *vm; @@ -53,11 +48,11 @@ typedef struct { static native_data_t *nat = NULL; // global native data -#endif - -#ifdef HAVE_BLUETOOTH -static void onConnectSinkResult(DBusMessage *msg, void *user, void *nat); -static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *nat); +static Properties sink_properties[] = { + {"State", DBUS_TYPE_STRING}, + {"Connected", DBUS_TYPE_BOOLEAN}, + {"Playing", DBUS_TYPE_BOOLEAN}, + }; #endif /* Returns true on success (even if adapter is present but disabled). @@ -99,91 +94,58 @@ static void cleanupNative(JNIEnv* env, jobject object) { } #endif } -static jobjectArray listHeadsetsNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "ListHeadsets", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} -static jstring createHeadsetNative(JNIEnv *env, jobject object, - jstring address) { +static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object, + jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s\n", c_address); - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "CreateHeadset", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); -static jstring removeHeadsetNative(JNIEnv *env, jobject object, jstring path) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "RemoveHeadset", - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID); + reply = dbus_func_args_timeout(env, + nat->conn, -1, c_path, + "org.bluez.AudioSink", "GetProperties", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_string(env, reply) : NULL; + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return NULL; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + DBusMessageIter iter; + if (dbus_message_iter_init(reply, &iter)) + return parse_properties(env, &iter, (Properties *)&sink_properties, + sizeof(sink_properties) / sizeof(Properties)); } #endif return NULL; } -static jstring getAddressNative(JNIEnv *env, jobject object, jstring path) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { - const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, c_path, - "org.bluez.audio.Device", "GetAddress", - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - size_t path_sz = env->GetStringUTFLength(path) + 1; - char *c_path_copy = (char *)malloc(path_sz); // callback data - strncpy(c_path_copy, c_path, path_sz); - - bool ret = - dbus_func_args_async(env, nat->conn, -1, - onConnectSinkResult, (void *)c_path_copy, nat, - c_path, - "org.bluez.audio.Sink", "Connect", - DBUS_TYPE_INVALID); + DBusError err; + dbus_error_init(&err); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, c_path, + "org.bluez.AudioSink", "Connect", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - if (!ret) { - free(c_path_copy); + + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return JNI_FALSE; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); return JNI_FALSE; } return JNI_TRUE; @@ -198,19 +160,20 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object, LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - size_t path_sz = env->GetStringUTFLength(path) + 1; - char *c_path_copy = (char *)malloc(path_sz); // callback data - strncpy(c_path_copy, c_path, path_sz); - - bool ret = - dbus_func_args_async(env, nat->conn, -1, - onDisconnectSinkResult, (void *)c_path_copy, nat, - c_path, - "org.bluez.audio.Sink", "Disconnect", - DBUS_TYPE_INVALID); + DBusError err; + dbus_error_init(&err); + + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, c_path, + "org.bluez.AudioSink", "Disconnect", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - if (!ret) { - free(c_path_copy); + + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return JNI_FALSE; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); return JNI_FALSE; } return JNI_TRUE; @@ -219,94 +182,8 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object, return JNI_FALSE; } -static jboolean isSinkConnectedNative(JNIEnv *env, jobject object, jstring path) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { - const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, c_path, - "org.bluez.audio.Sink", "IsConnected", - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -#ifdef HAVE_BLUETOOTH -static void onConnectSinkResult(DBusMessage *msg, void *user, void *natData) { - LOGV(__FUNCTION__); - - char *c_path = (char *)user; - DBusError err; - JNIEnv *env; - - if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { - LOGE("%s: error finding Env for our VM\n", __FUNCTION__); - return; - } - - dbus_error_init(&err); - - LOGV("... path = %s", c_path); - if (dbus_set_error_from_message(&err, msg)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - dbus_error_free(&err); - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - if (env->ExceptionCheck()) { - LOGE("VM Exception occurred in native function %s (%s:%d)", - __FUNCTION__, __FILE__, __LINE__); - } - } // else Java callback is triggered by signal in a2dp_event_filter - - free(c_path); -} - -static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *natData) { - LOGV(__FUNCTION__); - - char *c_path = (char *)user; - DBusError err; - JNIEnv *env; - - if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { - LOGE("%s: error finding Env for our VM\n", __FUNCTION__); - return; - } - - dbus_error_init(&err); - - LOGV("... path = %s", c_path); - if (dbus_set_error_from_message(&err, msg)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - if (strcmp(err.name, "org.bluez.Error.NotConnected") == 0) { - // we were already disconnected, so report disconnect - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - } else { - // Assume it is still connected - env->CallVoidMethod(nat->me, - method_onSinkConnected, - env->NewStringUTF(c_path)); - } - dbus_error_free(&err); - if (env->ExceptionCheck()) { - LOGE("VM Exception occurred in native function %s (%s:%d)", - __FUNCTION__, __FILE__, __LINE__); - } - } // else Java callback is triggered by signal in a2dp_event_filter - - free(c_path); -} - DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { +#ifdef HAVE_BLUETOOTH DBusError err; if (!nat) { @@ -323,71 +200,23 @@ DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (dbus_message_is_signal(msg, - "org.bluez.audio.Manager", - "HeadsetCreated")) { - char *c_path; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID)) { - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onHeadsetCreated, - env->NewStringUTF(c_path)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Manager", - "HeadsetRemoved")) { - char *c_path; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID)) { - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onHeadsetRemoved, - env->NewStringUTF(c_path)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Connected")) { + if (dbus_message_is_signal(msg, "org.bluez.AudioSink", + "PropertyChanged")) { + jobjectArray str_array = + parse_property_change(env, msg, (Properties *)&sink_properties, + sizeof(sink_properties) / sizeof(Properties)); const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); env->CallVoidMethod(nat->me, - method_onSinkConnected, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Disconnected")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Playing")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkPlaying, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Stopped")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkStopped, - env->NewStringUTF(c_path)); + method_onSinkPropertyChanged, + env->NewStringUTF(c_path), + str_array); + for (int i = 0; i < env->GetArrayLength(str_array); i++) { + env->DeleteLocalRef(env->GetObjectArrayElement(str_array, i)); + } + env->DeleteLocalRef(str_array); result = DBUS_HANDLER_RESULT_HANDLED; - } - - if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) { + return result; + } else { LOGV("... ignored"); } if (env->ExceptionCheck()) { @@ -406,14 +235,11 @@ static JNINativeMethod sMethods[] = { {"initNative", "()Z", (void *)initNative}, {"cleanupNative", "()V", (void *)cleanupNative}, - /* Bluez audio 3.36 API */ - {"listHeadsetsNative", "()[Ljava/lang/String;", (void*)listHeadsetsNative}, - {"createHeadsetNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)createHeadsetNative}, - {"removeHeadsetNative", "(Ljava/lang/String;)Z", (void*)removeHeadsetNative}, - {"getAddressNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAddressNative}, - {"connectSinkNative", "(Ljava/lang/String;)Z", (void*)connectSinkNative}, - {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void*)disconnectSinkNative}, - {"isSinkConnectedNative", "(Ljava/lang/String;)Z", (void*)isSinkConnectedNative}, + /* Bluez audio 4.40 API */ + {"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative}, + {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative}, + {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", + (void *)getSinkPropertiesNative}, }; int register_android_server_BluetoothA2dpService(JNIEnv *env) { @@ -424,12 +250,8 @@ int register_android_server_BluetoothA2dpService(JNIEnv *env) { } #ifdef HAVE_BLUETOOTH - method_onHeadsetCreated = env->GetMethodID(clazz, "onHeadsetCreated", "(Ljava/lang/String;)V"); - method_onHeadsetRemoved = env->GetMethodID(clazz, "onHeadsetRemoved", "(Ljava/lang/String;)V"); - method_onSinkConnected = env->GetMethodID(clazz, "onSinkConnected", "(Ljava/lang/String;)V"); - method_onSinkDisconnected = env->GetMethodID(clazz, "onSinkDisconnected", "(Ljava/lang/String;)V"); - method_onSinkPlaying = env->GetMethodID(clazz, "onSinkPlaying", "(Ljava/lang/String;)V"); - method_onSinkStopped = env->GetMethodID(clazz, "onSinkStopped", "(Ljava/lang/String;)V"); + method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); #endif return AndroidRuntime::registerNativeMethods(env, diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index b6e9798..4be4761 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -14,7 +14,8 @@ ** limitations under the License. */ -#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter" +#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter" +#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device" #define LOG_TAG "BluetoothDeviceService.cpp" #include "android_bluetooth_common.h" @@ -60,8 +61,11 @@ typedef struct { extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *, jobject); -void onCreateBondingResult(DBusMessage *msg, void *user, void *nat); -void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *nat); +extern DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); +void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat); + /** Get native data stored in the opaque (Java code maintained) pointer mNativeData * Perform quick sanity check, if there are any problems return NULL @@ -109,36 +113,67 @@ static bool initializeNativeDataNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } - - nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME; #endif /*HAVE_BLUETOOTH*/ return true; } -static void cleanupNativeDataNative(JNIEnv* env, jobject object) { +static const char *get_adapter_path(JNIEnv* env, jobject object) { + event_loop_native_data_t *event_nat = + get_EventLoop_native_data(env, env->GetObjectField(object, + field_mEventLoop)); + if (event_nat == NULL) + return NULL; + return event_nat->adapter; +} + +// This function is called when the adapter is enabled. +static jboolean setupNativeDataNative(JNIEnv* env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = (native_data_t *)env->GetIntField(object, field_mNativeData); - if (nat) { - free(nat); - nat = NULL; + event_loop_native_data_t *event_nat = + get_EventLoop_native_data(env, env->GetObjectField(object, + field_mEventLoop)); + // Register agent for remote devices. + const char *device_agent_path = "/android/bluetooth/remote_device_agent"; + static const DBusObjectPathVTable agent_vtable = { + NULL, agent_event_filter, NULL, NULL, NULL, NULL }; + + if (!dbus_connection_register_object_path(nat->conn, device_agent_path, + &agent_vtable, event_nat)) { + LOGE("%s: Can't register object path %s for remote device agent!", + __FUNCTION__, device_agent_path); + return JNI_FALSE; } -#endif +#endif /*HAVE_BLUETOOTH*/ + return JNI_TRUE; } -static jstring getNameNative(JNIEnv *env, jobject object){ +static jboolean tearDownNativeDataNative(JNIEnv *env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + if (nat != NULL) { + const char *device_agent_path = + "/android/bluetooth/remote_device_agent"; + dbus_connection_unregister_object_path (nat->conn, device_agent_path); + } +#endif /*HAVE_BLUETOOTH*/ + return JNI_TRUE; +} + +static void cleanupNativeDataNative(JNIEnv* env, jobject object) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); if (nat) { - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetName", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_string(env, reply) : NULL; + free(nat); + nat = NULL; } #endif - return NULL; } static jstring getAdapterPathNative(JNIEnv *env, jobject object) { @@ -146,7 +181,7 @@ static jstring getAdapterPathNative(JNIEnv *env, jobject object) { #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); if (nat) { - return (env->NewStringUTF(nat->adapter)); + return (env->NewStringUTF(get_adapter_path(env, object))); } #endif return NULL; @@ -170,28 +205,23 @@ static jboolean startDiscoveryNative(JNIEnv *env, jobject object) { dbus_error_init(&err); /* Compose the command */ - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "DiscoverDevices"); + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "StartDiscovery"); if (msg == NULL) { - LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } goto done; } /* Send the command. */ reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); if (dbus_error_is_set(&err)) { - /* We treat the in-progress error code as success. */ - if(strcmp(err.message, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { - LOGW("%s: D-Bus error: %s, treating as startDiscoveryNative success\n", - __FUNCTION__, err.message); - ret = JNI_TRUE; - goto done; - } else { - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - ret = JNI_FALSE; - goto done; - } + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + ret = JNI_FALSE; + goto done; } ret = JNI_TRUE; @@ -204,7 +234,7 @@ done: #endif } -static void cancelDiscoveryNative(JNIEnv *env, jobject object) { +static void stopDiscoveryNative(JNIEnv *env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH DBusMessage *msg = NULL; @@ -222,18 +252,20 @@ static void cancelDiscoveryNative(JNIEnv *env, jobject object) { } /* Compose the command */ - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "CancelDiscovery"); - + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "StopDiscovery"); if (msg == NULL) { - LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); + if (dbus_error_is_set(&err)) + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); goto done; } /* Send the command. */ reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); if (dbus_error_is_set(&err)) { - if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized") == 0) { + if(strncmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized", + strlen(BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized")) == 0) { // hcid sends this if there is no active discovery to cancel LOGV("%s: There was no active discovery to cancel", __FUNCTION__); dbus_error_free(&err); @@ -248,232 +280,8 @@ done: #endif } -static jboolean startPeriodicDiscoveryNative(JNIEnv *env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - DBusMessage *msg = NULL; - DBusMessage *reply = NULL; - DBusError err; - jboolean ret = JNI_FALSE; - - native_data_t *nat = get_native_data(env, object); - if (nat == NULL) { - goto done; - } - - dbus_error_init(&err); - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "StartPeriodicDiscovery"); - if (msg == NULL) { - LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); - goto done; - } - - /* Send the command. */ - reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); - if (dbus_error_is_set(&err)) { - /* We treat the in-progress error code as success. */ - if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { - LOGW("%s: D-Bus error: %s (%s), treating as " - "startPeriodicDiscoveryNative success\n", - __FUNCTION__, err.name, err.message); - } else { - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - ret = JNI_FALSE; - goto done; - } - } - - ret = JNI_TRUE; -done: - if (reply) dbus_message_unref(reply); - if (msg) dbus_message_unref(msg); - return ret; -#else - return JNI_FALSE; -#endif -} - -static jboolean stopPeriodicDiscoveryNative(JNIEnv *env, jobject object) { - LOGV(__FUNCTION__); -#ifdef HAVE_BLUETOOTH - DBusMessage *msg = NULL; - DBusMessage *reply = NULL; - DBusError err; - const char *name; - jboolean ret = JNI_FALSE; - - native_data_t *nat = get_native_data(env, object); - if (nat == NULL) { - goto done; - } - - dbus_error_init(&err); - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "StopPeriodicDiscovery"); - if (msg == NULL) { - LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); - goto done; - } - - /* Send the command. */ - reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); - if (dbus_error_is_set(&err)) { - /* We treat the in-progress error code as success. */ - if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { - LOGW("%s: D-Bus error: %s (%s), treating as " - "stopPeriodicDiscoveryNative success\n", - __FUNCTION__, err.name, err.message); - } else { - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - ret = JNI_FALSE; - goto done; - } - } - - ret = JNI_TRUE; -done: - if (reply) dbus_message_unref(reply); - if (msg) dbus_message_unref(msg); - return ret; -#else - return JNI_FALSE; -#endif -} - -static jboolean isPeriodicDiscoveryNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsPeriodicDiscovery", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean setDiscoverableTimeoutNative(JNIEnv *env, jobject object, jint timeout_s) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - - if (timeout_s < 0) { - return JNI_FALSE; - } - - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetDiscoverableTimeout", - DBUS_TYPE_UINT32, &timeout_s, - DBUS_TYPE_INVALID); - if (reply != NULL) { - dbus_message_unref(reply); - return JNI_TRUE; - } - } -#endif - return JNI_FALSE; -} - -static jint getDiscoverableTimeoutNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetDiscoverableTimeout", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_uint32(env, reply) : -1; - } -#endif - return -1; -} - -static jboolean isConnectedNative(JNIEnv *env, jobject object, jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "IsConnected", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - // Set a timeout of 5 seconds. Specifying the default timeout is - // not long enough, as a remote-device disconnect results in - // signal RemoteDisconnectRequested being sent, followed by a - // delay of 2 seconds, after which the actual disconnect takes - // place. - DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, 60000, nat->adapter, - DBUS_CLASS_NAME, "DisconnectRemoteDevice", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) dbus_message_unref(reply); - } -#endif -} - -static jstring getModeNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetMode", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} - -static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_mode = env->GetStringUTFChars(mode, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetMode", - DBUS_TYPE_STRING, &c_mode, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(mode, c_mode); - if (reply) { - dbus_message_unref(reply); - return JNI_TRUE; - } - return JNI_FALSE; - } -#endif - return JNI_FALSE; -} - -static jboolean createBondingNative(JNIEnv *env, jobject object, - jstring address, jint timeout_ms) { +static jboolean createPairedDeviceNative(JNIEnv *env, jobject object, + jstring address, jint timeout_ms) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); @@ -485,14 +293,20 @@ static jboolean createBondingNative(JNIEnv *env, jobject object, const char *c_address = env->GetStringUTFChars(address, NULL); LOGV("... address = %s", c_address); char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); + const char *capabilities = "DisplayYesNo"; + const char *agent_path = "/android/bluetooth/remote_device_agent"; + strlcpy(context_address, c_address, BTADDR_SIZE); // for callback bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms, - onCreateBondingResult, // callback + onCreatePairedDeviceResult, // callback context_address, eventLoopNat, - nat->adapter, - DBUS_CLASS_NAME, "CreateBonding", + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, + "CreatePairedDevice", DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_STRING, &capabilities, DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(address, c_address); return ret ? JNI_TRUE : JNI_FALSE; @@ -502,396 +316,98 @@ static jboolean createBondingNative(JNIEnv *env, jobject object, return JNI_FALSE; } -static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object, - jstring address) { - LOGV(__FUNCTION__); +static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object, + jstring path, + jstring pattern, jint attr_id) { #ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s", c_address); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + if (nat && eventLoopNat) { + const char *c_pattern = env->GetStringUTFChars(pattern, NULL); + const char *c_path = env->GetStringUTFChars(path, NULL); + LOGV("... pattern = %s", c_pattern); + LOGV("... attr_id = %#X", attr_id); DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, - DBUS_CLASS_NAME, "CancelBondingProcess", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) { - dbus_message_unref(reply); - } - return JNI_TRUE; + dbus_func_args(env, nat->conn, c_path, + DBUS_DEVICE_IFACE, "GetServiceAttributeValue", + DBUS_TYPE_STRING, &c_pattern, + DBUS_TYPE_UINT16, &attr_id, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(pattern, c_pattern); + env->ReleaseStringUTFChars(path, c_path); + return reply ? dbus_returns_int32(env, reply) : -1; } #endif - return JNI_FALSE; + return -1; } -static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) { +static jboolean cancelDeviceCreationNative(JNIEnv *env, jobject object, + jstring address) { LOGV(__FUNCTION__); jboolean result = JNI_FALSE; #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); if (nat) { const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s", c_address); DBusError err; dbus_error_init(&err); + LOGV("... address = %s", c_address); DBusMessage *reply = - dbus_func_args_error(env, nat->conn, &err, nat->adapter, - DBUS_CLASS_NAME, "RemoveBonding", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - if (dbus_error_is_set(&err)) { - if (dbus_error_has_name(&err, - BLUEZ_DBUS_BASE_IFC ".Error.DoesNotExist")) { - LOGW("%s: Warning: %s (%s)", __FUNCTION__, err.message, - c_address); - result = JNI_TRUE; - } else { - LOGE("%s: D-Bus error %s (%s)", __FUNCTION__, err.name, - err.message); - } - } else { - result = JNI_TRUE; - } - + dbus_func_args_timeout(env, nat->conn, -1, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "CancelDeviceCreation", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(address, c_address); - dbus_error_free(&err); - if (reply) dbus_message_unref(reply); - } -#endif - return result; -} - -static jobjectArray listBondingsNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ListBondings", - DBUS_TYPE_INVALID); - // return String[] - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - -static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ListConnections", - DBUS_TYPE_INVALID); - // return String[] - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - -static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "ListRemoteDevices", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} - -static jstring common_Get(JNIEnv *env, jobject object, const char *func) { - LOGV("%s:%s", __FUNCTION__, func); -#ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); - if (nat) { - DBusError err; - dbus_error_init(&err); - DBusMessage *reply = - dbus_func_args_error(env, nat->conn, &err, nat->adapter, - DBUS_CLASS_NAME, func, - DBUS_TYPE_INVALID); - if (reply) { - return dbus_returns_string(env, reply); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return JNI_FALSE; } else { - LOG_AND_FREE_DBUS_ERROR(&err); - return NULL; - } - } -#endif - return NULL; -} - -static jstring getAddressNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetAddress"); -} - -static jstring getVersionNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetVersion"); -} - -static jstring getRevisionNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetRevision"); -} - -static jstring getManufacturerNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetManufacturer"); -} - -static jstring getCompanyNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetCompany"); -} - -static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, obj); - if (nat) { - const char *c_name = env->GetStringUTFChars(name, NULL); - DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "SetName", - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(name, c_name); - if (reply) { - dbus_message_unref(reply); - return JNI_TRUE; + result = JNI_TRUE; } + dbus_message_unref(reply); } #endif return JNI_FALSE; } -static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, - jstring address) { - LOGV("%s:%s", __FUNCTION__, func); +static jboolean removeDeviceNative(JNIEnv *env, jobject object, jstring object_path) { + LOGV(__FUNCTION__); + jboolean result = JNI_FALSE; #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); + const char *c_object_path = env->GetStringUTFChars(object_path, NULL); DBusError err; dbus_error_init(&err); - - LOGV("... address = %s", c_address); - DBusMessage *reply = - dbus_func_args_error(env, nat->conn, &err, nat->adapter, - DBUS_CLASS_NAME, func, - DBUS_TYPE_STRING, &c_address, + dbus_func_args_error(env, nat->conn, &err, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "RemoveDevice", + DBUS_TYPE_OBJECT_PATH, &c_object_path, DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) { - return dbus_returns_string(env, reply); - } else if (!strcmp(func, "GetRemoteName") && - dbus_error_has_name(&err, "org.bluez.Error.RequestDeferred")) { - // This error occurs if we request name during device discovery, - // its fine - LOGV("... %s: %s", func, err.message); - dbus_error_free(&err); - return NULL; + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + result = JNI_FALSE; } else { - LOG_AND_FREE_DBUS_ERROR(&err); - return NULL; - } - } -#endif - return NULL; -} - -static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteVersion", address); -} - -static jstring getRemoteRevisionNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteRevision", address); -} - -static jstring getRemoteManufacturerNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteManufacturer", address); -} - -static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteCompany", address); -} - -static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "GetRemoteName", address); -} - -static jstring lastSeenNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "LastSeen", address); -} - -static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) { - return common_getRemote(env, obj, "LastUsed", address); -} - -static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { - jint result = BLUETOOTH_CLASS_ERROR; -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteClass", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - if (reply) - { - DBusError err; - dbus_error_init(&err); - if (!dbus_message_get_args(reply, &err, - DBUS_TYPE_UINT32, &result, - DBUS_TYPE_INVALID)) { - LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); - } - dbus_message_unref(reply); + result = JNI_TRUE; } + env->ReleaseStringUTFChars(object_path, c_object_path); + if (reply) dbus_message_unref(reply); } #endif return result; } -static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object, - jstring address) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteFeatures", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - /* array of DBUS_TYPE_BYTE_AS_STRING */ - return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; - } -#endif - return NULL; -} - -static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object, - jstring address, jstring match) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - jintArray intArray = NULL; - const char *c_address = env->GetStringUTFChars(address, NULL); - const char *c_match = env->GetStringUTFChars(match, NULL); - - LOGV("... address = %s match = %s", c_address, c_match); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceHandles", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_match, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - env->ReleaseStringUTFChars(match, c_match); - if (reply) - { - DBusError err; - jint *list; - int i, len; - - dbus_error_init(&err); - if (dbus_message_get_args (reply, &err, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, - &list, &len, - DBUS_TYPE_INVALID)) { - if (len) { - intArray = env->NewIntArray(len); - if (intArray) - env->SetIntArrayRegion(intArray, 0, len, list); - } - } else { - LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); - } - - dbus_message_unref(reply); - } - return intArray; - } -#endif - return NULL; -} - -static jbyteArray getRemoteServiceRecordNative(JNIEnv *env, jobject object, - jstring address, jint handle) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - - LOGV("... address = %s", c_address); - - DBusMessage *reply = - dbus_func_args(env, nat->conn, nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceRecord", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; - } -#endif - return NULL; -} - -static jboolean getRemoteServiceChannelNative(JNIEnv *env, jobject object, - jstring address, jshort uuid16) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, object); - jobject eventLoop = env->GetObjectField(object, field_mEventLoop); - struct event_loop_native_data_t *eventLoopNat = - get_EventLoop_native_data(env, eventLoop); - if (nat && eventLoopNat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); - strlcpy(context_address, c_address, BTADDR_SIZE); - - LOGV("... address = %s", c_address); - LOGV("... uuid16 = %#X", uuid16); - - bool ret = dbus_func_args_async(env, nat->conn, 20000, // ms - onGetRemoteServiceChannelResult, context_address, - eventLoopNat, - nat->adapter, - DBUS_CLASS_NAME, "GetRemoteServiceChannel", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT16, &uuid16, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return ret ? JNI_TRUE : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - static jint enableNative(JNIEnv *env, jobject object) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); @@ -971,10 +487,141 @@ static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address, return JNI_FALSE; } +static jobjectArray getDevicePropertiesNative(JNIEnv *env, jobject object, + jstring path) +{ +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); + + const char *c_path = env->GetStringUTFChars(path, NULL); + reply = dbus_func_args_timeout(env, + nat->conn, -1, c_path, + DBUS_DEVICE_IFACE, "GetProperties", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(reply, &iter)) + str_array = parse_remote_device_properties(env, &iter); + dbus_message_unref(reply); + return str_array; + } +#endif + return NULL; +} + +static jobjectArray getAdapterPropertiesNative(JNIEnv *env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); + + reply = dbus_func_args_timeout(env, + nat->conn, -1, get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "GetProperties", + DBUS_TYPE_INVALID); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(reply, &iter)) + str_array = parse_adapter_properties(env, &iter); + dbus_message_unref(reply); + return str_array; + } +#endif + return NULL; +} + +static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key, + void *value, jint type) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + native_data_t *nat = get_native_data(env, object); + if (nat) { + DBusMessage *reply, *msg; + DBusMessageIter iter; + DBusError err; + const char *c_key = env->GetStringUTFChars(key, NULL); + dbus_error_init(&err); + + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "SetProperty"); + if (!msg) { + LOGE("%s: Can't allocate new method call for GetProperties!", + __FUNCTION__); + return JNI_FALSE; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID); + dbus_message_iter_init_append(msg, &iter); + append_variant(&iter, type, value); + + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + dbus_message_unref(msg); + + env->ReleaseStringUTFChars(key, c_key); + + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return JNI_FALSE; + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean setAdapterPropertyStringNative(JNIEnv *env, jobject object, jstring key, + jstring value) { + const char *c_value = env->GetStringUTFChars(value, NULL); + jboolean ret = setAdapterPropertyNative(env, object, key, (void *)&c_value, DBUS_TYPE_STRING); + env->ReleaseStringUTFChars(value, (char *)c_value); + return ret; +} + +static jboolean setAdapterPropertyIntegerNative(JNIEnv *env, jobject object, jstring key, + jint value) { + return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_UINT32); +} + +static jboolean setAdapterPropertyBooleanNative(JNIEnv *env, jobject object, jstring key, + jint value) { + return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_BOOLEAN); +} + + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, + {"setupNativeDataNative", "()Z", (void *)setupNativeDataNative}, + {"tearDownNativeDataNative", "()Z", (void *)tearDownNativeDataNative}, {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative}, @@ -982,46 +629,25 @@ static JNINativeMethod sMethods[] = { {"enableNative", "()I", (void *)enableNative}, {"disableNative", "()I", (void *)disableNative}, - {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative}, - {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative}, - {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative}, - {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative}, - {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative}, - {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative}, - {"getCompanyNative", "()Ljava/lang/String;", (void *)getCompanyNative}, - - {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative}, - {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative}, - - {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative}, - {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative}, - - {"startDiscoveryNative", "(Z)Z", (void*)startDiscoveryNative}, - {"cancelDiscoveryNative", "()Z", (void *)cancelDiscoveryNative}, - {"startPeriodicDiscoveryNative", "()Z", (void *)startPeriodicDiscoveryNative}, - {"stopPeriodicDiscoveryNative", "()Z", (void *)stopPeriodicDiscoveryNative}, - {"isPeriodicDiscoveryNative", "()Z", (void *)isPeriodicDiscoveryNative}, - {"listRemoteDevicesNative", "()[Ljava/lang/String;", (void *)listRemoteDevicesNative}, - - {"listConnectionsNative", "()[Ljava/lang/String;", (void *)listConnectionsNative}, - {"isConnectedNative", "(Ljava/lang/String;)Z", (void *)isConnectedNative}, - {"disconnectRemoteDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectRemoteDeviceNative}, - - {"createBondingNative", "(Ljava/lang/String;I)Z", (void *)createBondingNative}, - {"cancelBondingProcessNative", "(Ljava/lang/String;)Z", (void *)cancelBondingProcessNative}, - {"listBondingsNative", "()[Ljava/lang/String;", (void *)listBondingsNative}, - {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative}, - - {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative}, - {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative}, - {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative}, - {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative}, - {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative}, - {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative}, - {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative}, - {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative}, - {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative}, - {"lastUsedNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastUsedNative}, + {"getAdapterPropertiesNative", "()[Ljava/lang/Object;", (void *)getAdapterPropertiesNative}, + {"getDevicePropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", + (void *)getDevicePropertiesNative}, + {"setAdapterPropertyStringNative", "(Ljava/lang/String;Ljava/lang/String;)Z", + (void *)setAdapterPropertyStringNative}, + {"setAdapterPropertyBooleanNative", "(Ljava/lang/String;I)Z", + (void *)setAdapterPropertyBooleanNative}, + {"setAdapterPropertyIntegerNative", "(Ljava/lang/String;I)Z", + (void *)setAdapterPropertyIntegerNative}, + + {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative}, + {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative}, + + {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative}, + {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative}, + {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative}, + {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I", + (void *)getDeviceServiceChannelNative}, + {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative}, {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative}, }; diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 7c5da5b..9438b48 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -39,30 +39,19 @@ namespace android { #ifdef HAVE_BLUETOOTH static jfieldID field_mNativeData; -static jmethodID method_onModeChanged; -static jmethodID method_onNameChanged; -static jmethodID method_onDiscoveryStarted; -static jmethodID method_onDiscoveryCompleted; -static jmethodID method_onRemoteDeviceFound; -static jmethodID method_onRemoteDeviceDisappeared; -static jmethodID method_onRemoteClassUpdated; -static jmethodID method_onRemoteNameUpdated; -static jmethodID method_onRemoteNameFailed; -static jmethodID method_onRemoteDeviceConnected; -static jmethodID method_onRemoteDeviceDisconnectRequested; -static jmethodID method_onRemoteDeviceDisconnected; -static jmethodID method_onBondingCreated; -static jmethodID method_onBondingRemoved; - -static jmethodID method_onCreateBondingResult; -static jmethodID method_onGetRemoteServiceChannelResult; - -static jmethodID method_onPasskeyAgentRequest; -static jmethodID method_onPasskeyAgentCancel; -static jmethodID method_onAuthAgentAuthorize; -static jmethodID method_onAuthAgentCancel; - -static jmethodID method_onRestartRequired; +static jmethodID method_onPropertyChanged; +static jmethodID method_onDevicePropertyChanged; +static jmethodID method_onDeviceFound; +static jmethodID method_onDeviceDisappeared; +static jmethodID method_onDeviceCreated; +static jmethodID method_onDeviceRemoved; + +static jmethodID method_onCreatePairedDeviceResult; +static jmethodID method_onGetDeviceServiceChannelResult; + +static jmethodID method_onRequestPinCode; +static jmethodID method_onAgentAuthorize; +static jmethodID method_onAgentCancel; typedef event_loop_native_data_t native_data_t; @@ -80,30 +69,26 @@ static void classInitNative(JNIEnv* env, jclass clazz) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH - method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V"); - method_onNameChanged = env->GetMethodID(clazz, "onNameChanged", "(Ljava/lang/String;)V"); - method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V"); - method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V"); - method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V"); - method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V"); - method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V"); - method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V"); - method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V"); - method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V"); - method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V"); - method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V"); - method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V"); - method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V"); - - method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V"); - - method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V"); - method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V"); - method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); - method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V"); - - method_onRestartRequired = env->GetMethodID(clazz, "onRestartRequired", "()V"); + method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged", + "([Ljava/lang/String;)V"); + method_onDevicePropertyChanged = env->GetMethodID(clazz, + "onDevicePropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared", + "(Ljava/lang/String;)V"); + method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V"); + method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V"); + + method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult", + "(Ljava/lang/String;I)V"); + + method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize", + "(Ljava/lang/String;Ljava/lang/String;)Z"); + method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V"); + method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode", + "(Ljava/lang/String;I)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif @@ -153,9 +138,11 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) { #ifdef HAVE_BLUETOOTH static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data); -static DBusHandlerResult agent_event_filter(DBusConnection *conn, - DBusMessage *msg, - void *data); +DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); +static int register_agent(native_data_t *nat, + const char *agent_path, const char *capabilities); static const DBusObjectPathVTable agent_vtable = { NULL, agent_event_filter, NULL, NULL, NULL, NULL @@ -164,11 +151,12 @@ static const DBusObjectPathVTable agent_vtable = { static jboolean setUpEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); - dbus_threads_init_default(); - DBusError err; - dbus_error_init(&err); if (nat != NULL && nat->conn != NULL) { + dbus_threads_init_default(); + DBusError err; + dbus_error_init(&err); + // Add a filter for all incoming messages if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){ return JNI_FALSE; @@ -190,108 +178,141 @@ static jboolean setUpEventLoop(native_data_t *nat) { return JNI_FALSE; } dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Manager'", + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Device'", - &err); - if (dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; - } - dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Sink'", + "type='signal',interface='org.bluez.AudioSink'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } - // Add an object handler for passkey agent method calls - const char *path = "/android/bluetooth/Agent"; - if (!dbus_connection_register_object_path(nat->conn, path, - &agent_vtable, nat)) { - LOGE("%s: Can't register object path %s for agent!", - __FUNCTION__, path); + const char *agent_path = "/android/bluetooth/agent"; + const char *capabilities = "DisplayYesNo"; + if (register_agent(nat, agent_path, capabilities) < 0) { + dbus_connection_unregister_object_path (nat->conn, agent_path); return JNI_FALSE; } + return JNI_TRUE; + } + return JNI_FALSE; +} - // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep - // trying for 10 seconds. - int attempt; - for (attempt = 0; attempt < 1000; attempt++) { - DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err, - BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "RegisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) { - // Success - dbus_message_unref(reply); - LOGV("Registered agent on attempt %d of 1000\n", attempt); - break; - } else if (dbus_error_has_name(&err, - "org.freedesktop.DBus.Error.ServiceUnknown")) { - // hcid is still down, retry - dbus_error_free(&err); - usleep(10000); // 10 ms - } else { - // Some other error we weren't expecting - LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; - } + +const char * get_adapter_path(DBusConnection *conn) { + DBusMessage *msg, *reply; + DBusError err; + const char *device_path = NULL; + msg = dbus_message_new_method_call("org.bluez", "/", + "org.bluez.Manager", "DefaultAdapter"); + if (!msg) { + LOGE("%s: Can't allocate new method call for GetProperties!", + __FUNCTION__); + return NULL; + } + dbus_message_append_args(msg, DBUS_TYPE_INVALID); + + dbus_error_init(&err); + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); } - if (attempt == 1000) { - LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " - "is hcid running?"); - return JNI_FALSE; + return NULL; + } + if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, + &device_path, DBUS_TYPE_INVALID) + || !device_path){ + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); } + return NULL; + } + return device_path; +} - // Now register the Auth agent - DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err, - BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "RegisterDefaultAuthorizationAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (!reply) { +static int register_agent(native_data_t *nat, + const char * agent_path, const char * capabilities) +{ + DBusMessage *msg, *reply; + DBusError err; + + if (!dbus_connection_register_object_path(nat->conn, agent_path, + &agent_vtable, nat)) { + LOGE("%s: Can't register object path %s for agent!", + __FUNCTION__, agent_path); + return -1; + } + + nat->adapter = get_adapter_path(nat->conn); + msg = dbus_message_new_method_call("org.bluez", nat->adapter, + "org.bluez.Adapter", "RegisterAgent"); + if (!msg) { + LOGE("%s: Can't allocate new method call for agent!", + __FUNCTION__); + return -1; + } + dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_STRING, &capabilities, + DBUS_TYPE_INVALID); + + dbus_error_init(&err); + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + LOGE("%s: Can't register agent!", __FUNCTION__); + if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; } - - dbus_message_unref(reply); - return JNI_TRUE; + return -1; } - return JNI_FALSE; + dbus_message_unref(reply); + dbus_connection_flush(nat->conn); + + return 0; } static void tearDownEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); if (nat != NULL && nat->conn != NULL) { + DBusMessage *msg, *reply; DBusError err; dbus_error_init(&err); + const char * agent_path = "/android/bluetooth/agent"; - const char *path = "/android/bluetooth/Agent"; - DBusMessage *reply = - dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) dbus_message_unref(reply); + msg = dbus_message_new_method_call("org.bluez", + nat->adapter, + "org.bluez.Adapter", + "UnregisterAgent"); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + dbus_error_free(&err); + } - reply = - dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultAuthorizationAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) dbus_message_unref(reply); + dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_INVALID); + reply = dbus_connection_send_with_reply_and_block(nat->conn, + msg, -1, &err); + dbus_message_unref(msg); + + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + dbus_error_free(&err); + } - dbus_connection_unregister_object_path(nat->conn, path); + dbus_message_unref(reply); + dbus_connection_flush(nat->conn); + dbus_connection_unregister_object_path(nat->conn, agent_path); dbus_bus_remove_match(nat->conn, "type='signal',interface='org.bluez.audio.Sink'", @@ -649,267 +670,129 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("%s: Received signal %s:%s from %s", __FUNCTION__, - dbus_message_get_interface(msg), dbus_message_get_member(msg), - dbus_message_get_path(msg)); + // STOPSHIP: Change to LOGV + LOGE("%s: Received signal %s:%s from %s", __FUNCTION__, + dbus_message_get_interface(msg), dbus_message_get_member(msg), + dbus_message_get_path(msg)); if (dbus_message_is_signal(msg, "org.bluez.Adapter", - "RemoteDeviceFound")) { + "DeviceFound")) { char *c_address; - int n_class; - short n_rssi; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &n_class, - DBUS_TYPE_INT16, &n_rssi, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class, - n_rssi); + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(msg, &iter)) { + dbus_message_iter_get_basic(&iter, &c_address); + if (dbus_message_iter_next(&iter)) + str_array = + parse_remote_device_properties(env, &iter); + } + if (str_array != NULL) { env->CallVoidMethod(nat->me, - method_onRemoteDeviceFound, + method_onDeviceFound, env->NewStringUTF(c_address), - (jint)n_class, - (jshort)n_rssi); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "DiscoveryStarted")) { - LOGI("DiscoveryStarted signal received"); - env->CallVoidMethod(nat->me, method_onDiscoveryStarted); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "DiscoveryCompleted")) { - LOGI("DiscoveryCompleted signal received"); - env->CallVoidMethod(nat->me, method_onDiscoveryCompleted); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisappeared")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteClassUpdated")) { - char *c_address; - int n_class; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &n_class, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, method_onRemoteClassUpdated, - env->NewStringUTF(c_address), (jint)n_class); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteNameUpdated")) { - char *c_address; - char *c_name; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s, name = %s", c_address, c_name); - env->CallVoidMethod(nat->me, - method_onRemoteNameUpdated, - env->NewStringUTF(c_address), - env->NewStringUTF(c_name)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteNameFailed")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteNameFailed, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + str_array); + } else + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceConnected")) { + "org.bluez.Adapter", + "DeviceDisappeared")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteDeviceConnected, + env->CallVoidMethod(nat->me, method_onDeviceDisappeared, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisconnectRequested")) { - char *c_address; + "org.bluez.Adapter", + "DeviceCreated")) { + char *c_object_path; if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_OBJECT_PATH, &c_object_path, DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); + LOGV("... address = %s", c_object_path); env->CallVoidMethod(nat->me, - method_onRemoteDeviceDisconnectRequested, - env->NewStringUTF(c_address)); + method_onDeviceCreated, + env->NewStringUTF(c_object_path)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisconnected")) { - char *c_address; + "org.bluez.Adapter", + "DeviceRemoved")) { + char *c_object_path; if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteDeviceDisconnected, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "BondingCreated")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onBondingCreated, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "BondingRemoved")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onBondingRemoved, - env->NewStringUTF(c_address)); + DBUS_TYPE_OBJECT_PATH, &c_object_path, + DBUS_TYPE_INVALID)) { + LOGV("... Object Path = %s", c_object_path); + env->CallVoidMethod(nat->me, + method_onDeviceRemoved, + env->NewStringUTF(c_object_path)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", - "ModeChanged")) { - char *c_mode; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_mode, - DBUS_TYPE_INVALID)) { - LOGV("... mode = %s", c_mode); + "PropertyChanged")) { + jobjectArray str_array = parse_adapter_property_change(env, msg); + if (str_array != NULL) { + /* Check if bluetoothd has (re)started, if so update the path. */ + jstring property =(jstring) env->GetObjectArrayElement(str_array, 0); + const char *c_property = env->GetStringUTFChars(property, NULL); + if (!strncmp(c_property, "Powered", strlen("Powered"))) { + jstring value = + (jstring) env->GetObjectArrayElement(str_array, 1); + const char *c_value = env->GetStringUTFChars(value, NULL); + if (!strncmp(c_value, "true", strlen("true"))) + nat->adapter = get_adapter_path(nat->conn); + env->ReleaseStringUTFChars(value, c_value); + } + env->ReleaseStringUTFChars(property, c_property); + env->CallVoidMethod(nat->me, - method_onModeChanged, - env->NewStringUTF(c_mode)); + method_onPropertyChanged, + str_array); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "NameChanged")) { - char *c_name; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID)) { - LOGV("... name = %s", c_name); + "org.bluez.Device", + "PropertyChanged")) { + jobjectArray str_array = parse_remote_device_property_change(env, msg); + if (str_array != NULL) { + const char *remote_device_path = dbus_message_get_path(msg); env->CallVoidMethod(nat->me, - method_onNameChanged, - env->NewStringUTF(c_name)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.freedesktop.DBus", - "NameOwnerChanged")) { - char *c_name; - char *c_old_owner; - char *c_new_owner; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_STRING, &c_old_owner, - DBUS_TYPE_STRING, &c_new_owner, - DBUS_TYPE_INVALID)) { - LOGV("... name = %s", c_name); - LOGV("... old_owner = %s", c_old_owner); - LOGV("... new_owner = %s", c_new_owner); - if (!strcmp(c_name, "org.bluez") && c_new_owner[0] != '\0') { - // New owner of org.bluez. This can only happen when hcid - // restarts. Need to restart framework BT services to recover. - LOGE("Looks like hcid restarted"); - env->CallVoidMethod(nat->me, method_onRestartRequired); - } + method_onDevicePropertyChanged, + env->NewStringUTF(remote_device_path), + str_array); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; - } + } return a2dp_event_filter(msg, env); } // Called by dbus during WaitForAndDispatchEventNative() -static DBusHandlerResult agent_event_filter(DBusConnection *conn, - DBusMessage *msg, void *data) { +DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, void *data) { native_data_t *nat = (native_data_t *)data; JNIEnv *env; - nat->vm->GetEnv((void**)&env, nat->envVer); if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) { LOGV("%s: not interested (not a method call).", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("%s: Received method %s:%s", __FUNCTION__, + LOGI("%s: Received method %s:%s", __FUNCTION__, dbus_message_get_interface(msg), dbus_message_get_member(msg)); - if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Request")) { - - const char *adapter; - const char *address; - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Request() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - LOGV("... address = %s", address); - - dbus_message_ref(msg); // increment refcount because we pass to java + if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED; - env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest, - env->NewStringUTF(address), (int)msg); - - return DBUS_HANDLER_RESULT_HANDLED; - - } else if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Cancel")) { - - const char *adapter; - const char *address; - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - LOGV("... address = %s", address); + nat->vm->GetEnv((void**)&env, nat->envVer); + if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "Cancel")) { - env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel, - env->NewStringUTF(address)); + env->CallVoidMethod(nat->me, method_onAgentCancel); // reply DBusMessage *reply = dbus_message_new_method_return(msg); @@ -922,41 +805,23 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Release")) { - LOGW("We are no longer the passkey agent!"); - - // reply - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) { - LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Authorize")) { - const char *adapter; - const char *address; - const char *service; + "org.bluez.Agent", "Authorize")) { + char *object_path; const char *uuid; if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &service, + DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID)) { LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("... address = %s", address); - LOGV("... service = %s", service); + LOGV("... object_path = %s", object_path); LOGV("... uuid = %s", uuid); - bool auth_granted = env->CallBooleanMethod(nat->me, - method_onAuthAgentAuthorize, env->NewStringUTF(address), - env->NewStringUTF(service), env->NewStringUTF(uuid)); + bool auth_granted = + env->CallBooleanMethod(nat->me, method_onAgentAuthorize, + env->NewStringUTF(object_path), env->NewStringUTF(uuid)); // reply if (auth_granted) { @@ -979,43 +844,22 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, } return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Cancel")) { - const char *adapter; - const char *address; - const char *service; - const char *uuid; + "org.bluez.Agent", "RequestPinCode")) { + char *object_path; if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &service, - DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); + LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("... address = %s", address); - LOGV("... service = %s", service); - LOGV("... uuid = %s", uuid); - - env->CallVoidMethod(nat->me, - method_onAuthAgentCancel, env->NewStringUTF(address), - env->NewStringUTF(service), env->NewStringUTF(uuid)); - - // reply - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) { - LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(reply); + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPinCode, + env->NewStringUTF(object_path), + int(msg)); return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Release")) { - LOGW("We are no longer the auth agent!"); - + "org.bluez.Agent", "Release")) { // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { @@ -1026,7 +870,7 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else { - LOGV("... ignored"); + LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg)); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -1044,7 +888,7 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, #define BOND_RESULT_REMOTE_DEVICE_DOWN 4 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5 -void onCreateBondingResult(DBusMessage *msg, void *user, void *n) { +void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); native_data_t *nat = (native_data_t *)n; @@ -1095,7 +939,7 @@ void onCreateBondingResult(DBusMessage *msg, void *user, void *n) { } env->CallVoidMethod(nat->me, - method_onCreateBondingResult, + method_onCreatePairedDeviceResult, env->NewStringUTF(address), result); done: @@ -1103,7 +947,7 @@ done: free(user); } -void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) { +void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); const char *address = (const char *) user; @@ -1122,14 +966,13 @@ void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) { !dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &channel, DBUS_TYPE_INVALID)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); dbus_error_free(&err); } done: env->CallVoidMethod(nat->me, - method_onGetRemoteServiceChannelResult, + method_onGetDeviceServiceChannelResult, env->NewStringUTF(address), channel); free(user); -- cgit v1.1