summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJaikumar Ganesh <jaikumar@google.com>2009-05-05 22:26:12 -0700
committerJaikumar Ganesh <jaikumar@google.com>2009-06-09 17:21:08 -0700
commitd5ac1ae36b4e096eb97984334f86d0c68abea2f7 (patch)
tree0e9c8b664eee7bce22f49a17dab4993292c17e1d /core
parentb099c4699b8d32295caa7b59637657d47a7c7486 (diff)
downloadframeworks_base-d5ac1ae36b4e096eb97984334f86d0c68abea2f7.zip
frameworks_base-d5ac1ae36b4e096eb97984334f86d0c68abea2f7.tar.gz
frameworks_base-d5ac1ae36b4e096eb97984334f86d0c68abea2f7.tar.bz2
Framework changes for bluez4.
Changes in the Bluetooth JNI calls and framework functions for Bluez4.
Diffstat (limited to 'core')
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java192
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java62
-rw-r--r--core/java/android/bluetooth/IBluetoothDevice.aidl26
-rw-r--r--core/java/android/bluetooth/IBluetoothDeviceCallback.aidl25
-rw-r--r--core/java/android/server/BluetoothA2dpService.java403
-rw-r--r--core/java/android/server/BluetoothDeviceService.java698
-rw-r--r--core/java/android/server/BluetoothEventLoop.java266
-rw-r--r--core/jni/android_bluetooth_common.cpp363
-rw-r--r--core/jni/android_bluetooth_common.h17
-rw-r--r--core/jni/android_server_BluetoothA2dpService.cpp324
-rw-r--r--core/jni/android_server_BluetoothDeviceService.cpp944
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp633
12 files changed, 1658 insertions, 2295 deletions
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<String, SinkState> 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<String> mPendingDisconnects = new ArrayList<String>();
- // 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<String, Integer> 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<String, Integer>();
+
+ 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, SinkState>();
- 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<String> lookupSinksMatchingStates(int[] states) {
List<String> sinks = new ArrayList<String>();
- 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<String, String> mProperties;
+ private HashMap <String, Map<String, String>> 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<String, String>();
+ mRemoteDeviceProperties = new HashMap<String, Map<String,String>>();
}
- 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<String, IBluetoothDeviceCallback> callbacksMap =
- mEventLoop.getRemoteServiceChannelCallbacks();
-
- for (Iterator<String> 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 <String, String> 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<String, String> 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<String, String> propertyValues = new HashMap<String, String>();
+ 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 <String, String> 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<String, IBluetoothDeviceCallback> 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<String, Integer> mPasskeyAgentRequestData;
- private final HashMap<String, IBluetoothDeviceCallback> 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<String, IBluetoothDeviceCallback> getRemoteServiceChannelCallbacks() {
- return mGetRemoteServiceChannelCallbacks;
- }
-
- /* pacakge */ HashMap<String, Integer> getPasskeyAgentRequestData() {
+ /* package */ HashMap<String, Integer> 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);