Bug 730992 - Patch 2: The machinery in Bluetooth*Service, r=qdot, sr=mrbkap
authorEric Chou <echou@mozilla.com>
Fri, 17 Aug 2012 18:35:59 +0800
changeset 105756 6fa3009c086f8631755fe506c180f228b37393e5
parent 105755 7d926d24c4ead707d65f165463e2c22bf643f465
child 105757 676954e4d2a7f4e99c34d218a23f515e0b3d96b7
push idunknown
push userunknown
push dateunknown
reviewersqdot, mrbkap
bugs730992
milestone17.0a1
Bug 730992 - Patch 2: The machinery in Bluetooth*Service, r=qdot, sr=mrbkap --- dom/bluetooth/BluetoothCommon.h | 8 + dom/bluetooth/BluetoothService.h | 17 + dom/bluetooth/linux/BluetoothDBusService.cpp | 561 ++++++++++++++++++++++++- dom/bluetooth/linux/BluetoothDBusService.h | 23 + 4 files changed, 586 insertions(+), 23 deletions(-)
dom/bluetooth/BluetoothCommon.h
dom/bluetooth/BluetoothService.h
dom/bluetooth/linux/BluetoothDBusService.cpp
dom/bluetooth/linux/BluetoothDBusService.h
--- a/dom/bluetooth/BluetoothCommon.h
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -13,16 +13,24 @@
 
 #define BEGIN_BLUETOOTH_NAMESPACE \
   namespace mozilla { namespace dom { namespace bluetooth {
 #define END_BLUETOOTH_NAMESPACE \
   } /* namespace bluetooth */ } /* namespace dom */ } /* namespace mozilla */
 #define USING_BLUETOOTH_NAMESPACE \
   using namespace mozilla::dom::bluetooth;
 
+#define LOCAL_AGENT_PATH  "/B2G/bluetooth/agent"
+#define REMOTE_AGENT_PATH "/B2G/bluetooth/remote_device_agent"
+
+// Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx)
+#define BLUETOOTH_ADDRESS_LENGTH 17
+
+#define DOM_BLUETOOTH_URL_PREF "dom.mozBluetooth.whitelist"
+
 class nsCString;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSignal;
 typedef mozilla::Observer<BluetoothSignal> BluetoothSignalObserver;
 
 // Enums for object types, currently used for shared function lookups
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -204,29 +204,46 @@ public:
   virtual nsTArray<PRUint32>
   AddReservedServicesInternal(const nsAString& aAdapterPath,
                               const nsTArray<PRUint32>& aServices) = 0;
 
   virtual bool
   RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                  const nsTArray<PRUint32>& aServiceHandles) = 0;
 
+  virtual nsresult
+  CreatePairedDeviceInternal(const nsAString& aAdapterPath,
+                             const nsAString& aAddress,
+                             int aTimeout,
+                             BluetoothReplyRunnable* aRunnable) = 0;
+
+  virtual nsresult
+  RemoveDeviceInternal(const nsAString& aAdapterPath,
+                       const nsAString& aObjectPath,
+                       BluetoothReplyRunnable* aRunnable) = 0;
+
+  virtual bool SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode) = 0;
+  virtual bool SetPasskeyInternal(const nsAString& aDeviceAddress, PRUint32 aPasskey) = 0;
+  virtual bool SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm) = 0;
+  virtual bool SetAuthorizationInternal(const nsAString& aDeviceAddress, bool aAllow) = 0;
+
   /**
    * Due to the fact that some operations require multiple calls, a
    * CommandThread is created that can run blocking, platform-specific calls
    * where either no asynchronous equivilent exists, or else where multiple
    * asynchronous calls would require excessive runnable bouncing between main
    * thread and IO thread.
    *
    * For instance, when we retrieve an Adapter object, we would like it to come
    * with all of its properties filled in and registered as an agent, which
    * requires a minimum of 3 calls to platform specific code on some platforms.
    *
    */
   nsCOMPtr<nsIThread> mBluetoothCommandThread;
+
 protected:
   BluetoothService()
   {
     mBluetoothSignalObserverTable.Init();
   }
 
   virtual ~BluetoothService()
   {
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -23,17 +23,17 @@
 
 #include <cstdio>
 #include <dbus/dbus.h>
 
 #include "nsIDOMDOMRequest.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "nsDebug.h"
-#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
 #include "mozilla/ipc/DBusThread.h"
 #include "mozilla/ipc/DBusUtils.h"
 #include "mozilla/ipc/RawDBusConnection.h"
 #include "mozilla/Util.h"
 
 /**
  * Some rules for dealing with memory in DBus:
  * - A DBusError only needs to be deleted if it's been set, not just
@@ -52,19 +52,21 @@ USING_BLUETOOTH_NAMESPACE
 #if defined(MOZ_WIDGET_GONK)
 #include <android/log.h>
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args);
 #else
 #define BTDEBUG true
 #define LOG(args...) if (BTDEBUG) printf(args);
 #endif
 
+#define B2G_AGENT_CAPABILITIES "DisplayYesNo"
 #define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC ".Manager"
 #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
 #define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
+#define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent"
 #define BLUEZ_DBUS_BASE_PATH      "/org/bluez"
 #define BLUEZ_DBUS_BASE_IFC       "org.bluez"
 #define BLUEZ_ERROR_IFC           "org.bluez.Error"
 
 typedef struct {
   const char* name;
   int type;
 } Properties;
@@ -128,16 +130,18 @@ static const char* sBluetoothDBusSignals
 };
 
 /**
  * DBus Connection held for the BluetoothCommandThread to use. Should never be
  * used by any other thread.
  * 
  */
 static nsAutoPtr<RawDBusConnection> gThreadConnection;
+static nsDataHashtable<nsStringHashKey, DBusMessage* > sPairingReqTable;
+static nsDataHashtable<nsStringHashKey, DBusMessage* > sAuthorizeReqTable;
 
 class DistributeBluetoothSignalTask : public nsRunnable {
   BluetoothSignal mSignal;
 public:
   DistributeBluetoothSignalTask(const BluetoothSignal& aSignal) :
     mSignal(aSignal)
   {
   }
@@ -150,17 +154,17 @@ public:
     if (!bs) {
       NS_WARNING("BluetoothService not available!");
       return NS_ERROR_FAILURE;
     }    
     return bs->DistributeSignal(mSignal);
   }  
 };
 
-bool
+static bool
 IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr)
 {
   if(aErr && dbus_error_is_set(aErr)) {
     aErrorStr = NS_ConvertUTF8toUTF16(aErr->message);
     LOG_AND_FREE_DBUS_ERROR(aErr);
     return true;
   }
   
@@ -182,17 +186,17 @@ IsDBusMessageError(DBusMessage* aMsg, DB
     } else {
       aErrorStr = NS_ConvertUTF8toUTF16(error_msg);
       return true;
     }
   }
   return false;
 }
 
-void
+static void
 DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
                        const BluetoothValue& aValue, const nsAString& aErrorStr)
 {
   // Reply will be deleted by the runnable after running on main thread
   BluetoothReply* reply;
   if (!aErrorStr.IsEmpty()) {
     nsString err(aErrorStr);
     reply = new BluetoothReply(BluetoothReplyError(err));
@@ -201,17 +205,17 @@ DispatchBluetoothReply(BluetoothReplyRun
   }
   
   aRunnable->SetReply(reply);
   if (NS_FAILED(NS_DispatchToMainThread(aRunnable))) {
     NS_WARNING("Failed to dispatch to main thread!");
   }
 }
 
-void
+static void
 UnpackObjectPathMessage(DBusMessage* aMsg, DBusError* aErr,
                         BluetoothValue& aValue, nsAString& aErrorStr)
 {
   DBusError err;
   dbus_error_init(&err);
   if (!IsDBusMessageError(aMsg, aErr, aErrorStr)) {
     NS_ASSERTION(dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN,
                  "Got dbus callback that's not a METHOD_RETURN!");
@@ -226,16 +230,310 @@ UnpackObjectPathMessage(DBusMessage* aMs
     } else {
       aValue = NS_ConvertUTF8toUTF16(object_path);
     }
   }
 }
 
 typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
 
+static nsString
+GetObjectPathFromAddress(const nsAString& aAdapterPath,
+                         const nsAString& aDeviceAddress)
+{
+  // The object path would be like /org/bluez/2906/hci0/dev_00_23_7F_CB_B4_F1,
+  // and the adapter path would be the first part of the object path, accoring
+  // to the example above, it's /org/bluez/2906/hci0.
+  nsString devicePath(aAdapterPath);
+  devicePath.AppendLiteral("/dev_");
+  devicePath.Append(aDeviceAddress);
+  devicePath.ReplaceChar(':', '_');
+  return devicePath;
+}
+
+static nsString
+GetAddressFromObjectPath(const nsAString& aObjectPath)
+{
+  // The object path would be like /org/bluez/2906/hci0/dev_00_23_7F_CB_B4_F1,
+  // and the adapter path would be the first part of the object path, accoring
+  // to the example above, it's /org/bluez/2906/hci0.
+  nsString address(aObjectPath);
+  int addressHead = address.RFind("/") + 5;
+
+  MOZ_ASSERT(addressHead + BLUETOOTH_ADDRESS_LENGTH == address.Length());
+
+  address.Cut(0, addressHead);
+  address.ReplaceChar('_', ':');
+
+  return address;
+}
+
+static void
+KeepDBusPairingMessage(const nsString& aDeviceAddress, DBusMessage* aMsg)
+{
+  sPairingReqTable.Put(aDeviceAddress, aMsg);
+
+  // Increase ref count here because we need this message later. 
+  // It'll be unrefed when set*Internal() is called.
+  dbus_message_ref(aMsg);
+}
+
+static DBusHandlerResult
+AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+  if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
+    LOG("%s: agent handler not interested (not a method call).\n", __FUNCTION__);
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  DBusError err;
+  dbus_error_init(&err);
+
+  nsString signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(msg));
+  nsString signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(msg));
+  nsString errorStr;
+  BluetoothValue v;
+  InfallibleTArray<BluetoothNamedValue> parameters;
+
+  // The following descriptions of each signal are retrieved from:
+  //
+  // http://maemo.org/api_refs/5.0/beta/bluez/agent.html
+  //
+  if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Cancel")) {
+    // This method gets called to indicate that the agent request failed before a reply
+    // was returned.
+
+    // Return directly
+    DBusMessage *reply = dbus_message_new_method_return(msg);
+
+    if (!reply) {
+      errorStr.AssignLiteral("Memory can't be allocated for the message.");
+    } else {
+      dbus_connection_send(conn, reply, NULL);
+      dbus_message_unref(reply);
+    }
+  } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Authorize")) {
+    // This method gets called when the service daemon needs to authorize a
+    // connection/service request.
+    char *objectPath;
+    const char *uuid;
+    if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &objectPath,
+                               DBUS_TYPE_STRING, &uuid,
+                               DBUS_TYPE_INVALID)) {
+      LOG("%s: Invalid arguments for Authorize() method", __FUNCTION__);
+      errorStr.AssignLiteral("Invalid arguments for Authorize() method");
+    } else {
+      nsString deviceAddress = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
+
+      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Device"), deviceAddress));
+      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("UUID"),
+                                                   NS_ConvertUTF8toUTF16(uuid)));
+
+      // Because we may have authorization request and pairing request from the 
+      // same remote device at the same time, we need two tables to keep these messages.
+      sAuthorizeReqTable.Put(deviceAddress, msg);
+
+      // Increase ref count here because we need this message later. 
+      // It'll be unrefed when setAuthorizationInternal() is called.
+      dbus_message_ref(msg);
+
+      v = parameters;
+    }  
+  } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestConfirmation")) {
+    // This method gets called when the service daemon needs to confirm a passkey for
+    // an authentication.
+    char *objectPath;
+    uint32_t passkey;
+    if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &objectPath,
+                               DBUS_TYPE_UINT32, &passkey,
+                               DBUS_TYPE_INVALID)) {
+      LOG("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
+      errorStr.AssignLiteral("Invalid arguments for RequestConfirmation() method");
+    } else {
+      nsString deviceAddress = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
+
+      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Device"), deviceAddress));
+      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Passkey"), passkey));
+      
+      KeepDBusPairingMessage(deviceAddress, msg);
+
+      v = parameters;
+    }
+  } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPinCode")) {
+    // This method gets called when the service daemon needs to get the passkey for an
+    // authentication. The return value should be a string of 1-16 characters length.
+    // The string can be alphanumeric.
+    char *objectPath;
+    if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &objectPath,
+                               DBUS_TYPE_INVALID)) {
+      LOG("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
+      errorStr.AssignLiteral("Invalid arguments for RequestPinCode() method");
+    } else {
+      nsString deviceAddress = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
+
+      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Device"), deviceAddress));
+
+      KeepDBusPairingMessage(deviceAddress, msg);
+
+      v = parameters;
+    }
+  } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPasskey")) {
+    // This method gets called when the service daemon needs to get the passkey for an
+    // authentication. The return value should be a numeric value between 0-999999.
+    char *objectPath;
+    if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &objectPath,
+                               DBUS_TYPE_INVALID)) {
+      LOG("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
+      errorStr.AssignLiteral("Invalid arguments for RequestPasskey() method");
+    } else {
+      nsString deviceAddress = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
+
+      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Device"), deviceAddress));
+
+      KeepDBusPairingMessage(deviceAddress, msg);
+
+      v = parameters;
+    }
+  } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) {
+    // This method gets called when the service daemon unregisters the agent. An agent
+    // can use it to do cleanup tasks. There is no need to unregister the agent, because
+    // when this method gets called it has already been unregistered.
+    DBusMessage *reply = dbus_message_new_method_return(msg);
+
+    if (!reply) {
+      errorStr.AssignLiteral("Memory can't be allocated for the message.");
+    } else {
+      dbus_connection_send(conn, reply, NULL);
+      dbus_message_unref(reply);
+
+      // Do not send an notification to upper layer, too annoying.
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  } else {
+    LOG("agent handler %s: Unhandled event. Ignore.", __FUNCTION__);
+  }
+
+  if (!errorStr.IsEmpty()) {
+    NS_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  BluetoothSignal signal(signalName, signalPath, v);
+
+  nsRefPtr<DistributeBluetoothSignalTask> t = new DistributeBluetoothSignalTask(signal);
+
+  if (NS_FAILED(NS_DispatchToMainThread(t))) {
+     NS_WARNING("Failed to dispatch to main thread!");
+  }
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static const DBusObjectPathVTable agentVtable = {
+  NULL, AgentEventFilter, NULL, NULL, NULL, NULL
+};
+
+// Local agent means agent for Adapter, not agent for Device. Some signals
+// will be passed to local agent, some will be passed to device agent.
+// For example, if a remote device would like to pair with us, then the
+// signal will be passed to local agent. If we start pairing process with
+// calling CreatePairedDevice, we'll get signal which should be passed to
+// device agent.
+static bool
+RegisterLocalAgent(const char* adapterPath,
+                   const char* agentPath,
+                   const char* capabilities)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
+                                            agentPath,
+                                            &agentVtable,
+                                            NULL)) {
+    LOG("%s: Can't register object path %s for agent!",
+        __FUNCTION__, agentPath);
+    return false;
+  }
+
+  DBusMessage* msg =
+    dbus_message_new_method_call("org.bluez", adapterPath,
+                                 DBUS_ADAPTER_IFACE, "RegisterAgent");
+  if (!msg) {
+    LOG("%s: Can't allocate new method call for agent!", __FUNCTION__);
+    return false;
+  }
+
+  if (!dbus_message_append_args(msg,
+                                DBUS_TYPE_OBJECT_PATH, &agentPath,
+                                DBUS_TYPE_STRING, &capabilities,
+                                DBUS_TYPE_INVALID)) {
+    LOG("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
+    return false;
+  }
+
+  DBusError err;
+  dbus_error_init(&err);
+
+  DBusMessage* reply =
+    dbus_connection_send_with_reply_and_block(gThreadConnection->GetConnection(),
+                                              msg, -1, &err);
+  dbus_message_unref(msg);
+
+  if (!reply) {
+    if (dbus_error_is_set(&err)) {
+      if(!strcmp(err.name, "org.bluez.Error.AlreadyExists")) {
+        LOG_AND_FREE_DBUS_ERROR(&err);
+#ifdef DEBUG
+        LOG("Agent already registered, still returning true");
+#endif
+      } else {
+        LOG_AND_FREE_DBUS_ERROR(&err);
+        LOG("%s: Can't register agent!", __FUNCTION__);
+        return false;
+      }
+    }
+  } else {
+    dbus_message_unref(reply);
+  }
+  
+  dbus_connection_flush(gThreadConnection->GetConnection());
+  return true;
+}
+
+static bool
+RegisterAgent(const nsAString& aAdapterPath)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  if (!RegisterLocalAgent(NS_ConvertUTF16toUTF8(aAdapterPath).get(), 
+                          LOCAL_AGENT_PATH, 
+                          B2G_AGENT_CAPABILITIES)) {
+    return false;
+  }
+
+  // There is no "RegisterAgent" function defined in device interface.
+  // When we call "CreatePairedDevice", it will do device agent registration for us.
+  // (See maemo.org/api_refs/5.0/beta/bluez/adapter.html)
+  if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
+                                            REMOTE_AGENT_PATH,
+                                            &agentVtable,
+                                            NULL)) {
+    LOG("%s: Can't register object path %s for remote device agent!",
+        __FUNCTION__, REMOTE_AGENT_PATH);
+
+    return false;
+  }
+
+  return true;
+}
+
 void
 RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable,
                 UnpackFunc aFunc)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   nsRefPtr<BluetoothReplyRunnable> replyRunnable =
     dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable));
 
@@ -670,19 +968,30 @@ BluetoothDBusService::StartInternal()
 
   // Add a filter for all incoming messages_base
   if (!dbus_connection_add_filter(mConnection, EventFilter,
                                   NULL, NULL)) {
     NS_WARNING("Cannot create DBus Event Filter for DBus Thread!");
     return NS_ERROR_FAILURE;
   }
 
+  sPairingReqTable.Init();
+  sAuthorizeReqTable.Init();
+
   return NS_OK;
 }
 
+PLDHashOperator
+UnrefDBusMessages(const nsAString& key, DBusMessage* value, void* arg)
+{
+  dbus_message_unref(value);
+
+  return PL_DHASH_NEXT;
+}
+
 nsresult
 BluetoothDBusService::StopInternal()
 {
   // This could block. It should never be run on the main thread.
   MOZ_ASSERT(!NS_IsMainThread());
   
   if (!mConnection) {
     StopDBus();
@@ -700,16 +1009,24 @@ BluetoothDBusService::StopInternal()
     }
   }
 
   dbus_connection_remove_filter(mConnection, EventFilter, nullptr);
   
   mConnection = nullptr;
   gThreadConnection = nullptr;
   mBluetoothSignalObserverTable.Clear();
+
+  // unref stored DBusMessages before clear the hashtable
+  sPairingReqTable.EnumerateRead(UnrefDBusMessages, nullptr);
+  sPairingReqTable.Clear();
+
+  sAuthorizeReqTable.EnumerateRead(UnrefDBusMessages, nullptr);
+  sAuthorizeReqTable.Clear();
+
   StopDBus();
   return NS_OK;
 }
 
 class DefaultAdapterPropertiesRunnable : public nsRunnable
 {
 public:
   DefaultAdapterPropertiesRunnable(BluetoothReplyRunnable* aRunnable)
@@ -762,32 +1079,36 @@ public:
       return NS_ERROR_FAILURE;
     }
     if(msg) {
       dbus_message_unref(msg);
     }
     // We have to manually attach the path to the rest of the elements
     v.get_ArrayOfBluetoothNamedValue().AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Path"),
                                                                          path));
+
+    RegisterAgent(path);
+
     DispatchBluetoothReply(mRunnable, v, replyError);
    
     return NS_OK;
   }
 
 private:
   nsRefPtr<BluetoothReplyRunnable> mRunnable;
 };
 
 nsresult
 BluetoothDBusService::GetDefaultAdapterPathInternal(BluetoothReplyRunnable* aRunnable)
 {
   if (!mConnection || !gThreadConnection) {
     NS_ERROR("Bluetooth service not started yet!");
     return NS_ERROR_FAILURE;
   }
+
   NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
   nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
 
   nsRefPtr<nsRunnable> func(new DefaultAdapterPropertiesRunnable(runnable));
   if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
     NS_WARNING("Cannot dispatch firmware loading task!");
     return NS_ERROR_FAILURE;
   }
@@ -1030,30 +1351,16 @@ BluetoothDBusService::SetProperty(Blueto
                             (void*)aRunnable)) {
     NS_WARNING("Could not start async function!");
     return NS_ERROR_FAILURE;
   }
   runnable.forget();
   return NS_OK;
 }
 
-nsString
-GetObjectPathFromAddress(const nsAString& aAdapterPath,
-                         const nsAString& aDeviceAddress)
-{
-  // The object path would be like /org/bluez/2906/hci0/dev_00_23_7F_CB_B4_F1,
-  // and the adapter path would be the first part of the object path, accoring
-  // to the example above, it's /org/bluez/2906/hci0.
-  nsString devicePath(aAdapterPath);
-  devicePath.AppendLiteral("/dev_");
-  devicePath.Append(aDeviceAddress);
-  devicePath.ReplaceChar(':', '_');
-  return devicePath;
-}
-
 bool
 BluetoothDBusService::GetDevicePath(const nsAString& aAdapterPath,
                                     const nsAString& aDeviceAddress,
                                     nsAString& aDevicePath)
 {
   aDevicePath = GetObjectPathFromAddress(aAdapterPath, aDeviceAddress);
   return true;
 }
@@ -1061,22 +1368,22 @@ BluetoothDBusService::GetDevicePath(cons
 int
 BluetoothDBusService::GetDeviceServiceChannelInternal(const nsAString& aObjectPath,
                                                       const nsAString& aPattern,
                                                       int aAttributeId)
 {
   // This is a blocking call, should not be run on main thread.
   MOZ_ASSERT(!NS_IsMainThread());
 
-  const char* deviceObjectPath = NS_ConvertUTF16toUTF8(aObjectPath).get();
-  const char* pattern = NS_ConvertUTF16toUTF8(aPattern).get();
+  nsCString tempPattern = NS_ConvertUTF16toUTF8(aPattern);
+  const char* pattern = tempPattern.get();
 
   DBusMessage *reply =
     dbus_func_args(gThreadConnection->GetConnection(),
-                   deviceObjectPath,
+                   NS_ConvertUTF16toUTF8(aObjectPath).get(),
                    DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
                    DBUS_TYPE_STRING, &pattern,
                    DBUS_TYPE_UINT16, &aAttributeId,
                    DBUS_TYPE_INVALID);
 
   return reply ? dbus_returns_int32(reply) : -1;
 }
 
@@ -1106,25 +1413,24 @@ ExtractHandles(DBusMessage *aReply, nsTA
 
 nsTArray<PRUint32>
 BluetoothDBusService::AddReservedServicesInternal(const nsAString& aAdapterPath,
                                                   const nsTArray<PRUint32>& aServices)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsTArray<PRUint32> ret;
-  const char* adapterPath = NS_ConvertUTF16toUTF8(aAdapterPath).get();
 
   int length = aServices.Length();
   if (length == 0) return ret;
 
   const uint32_t* services = aServices.Elements();
   DBusMessage* reply =
     dbus_func_args(gThreadConnection->GetConnection(),
-                   adapterPath,
+                   NS_ConvertUTF16toUTF8(aAdapterPath).get(),
                    DBUS_ADAPTER_IFACE, "AddReservedServiceRecords",
                    DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
                    &services, length, DBUS_TYPE_INVALID);
 
   if (!reply) {
     LOG("Null DBus message. Couldn't extract handles.");
     return ret;
   }
@@ -1151,8 +1457,217 @@ BluetoothDBusService::RemoveReservedServ
                    DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
                    &services, length, DBUS_TYPE_INVALID);
 
   if (!reply) return false;
 
   dbus_message_unref(reply);
   return true;
 }
+
+nsresult
+BluetoothDBusService::CreatePairedDeviceInternal(const nsAString& aAdapterPath,
+                                                 const nsAString& aDeviceAddress,
+                                                 int aTimeout,
+                                                 BluetoothReplyRunnable* aRunnable)
+{
+  const char *capabilities = B2G_AGENT_CAPABILITIES;
+  const char *deviceAgentPath = REMOTE_AGENT_PATH;
+
+  nsCString tempDeviceAddress = NS_ConvertUTF16toUTF8(aDeviceAddress);
+  const char *deviceAddress = tempDeviceAddress.get();
+
+  nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
+  // Then send CreatePairedDevice, it will register a temp device agent then
+  // unregister it after pairing process is over
+  bool ret = dbus_func_args_async(mConnection,
+                                  aTimeout,
+                                  GetObjectPathCallback,
+                                  (void*)runnable,
+                                  NS_ConvertUTF16toUTF8(aAdapterPath).get(),
+                                  DBUS_ADAPTER_IFACE,
+                                  "CreatePairedDevice",
+                                  DBUS_TYPE_STRING, &deviceAddress,
+                                  DBUS_TYPE_OBJECT_PATH, &deviceAgentPath,
+                                  DBUS_TYPE_STRING, &capabilities,
+                                  DBUS_TYPE_INVALID);
+
+  if (!ret) {
+    NS_WARNING("Could not start async function!");
+    return NS_ERROR_FAILURE;
+  }
+
+  runnable.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDBusService::RemoveDeviceInternal(const nsAString& aAdapterPath,
+                                           const nsAString& aDeviceAddress,
+                                           BluetoothReplyRunnable* aRunnable)
+{
+  nsCString tempDeviceObjectPath =
+    NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress));
+  const char* deviceObjectPath = tempDeviceObjectPath.get();
+
+  nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
+
+  // We don't really care about how long it would take on removing a device,
+  // just to make sure that the value of timeout is reasonable. So, we use 
+  // -1 for the timeout, which means a reasonable default timeout will be used.
+  bool ret = dbus_func_args_async(mConnection,
+                                  -1,
+                                  GetVoidCallback,
+                                  (void*)runnable,
+                                  NS_ConvertUTF16toUTF8(aAdapterPath).get(),
+                                  DBUS_ADAPTER_IFACE,
+                                  "RemoveDevice",
+                                  DBUS_TYPE_OBJECT_PATH, &deviceObjectPath,
+                                  DBUS_TYPE_INVALID);
+   if (!ret) {
+    NS_WARNING("Could not start async function!");
+    return NS_ERROR_FAILURE;
+  }
+
+  runnable.forget();
+  return NS_OK;
+}
+
+bool
+BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode)
+{
+  DBusMessage *msg;
+  if (!sPairingReqTable.Get(aDeviceAddress, &msg)) {
+    LOG("%s: Couldn't get original request message.", __FUNCTION__);
+    return false;
+  }
+
+  DBusMessage *reply = dbus_message_new_method_return(msg);
+
+  if (!reply) {
+    LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
+    dbus_message_unref(msg);
+    return false;
+  }
+
+  bool result;
+
+  nsCString tempPinCode = NS_ConvertUTF16toUTF8(aPinCode);
+  const char* pinCode = tempPinCode.get();
+
+  if (!dbus_message_append_args(reply,
+                                DBUS_TYPE_STRING, &pinCode,
+                                DBUS_TYPE_INVALID)) {
+    LOG("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
+    result = false;
+  } else {
+    result = dbus_connection_send(mConnection, reply, NULL);
+  }
+
+  dbus_message_unref(msg);
+  dbus_message_unref(reply);
+
+  sPairingReqTable.Remove(aDeviceAddress);
+
+  return result;
+}
+
+bool
+BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, PRUint32 aPasskey)
+{
+  DBusMessage *msg;
+  if (!sPairingReqTable.Get(aDeviceAddress, &msg)) {
+    LOG("%s: Couldn't get original request message.", __FUNCTION__);
+    return false;
+  }
+
+  DBusMessage *reply = dbus_message_new_method_return(msg);
+
+  if (!reply) {
+    LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
+    dbus_message_unref(msg);
+    return false;
+  }
+
+  uint32_t passkey = aPasskey;
+  bool result;
+
+  if (!dbus_message_append_args(reply,
+                                DBUS_TYPE_UINT32, &passkey,
+                                DBUS_TYPE_INVALID)) {
+    LOG("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
+    result = false;
+  } else {
+    result = dbus_connection_send(mConnection, reply, NULL);
+  }
+
+  dbus_message_unref(msg);
+  dbus_message_unref(reply);
+
+  sPairingReqTable.Remove(aDeviceAddress);
+
+  return result;
+}
+
+bool
+BluetoothDBusService::SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm)
+{
+  DBusMessage *msg;
+  if (!sPairingReqTable.Get(aDeviceAddress, &msg)) {
+    LOG("%s: Couldn't get original request message.", __FUNCTION__);
+    return false;
+  }
+
+  DBusMessage *reply;
+
+  if (aConfirm) {
+    reply = dbus_message_new_method_return(msg);   
+  } else {
+    reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "User rejected confirmation");
+  }
+
+  if (!reply) {
+    LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
+    dbus_message_unref(msg);
+    return false;
+  }
+
+  bool result = dbus_connection_send(mConnection, reply, NULL);
+  dbus_message_unref(msg);
+  dbus_message_unref(reply);
+
+  sPairingReqTable.Remove(aDeviceAddress);
+
+  return result;
+}
+
+bool
+BluetoothDBusService::SetAuthorizationInternal(const nsAString& aDeviceAddress, bool aAllow)
+{
+  DBusMessage *msg;
+  
+  if (!sAuthorizeReqTable.Get(aDeviceAddress, &msg)) {
+    LOG("%s: Couldn't get original request message.", __FUNCTION__);
+    return false;
+  }
+
+  DBusMessage *reply;
+
+  if (aAllow) {
+    reply = dbus_message_new_method_return(msg);   
+  } else {
+    reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "Authorization rejected");
+  }
+
+  if (!reply) {
+    LOG("%s: Memory can't be allocated for the message.", __FUNCTION__);
+    dbus_message_unref(msg);
+    return false;
+  }
+
+  bool result = dbus_connection_send(mConnection, reply, NULL);
+  dbus_message_unref(msg);
+  dbus_message_unref(reply);
+
+  sAuthorizeReqTable.Remove(aDeviceAddress);
+
+  return result;
+}
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -54,16 +54,39 @@ public:
   virtual nsTArray<PRUint32>
   AddReservedServicesInternal(const nsAString& aAdapterPath,
                               const nsTArray<PRUint32>& aServices);
 
   virtual bool
   RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                  const nsTArray<PRUint32>& aServiceHandles);
 
+  virtual nsresult
+  CreatePairedDeviceInternal(const nsAString& aAdapterPath,
+                             const nsAString& aDeviceAddress,
+                             int aTimeout,
+                             BluetoothReplyRunnable* aRunnable);
+
+  virtual nsresult
+  RemoveDeviceInternal(const nsAString& aAdapterPath,
+                       const nsAString& aDeviceObjectPath,
+                       BluetoothReplyRunnable* aRunnable);
+
+  virtual bool
+  SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode);
+
+  virtual bool
+  SetPasskeyInternal(const nsAString& aDeviceAddress, PRUint32 aPasskey);
+
+  virtual bool 
+  SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm);
+
+  virtual bool 
+  SetAuthorizationInternal(const nsAString& aDeviceAddress, bool aAllow);
+
 private:
   nsresult SendGetPropertyMessage(const nsAString& aPath,
                                   const char* aInterface,
                                   void (*aCB)(DBusMessage *, void *),
                                   BluetoothReplyRunnable* aRunnable);
   nsresult SendDiscoveryMessage(const nsAString& aAdapterPath,
                                 const char* aMessageName,
                                 BluetoothReplyRunnable* aRunnable);