Framework changes for bluez4.

Changes in the Bluetooth JNI calls and framework functions
for Bluez4.
diff --git a/Android.mk b/Android.mk
index 61f85b6..a8ed49d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -89,7 +89,6 @@
 	core/java/android/backup/IRestoreSession.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
-	core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadset.aidl \
         core/java/android/content/IContentService.aidl \
 	core/java/android/content/ISyncAdapter.aidl \
@@ -481,5 +480,3 @@
 ifeq (,$(ONE_SHOT_MAKEFILE))
 include $(call first-makefiles-under,$(LOCAL_PATH))
 endif
-
-
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 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 @@
         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 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 @@
         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 @@
     }
 
     /**
-     * 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 @@
         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 @@
     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 @@
     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 @@
     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.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.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 @@
     private static final String BLUETOOTH_ENABLED = "bluetooth_enabled";
 
     private static final int MESSAGE_CONNECT_TO = 1;
-    private static final int MESSAGE_DISCONNECT = 2;
+
+    private static final String PROPERTY_STATE = "State";
+
+    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 static int mSinkCount;
+
 
     private final Context mContext;
     private final IntentFilter mIntentFilter;
-    private HashMap<String, SinkState> mAudioDevices;
+    private HashMap<String, Integer> mAudioDevices;
     private final AudioManager mAudioManager;
-    private final BluetoothDevice mBluetooth;
-
-    // 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 class SinkState {
-        public String address;
-        public int state;
-        public SinkState(String a, int s) {address = a; state = s;}
-    }
-
-    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 BluetoothDeviceService mBluetoothService;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -151,6 +116,40 @@
         }
     };
 
+    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 @@
                 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 @@
                     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 @@
         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 @@
             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 @@
             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 @@
         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 @@
         if (!disconnectSinkNative(path)) {
             return BluetoothError.ERROR;
         } else {
-            updateState(path, BluetoothA2dp.STATE_DISCONNECTING);
             return BluetoothError.SUCCESS;
         }
     }
@@ -305,15 +333,10 @@
         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,135 +360,42 @@
                 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 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;
-    }
-
-    private synchronized final String lookupPath(String address) {
-        if (mAudioDevices == null) return null;
-
-        for (String path : mAudioDevices.keySet()) {
-            if (address.equals(mAudioDevices.get(path).address)) {
-                return path;
-            }
-        }
-        return null;
-    }
-
-    private synchronized List<String> lookupSinksMatchingStates(int[] states) {
-        List<String> sinks = new ArrayList<String>();
-        if (mAudioDevices == null) {
-            return sinks;
-        }
-        for (SinkState sink : mAudioDevices.values()) {
-            for (int state : states) {
-                if (sink.state == state) {
-                    sinks.add(sink.address);
-                    break;
-                }
-            }
-        }
-        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;
-        }
-
+    private void handleSinkStateChange(String address, int prevState, int 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--;
+            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);
 
             Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
             intent.putExtra(BluetoothIntent.ADDRESS, address);
@@ -473,25 +403,40 @@
             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);
+            if (DBG) log("A2DP state : address: " + address + " State:" + prevState + "->" + state);
+
+            if (state == BluetoothA2dp.STATE_CONNECTED) {
+                mAudioManager.setParameter(A2DP_SINK_ADDRESS, address);
+                mAudioManager.setBluetoothA2dpOn(true);
             }
         }
     }
 
+    private synchronized List<String> lookupSinksMatchingStates(int[] states) {
+        List<String> sinks = new ArrayList<String>();
+        if (mAudioDevices.isEmpty()) {
+            return sinks;
+        }
+        for (String path: mAudioDevices.keySet()) {
+            int sinkState = getSinkState(path);
+            for (int state : states) {
+                if (state == sinkState) {
+                    sinks.add(path);
+                    break;
+                }
+            }
+        }
+        return sinks;
+    }
+
+
     @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 @@
 
     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.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.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.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.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 @@
     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 @@
         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 @@
             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 @@
      * @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 @@
             return;
         }
         mEventLoop.stop();
+        tearDownNativeDataNative();
         disableNative();
 
         // mark in progress bondings as cancelled
@@ -188,25 +187,13 @@
                                     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 @@
         if (!disable(false)) {
             mRestart = false;
         }
-    }   
+    }
 
     private synchronized void setBluetoothState(int state) {
         if (state == mBluetoothState) {
@@ -343,6 +330,9 @@
 
 
             if (res) {
+                if (!setupNativeDataNative()) {
+                    return;
+                }
                 if (mSaveSetting) {
                     persistBluetoothOnSetting(true);
                 }
@@ -369,7 +359,8 @@
 
             if (res) {
                 // Update mode
-                mEventLoop.onModeChanged(getModeNative());
+                String[] propVal = {"Pairable", getProperty("Pairable")};
+                mEventLoop.onPropertyChanged(propVal);
             }
 
             if (mIsAirplaneSensitive && isAirplaneModeOn()) {
@@ -386,9 +377,6 @@
         Binder.restoreCallingIdentity(origCallerIdentityToken);
     }
 
-    private native int enableNative();
-    private native int disableNative();
-
     /* package */ BondState getBondState() {
         return mBondState;
     }
@@ -423,14 +411,19 @@
             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 @@
         }
 
     }
-    private native String[] listBondingsNative();
 
     private static String toBondStateString(int bondState) {
         switch (bondState) {
@@ -543,17 +535,36 @@
         }
     }
 
-    public synchronized String getAddress() {
+    /*package*/synchronized void getAllProperties() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getAddressNative();
-    }
-    private native String getAddressNative();
+        mProperties.clear();
 
-    public synchronized String getName() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getNameNative();
+        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 getNameNative();
+
+    /* package */ synchronized void setProperty(String name, String value) {
+        mProperties.put(name, value);
+    }
 
     public synchronized boolean setName(String name) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -561,74 +572,27 @@
         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();
-
-    public synchronized boolean startDiscovery(boolean resolveNames) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return startDiscoveryNative(resolveNames);
-    }
-    private native boolean startDiscoveryNative(boolean resolveNames);
-
-    public synchronized boolean cancelDiscovery() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return cancelDiscoveryNative();
-    }
-    private native boolean cancelDiscoveryNative();
-
-    public synchronized boolean isDiscovering() {
+    private boolean setPropertyInteger(String key, int value) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return mIsDiscovering;
+        return setAdapterPropertyIntegerNative(key, value);
     }
 
-    /* package */ void setIsDiscovering(boolean isDiscovering) {
-        mIsDiscovering = isDiscovering;
-    }
-
-    public synchronized boolean startPeriodicDiscovery() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return startPeriodicDiscoveryNative();
-    }
-    private native boolean startPeriodicDiscoveryNative();
-
-    public synchronized boolean stopPeriodicDiscovery() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        return stopPeriodicDiscoveryNative();
-    }
-    private native boolean stopPeriodicDiscoveryNative();
-
-    public synchronized boolean isPeriodicDiscovery() {
+    private boolean setPropertyBoolean(String key, boolean value) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return isPeriodicDiscoveryNative();
+        return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
     }
-    private native boolean isPeriodicDiscoveryNative();
 
     /**
      * Set the discoverability window for the device.  A timeout of zero
@@ -642,9 +606,67 @@
     public synchronized boolean setDiscoverableTimeout(int timeout) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
-        return setDiscoverableTimeoutNative(timeout);
+        return setPropertyInteger("DiscoverableTimeout", timeout);
     }
-    private native boolean setDiscoverableTimeoutNative(int timeout_s);
+
+    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);
+
+        return true;
+    }
+
+    /*package*/ synchronized String getProperty (String name) {
+        if (!mProperties.isEmpty())
+            return mProperties.get(name);
+        getAllProperties();
+        return mProperties.get(name);
+    }
+
+    public synchronized String getAddress() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return getProperty("Address");
+    }
+
+    public synchronized String getName() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return getProperty("Name");
+    }
+
+    /**
+     * 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 address Bluetooth address of remote device.
+     *
+     * @return The user-friendly name of the specified remote device.
+     */
+    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;
+    }
 
     /**
      * Get the discoverability window for the device.  A timeout of zero
@@ -656,45 +678,46 @@
      */
     public synchronized int getDiscoverableTimeout() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return getDiscoverableTimeoutNative();
+        String timeout = getProperty("DiscoverableTimeout");
+        if (timeout != null)
+           return Integer.valueOf(timeout);
+        else
+            return -1;
     }
-    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);
-    }
-    private native boolean isConnectedNative(String address);
 
     public synchronized int getScanMode() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        return bluezStringToScanMode(getModeNative());
-    }
-    private native String getModeNative();
+        if (!isEnabled())
+            return BluetoothError.ERROR;
 
-    public synchronized boolean setScanMode(int mode) {
+        boolean pairable = getProperty("Pairable").equals("true");
+        boolean discoverable = getProperty("Discoverable").equals("true");
+        return bluezStringToScanMode (pairable, discoverable);
+    }
+
+    public synchronized boolean startDiscovery() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
-        String bluezMode = scanModeToBluezString(mode);
-        if (bluezMode != null) {
-            return setModeNative(bluezMode);
-        }
-        return false;
-    }
-    private native boolean setModeNative(String mode);
-
-    public synchronized boolean disconnectRemoteDeviceAcl(String address) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH_ADMIN permission");
-        if (!BluetoothDevice.checkBluetoothAddress(address)) {
+        if (!isEnabled()) {
             return false;
         }
-        return disconnectRemoteDeviceNative(address);
+        return startDiscoveryNative();
     }
-    private native boolean disconnectRemoteDeviceNative(String address);
+
+    public synchronized boolean cancelDiscovery() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        return stopDiscoveryNative();
+    }
+
+    public synchronized boolean isDiscovering() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return mIsDiscovering;
+    }
+
+    /* package */ void setIsDiscovering(boolean isDiscovering) {
+        mIsDiscovering = isDiscovering;
+    }
 
     public synchronized boolean createBond(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -719,14 +742,13 @@
             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 @@
 
         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 @@
         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 @@
         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;
+    /*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 getRemoteVersionNative(address);
+        Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
+        return null;
     }
-    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;
+    /* 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 getRemoteRevisionNative(address);
+        mRemoteDeviceProperties.put(address, propertyValues);
     }
-    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 */ void removeRemoteDeviceProperties(String address) {
+        mRemoteDeviceProperties.remove(address);
+    }
+
+    /* 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 getRemoteManufacturerNative(address);
     }
-    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;
-        }
-        return getRemoteCompanyNative(address);
-    }
-    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);
-    }
-    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;
-        }
-        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 @@
      *
      * @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.
+     * @return String array of 128bit UUIDs
      */
-    public synchronized byte[] getRemoteFeatures(String address) {
+    public synchronized String[] getRemoteUuids(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
             return null;
         }
-        return getRemoteFeaturesNative(address);
+        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 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).
+     * Gets the rfcomm channel associated with the UUID.
      *
-     * @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.
+     * @param address Address of the remote device
+     * @param uuid UUID of the service attribute
      *
-     * @return all service handles corresponding to the string match.
-     *
-     * @see #getRemoteServiceRecord
+     * @return rfcomm channel associated with the service attribute
      */
-    public synchronized int[] getRemoteServiceHandles(String address, String match) {
+    public int getRemoteServiceChannel(String address, String uuid) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (!BluetoothDevice.checkBluetoothAddress(address)) {
-            return null;
+            return BluetoothError.ERROR_IPC;
         }
-        if (match == null) {
-            match = "";
-        }
-        return getRemoteServiceHandlesNative(address, match);
+        return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004);
     }
-    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.)
-     *
-     * 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 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
-     */
-    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) {
-        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;
-        }
-        // 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;
-    }
-    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 @@
         }
         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 @@
         }
         return cancelPinNative(address, data.intValue());
     }
-    private native boolean cancelPinNative(String address, int natveiData);
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -1190,20 +1003,22 @@
 
         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 @@
         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 @@
         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.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 @@
     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 @@
         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 @@
             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 @@
         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 @@
         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 onPasskeyAgentRequest(String address, int nativeData) {
+    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 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 @@
         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 @@
                 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 @@
     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 @@
     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 @@
         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 @@
     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 @@
 
 struct event_loop_native_data_t {
     DBusConnection *conn;
+    const char *adapter;
 
     /* protects the thread */
     pthread_mutex_t thread_mutex;
@@ -89,6 +90,12 @@
     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 @@
 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 @@
 
 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 @@
     }
 #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);
+        DBusError err;
+        dbus_error_init(&err);
 
-        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);
-
+        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 @@
     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);
+        DBusError err;
+        dbus_error_init(&err);
 
-        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);
+        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 @@
     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 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));
+                            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;
-    } 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));
-        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 @@
     {"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 @@
     }
 
 #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 @@
 
 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,12 +113,57 @@
         dbus_error_free(&err);
         return false;
     }
-
-    nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME;
 #endif  /*HAVE_BLUETOOTH*/
     return true;
 }
 
+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);
+    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 /*HAVE_BLUETOOTH*/
+    return JNI_TRUE;
+}
+
+static jboolean tearDownNativeDataNative(JNIEnv *env, jobject object) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    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
@@ -127,26 +176,12 @@
 #endif
 }
 
-static jstring getNameNative(JNIEnv *env, jobject object){
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    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;
-    }
-#endif
-    return NULL;
-}
-
 static jstring getAdapterPathNative(JNIEnv *env, jobject object) {
     LOGV(__FUNCTION__);
 #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 @@
     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 @@
 #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 @@
     }
 
     /* 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 @@
 #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 @@
         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,367 +316,9 @@
     return JNI_FALSE;
 }
 
-static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object,
-                                       jstring address) {
-    LOGV(__FUNCTION__);
-#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);
-        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;
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean removeBondingNative(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);
-        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;
-        }
-
-        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);
-        } 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;
-        }
-    }
-#endif
-    return JNI_FALSE;
-}
-
-static jstring common_getRemote(JNIEnv *env, jobject object, const char *func,
-                                jstring address) {
-    LOGV("%s:%s", __FUNCTION__, func);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    if (nat) {
-        const char *c_address = env->GetStringUTFChars(address, 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_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;
-        } 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);
-        }
-    }
-#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) {
+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);
@@ -870,28 +326,88 @@
     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,
+        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(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 -1;
+}
+
+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);
+        DBusError err;
+        dbus_error_init(&err);
+        LOGV("... address = %s", c_address);
+        DBusMessage *reply =
+            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);
-        return ret ? JNI_TRUE : JNI_FALSE;
+        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 {
+            result = JNI_TRUE;
+        }
+        dbus_message_unref(reply);
     }
 #endif
     return JNI_FALSE;
 }
 
+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_object_path = env->GetStringUTFChars(object_path, NULL);
+        DBusError err;
+        dbus_error_init(&err);
+        DBusMessage *reply =
+            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);
+        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 {
+            result = JNI_TRUE;
+        }
+        env->ReleaseStringUTFChars(object_path, c_object_path);
+        if (reply) dbus_message_unref(reply);
+    }
+#endif
+    return result;
+}
+
 static jint enableNative(JNIEnv *env, jobject object) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
@@ -971,10 +487,141 @@
     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 @@
     {"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},
+    {"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},
 
-    {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative},
-    {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative},
+    {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
+    {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
 
-    {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative},
-    {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative},
+    {"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},
 
-    {"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},
     {"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 @@
 #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_onPropertyChanged;
+static jmethodID method_onDevicePropertyChanged;
+static jmethodID method_onDeviceFound;
+static jmethodID method_onDeviceDisappeared;
+static jmethodID method_onDeviceCreated;
+static jmethodID method_onDeviceRemoved;
 
-static jmethodID method_onCreateBondingResult;
-static jmethodID method_onGetRemoteServiceChannelResult;
+static jmethodID method_onCreatePairedDeviceResult;
+static jmethodID method_onGetDeviceServiceChannelResult;
 
-static jmethodID method_onPasskeyAgentRequest;
-static jmethodID method_onPasskeyAgentCancel;
-static jmethodID method_onAuthAgentAuthorize;
-static jmethodID method_onAuthAgentCancel;
-
-static jmethodID method_onRestartRequired;
+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 @@
     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_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_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V");
+    method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
+                                                         "(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_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 @@
 #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 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 @@
             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;
         }
-
-        // 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;
-            }
-        }
-        if (attempt == 1000) {
-            LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), "
-                 "is hcid running?");
-            return JNI_FALSE;
-        }
-
-        // 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) {
-            LOG_AND_FREE_DBUS_ERROR(&err);
-            return JNI_FALSE;
-        }
-
-        dbus_message_unref(reply);
         return JNI_TRUE;
     }
-
     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);
+        }
+        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;
+}
+
+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 -1;
+    }
+
+    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);
 
-        dbus_connection_unregister_object_path(nat->conn, path);
+        if (!reply && dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+            dbus_error_free(&err);
+        }
+
+        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 @@
         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);
+                                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",
-                                      "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")) {
+                                     "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_onRemoteDeviceDisappeared,
+            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",
-                                      "RemoteClassUpdated")) {
-        char *c_address;
-        int n_class;
+                                     "org.bluez.Adapter",
+                                     "DeviceCreated")) {
+        char *c_object_path;
         if (dbus_message_get_args(msg, &err,
-                                  DBUS_TYPE_STRING, &c_address,
-                                  DBUS_TYPE_UINT32, &n_class,
+                                  DBUS_TYPE_OBJECT_PATH, &c_object_path,
                                   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);
+            LOGV("... address = %s", c_object_path);
             env->CallVoidMethod(nat->me,
-                                method_onRemoteNameUpdated,
-                                env->NewStringUTF(c_address),
-                                env->NewStringUTF(c_name));
+                                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",
-                                      "RemoteNameFailed")) {
-        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_onRemoteNameFailed,
-                                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",
-                                      "RemoteDeviceConnected")) {
-        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->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;
-        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_onRemoteDeviceDisconnectRequested,
-                                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",
-                                      "RemoteDeviceDisconnected")) {
-        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_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));
-        } 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);
-            env->CallVoidMethod(nat->me,
-                                method_onModeChanged,
-                                env->NewStringUTF(c_mode));
-        } 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);
-            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);
-            }
-        } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
-        return DBUS_HANDLER_RESULT_HANDLED;
-    }
 
+        return DBUS_HANDLER_RESULT_HANDLED;
+    } else if (dbus_message_is_signal(msg,
+                                      "org.bluez.Adapter",
+                                      "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_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.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_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 (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
+
+    nat->vm->GetEnv((void**)&env, nat->envVer);
     if (dbus_message_is_method_call(msg,
-            "org.bluez.PasskeyAgent", "Request")) {
+            "org.bluez.Agent", "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 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
-
-        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);
-
-        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 @@
         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 @@
         }
         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 @@
         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 @@
 #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 @@
     }
 
     env->CallVoidMethod(nat->me,
-                        method_onCreateBondingResult,
+                        method_onCreatePairedDeviceResult,
                         env->NewStringUTF(address),
                         result);
 done:
@@ -1103,7 +947,7 @@
     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 @@
         !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);
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index 874f2c4..ea7d6c2 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -51,8 +51,7 @@
   LOCAL_SRC_FILES += A2dpAudioInterface.cpp
   LOCAL_SHARED_LIBRARIES += liba2dp
   LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
-  LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs)
-  LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils)
+  LOCAL_C_INCLUDES += $(call include-path-for, bluez)
 endif
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c82b49a..36fc5bb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -173,7 +173,7 @@
                 bluetooth = new BluetoothDeviceService(context);
                 bluetooth.init();
                 ServiceManager.addService(Context.BLUETOOTH_SERVICE, bluetooth);
-                bluetoothA2dp = new BluetoothA2dpService(context);
+                bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
                 ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
                                           bluetoothA2dp);
 
@@ -414,19 +414,19 @@
     public static final int FACTORY_TEST_OFF = 0;
     public static final int FACTORY_TEST_LOW_LEVEL = 1;
     public static final int FACTORY_TEST_HIGH_LEVEL = 2;
-    
-    /** 
-     * This method is called from Zygote to initialize the system. This will cause the native 
+
+    /**
+     * This method is called from Zygote to initialize the system. This will cause the native
      * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
      * up into init2() to start the Android services.
-     */ 
+     */
     native public static void init1(String[] args);
 
     public static void main(String[] args) {
         // The system server has to run all of the time, so it needs to be
         // as efficient as possible with its memory usage.
         VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-        
+
         System.loadLibrary("android_servers");
         init1(args);
     }