Bug 1073548: Add |mozilla::ipc::BluetoothDaemonConnection|, r=shawnjohnjr
authorThomas Zimmermann <tdz@users.sourceforge.net>
Mon, 03 Nov 2014 13:03:49 +0100
changeset 213747 d21a4a6dfc527452ac178a8e44420d18c36fda84
parent 213746 f462a870ea876174928ddde75a3e1b47ab2f32f4
child 213748 eb1b257f5e8853c0f7faf2fba222931c0c6e166f
push id9723
push userryanvm@gmail.com
push dateMon, 03 Nov 2014 21:49:13 +0000
treeherderfx-team@a458860efad7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshawnjohnjr
bugs1073548
milestone36.0a1
Bug 1073548: Add |mozilla::ipc::BluetoothDaemonConnection|, r=shawnjohnjr This patch adds |BluetoothDaemonConnection|, which transfers PDUs from and to the Bluetooth daemon. The class is build around the existing socket I/O infrastructure.
ipc/bluetooth/BluetoothDaemonConnection.cpp
ipc/bluetooth/BluetoothDaemonConnection.h
ipc/bluetooth/moz.build
ipc/moz.build
new file mode 100644
--- /dev/null
+++ b/ipc/bluetooth/BluetoothDaemonConnection.cpp
@@ -0,0 +1,555 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothDaemonConnection.h"
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "mozilla/ipc/UnixSocketWatcher.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
+
+#ifdef CHROMIUM_LOG
+#undef CHROMIUM_LOG
+#endif
+
+#if defined(MOZ_WIDGET_GONK)
+#include <android/log.h>
+#define CHROMIUM_LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "I/O", args);
+#else
+#include <stdio.h>
+#define IODEBUG true
+#define CHROMIUM_LOG(args...) if (IODEBUG) printf(args);
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+// The connection to the Bluetooth daemon is established
+// using an abstract socket name. The \0 prefix will be added
+// by the |Connect| method.
+static const char sBluetoothdSocketName[] = "bluez_hal_socket";
+
+//
+// BluetoothDaemonPDU
+//
+
+BluetoothDaemonPDU::BluetoothDaemonPDU(uint8_t aService, uint8_t aOpcode,
+                                       uint16_t aPayloadSize)
+: UnixSocketIOBuffer(HEADER_SIZE + aPayloadSize)
+, mUserData(nullptr)
+{
+  uint8_t* data = Append(HEADER_SIZE);
+  MOZ_ASSERT(data);
+
+  // Setup PDU header
+  data[OFF_SERVICE] = aService;
+  data[OFF_OPCODE] = aOpcode;
+  memcpy(data + OFF_LENGTH, &aPayloadSize, sizeof(aPayloadSize));
+}
+
+BluetoothDaemonPDU::BluetoothDaemonPDU(size_t aPayloadSize)
+: UnixSocketIOBuffer(HEADER_SIZE + aPayloadSize)
+, mUserData(nullptr)
+{ }
+
+ssize_t
+BluetoothDaemonPDU::Send(int aFd)
+{
+  struct iovec iv;
+  memset(&iv, 0, sizeof(iv));
+  iv.iov_base = GetData(GetLeadingSpace());
+  iv.iov_len = GetSize();
+
+  struct msghdr msg;
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+  msg.msg_control = nullptr;
+  msg.msg_controllen = 0;
+
+  ssize_t res = TEMP_FAILURE_RETRY(sendmsg(aFd, &msg, 0));
+  if (res < 0) {
+    MOZ_ASSERT(errno != EBADF); /* internal error */
+    OnError("sendmsg", errno);
+    return -1;
+  }
+
+  Consume(res);
+
+  return res;
+}
+
+#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
+    ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
+      ((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
+
+ssize_t
+BluetoothDaemonPDU::Receive(int aFd)
+{
+  struct iovec iv;
+  memset(&iv, 0, sizeof(iv));
+  iv.iov_base = GetData(0);
+  iv.iov_len = GetAvailableSpace();
+
+  uint8_t cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+  struct msghdr msg;
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsgbuf;
+  msg.msg_controllen = sizeof(cmsgbuf);
+
+  ssize_t res = TEMP_FAILURE_RETRY(recvmsg(aFd, &msg, MSG_NOSIGNAL));
+  if (res < 0) {
+    MOZ_ASSERT(errno != EBADF); /* internal error */
+    OnError("recvmsg", errno);
+    return -1;
+  }
+  if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
+    return -1;
+  }
+
+  SetRange(0, res);
+
+  struct cmsghdr *chdr = CMSG_FIRSTHDR(&msg);
+
+  for (; chdr; chdr = CMSG_NXTHDR(&msg, chdr)) {
+    if (NS_WARN_IF(!CMSGHDR_CONTAINS_FD(chdr))) {
+      continue;
+    }
+    // Retrieve sent file descriptor. If multiple file descriptors
+    // have been sent, we close all but the final one.
+    mReceivedFd = *(static_cast<int*>(CMSG_DATA(chdr)));
+  }
+
+  return res;
+}
+
+int
+BluetoothDaemonPDU::AcquireFd()
+{
+  return mReceivedFd.forget();
+}
+
+nsresult
+BluetoothDaemonPDU::UpdateHeader()
+{
+  size_t len = GetPayloadSize();
+  if (len >= MAX_PAYLOAD_LENGTH) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  uint16_t len16 = static_cast<uint16_t>(len);
+
+  memcpy(GetData(OFF_LENGTH), &len16, sizeof(len16));
+
+  return NS_OK;
+}
+
+size_t
+BluetoothDaemonPDU::GetPayloadSize() const
+{
+  MOZ_ASSERT(GetSize() >= HEADER_SIZE);
+
+  return GetSize() - HEADER_SIZE;
+}
+
+void
+BluetoothDaemonPDU::OnError(const char* aFunction, int aErrno)
+{
+  CHROMIUM_LOG("%s failed with error %d (%s)",
+               aFunction, aErrno, strerror(aErrno));
+}
+
+//
+// BluetoothDaemonPDUConsumer
+//
+
+BluetoothDaemonPDUConsumer::BluetoothDaemonPDUConsumer()
+{ }
+
+BluetoothDaemonPDUConsumer::~BluetoothDaemonPDUConsumer()
+{ }
+
+//
+// BluetoothDaemonConnectionIO
+//
+
+class BluetoothDaemonConnectionIO MOZ_FINAL : public UnixSocketWatcher
+{
+public:
+  BluetoothDaemonConnectionIO(MessageLoop* aIOLoop,
+                              BluetoothDaemonConnection* aConnection,
+                              BluetoothDaemonPDUConsumer* aConsumer);
+
+  SocketBase* GetSocketBase();
+
+  // Shutdown state
+  //
+
+  bool IsShutdownOnMainThread() const;
+  void ShutdownOnMainThread();
+
+  bool IsShutdownOnIOThread() const;
+  void ShutdownOnIOThread();
+
+  // Task callback methods
+  //
+
+  void Connect(const char* aSocketName);
+
+  void Send(BluetoothDaemonPDU* aPDU);
+  void EnqueueData(BluetoothDaemonPDU* aPDU);
+  bool HasPendingData() const;
+
+  nsresult Receive(struct msghdr& msg);
+
+  void OnSocketCanReceiveWithoutBlocking() MOZ_OVERRIDE;
+  void OnSocketCanSendWithoutBlocking() MOZ_OVERRIDE;
+
+  void OnConnected() MOZ_OVERRIDE;
+  void OnError(const char* aFunction, int aErrno) MOZ_OVERRIDE;
+
+private:
+  ssize_t  ReceiveData(int aFd);
+  nsresult SendPendingData(int aFd);
+
+  BluetoothDaemonConnection* mConnection;
+  BluetoothDaemonPDUConsumer* mConsumer;
+  nsAutoPtr<BluetoothDaemonPDU> mPDU;
+  nsTArray<BluetoothDaemonPDU*> mOutgoingQ;
+  bool mShuttingDownOnIOThread;
+};
+
+BluetoothDaemonConnectionIO::BluetoothDaemonConnectionIO(
+  MessageLoop* aIOLoop,
+  BluetoothDaemonConnection* aConnection,
+  BluetoothDaemonPDUConsumer* aConsumer)
+: UnixSocketWatcher(aIOLoop)
+, mConnection(aConnection)
+, mConsumer(aConsumer)
+, mShuttingDownOnIOThread(false)
+{
+  MOZ_ASSERT(mConnection);
+  MOZ_ASSERT(mConsumer);
+
+  /* There's only one PDU for receiving, which we reuse everytime */
+  mPDU = new BluetoothDaemonPDU(BluetoothDaemonPDU::MAX_PAYLOAD_LENGTH);
+}
+
+SocketBase*
+BluetoothDaemonConnectionIO::GetSocketBase()
+{
+  return mConnection;
+}
+
+bool
+BluetoothDaemonConnectionIO::IsShutdownOnMainThread() const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return mConnection == nullptr;
+}
+
+void
+BluetoothDaemonConnectionIO::ShutdownOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!IsShutdownOnMainThread());
+
+  mConnection = nullptr;
+}
+
+bool
+BluetoothDaemonConnectionIO::IsShutdownOnIOThread() const
+{
+  return mShuttingDownOnIOThread;
+}
+
+void
+BluetoothDaemonConnectionIO::ShutdownOnIOThread()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  Close(); // will also remove fd from I/O loop
+  mShuttingDownOnIOThread = true;
+}
+
+void
+BluetoothDaemonConnectionIO::Connect(const char* aSocketName)
+{
+  static const size_t sNameOffset = 1;
+
+  MOZ_ASSERT(aSocketName);
+
+  // Create socket address
+
+  struct sockaddr_un addr;
+  size_t namesiz = strlen(aSocketName) + 1;
+
+  if((sNameOffset + namesiz) > sizeof(addr.sun_path)) {
+    CHROMIUM_LOG("Address too long for socket struct!");
+    return;
+  }
+  memset(addr.sun_path, '\0', sNameOffset); // abstract socket
+  memcpy(addr.sun_path + sNameOffset, aSocketName, namesiz);
+  addr.sun_family = AF_UNIX;
+
+  socklen_t socklen = offsetof(struct sockaddr_un, sun_path) + 1 + namesiz;
+
+  // Create socket
+
+  int fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+  if (fd < 0) {
+    OnError("socket", errno);
+    return;
+  }
+  if (TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, O_NONBLOCK)) < 0) {
+    OnError("fcntl", errno);
+    ScopedClose cleanupFd(fd);
+    return;
+  }
+
+  SetFd(fd);
+
+  // Connect socket to address; calls OnConnected()
+  // on success, or OnError() otherwise
+  nsresult rv = UnixSocketWatcher::Connect(
+    reinterpret_cast<struct sockaddr*>(&addr), socklen);
+  NS_WARN_IF(NS_FAILED(rv));
+}
+
+void
+BluetoothDaemonConnectionIO::Send(BluetoothDaemonPDU* aPDU)
+{
+  MOZ_ASSERT(mConsumer);
+  MOZ_ASSERT(aPDU);
+
+  mConsumer->StoreUserData(*aPDU); // Store user data for reply
+  EnqueueData(aPDU);
+  AddWatchers(WRITE_WATCHER, false);
+}
+
+void
+BluetoothDaemonConnectionIO::EnqueueData(BluetoothDaemonPDU* aPDU)
+{
+  MOZ_ASSERT(aPDU);
+
+  mOutgoingQ.AppendElement(aPDU);
+}
+
+bool
+BluetoothDaemonConnectionIO::HasPendingData() const
+{
+  return !mOutgoingQ.IsEmpty();
+}
+
+ssize_t
+BluetoothDaemonConnectionIO::ReceiveData(int aFd)
+{
+  MOZ_ASSERT(aFd >= 0);
+
+  ssize_t res = mPDU->Receive(aFd);
+  if (res < 0) {
+    /* an I/O error occured */
+    nsRefPtr<nsRunnable> r =
+      new SocketIORequestClosingRunnable<BluetoothDaemonConnectionIO>(this);
+    NS_DispatchToMainThread(r);
+    return -1;
+  } else if (!res) {
+    /* EOF or peer shut down sending */
+    nsRefPtr<nsRunnable> r =
+      new SocketIORequestClosingRunnable<BluetoothDaemonConnectionIO>(this);
+    NS_DispatchToMainThread(r);
+    return 0;
+  }
+
+  mConsumer->Handle(*mPDU);
+
+  return res;
+}
+
+void
+BluetoothDaemonConnectionIO::OnSocketCanReceiveWithoutBlocking()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
+  MOZ_ASSERT(!IsShutdownOnIOThread());
+
+  ssize_t res = ReceiveData(GetFd());
+  if (res < 0) {
+    /* I/O error */
+    RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
+  } else if (!res) {
+    /* EOF or peer shutdown */
+    RemoveWatchers(READ_WATCHER);
+  }
+}
+
+nsresult
+BluetoothDaemonConnectionIO::SendPendingData(int aFd)
+{
+  while (HasPendingData()) {
+    BluetoothDaemonPDU* outgoing = mOutgoingQ.ElementAt(0);
+    MOZ_ASSERT(outgoing);
+
+    ssize_t res = outgoing->Send(aFd);
+    if (res < 0) {
+      /* an I/O error occured */
+      return NS_ERROR_FAILURE;
+    } else if (!res) {
+      /* I/O is currently blocked; try again later */
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(!outgoing->GetSize());
+    mOutgoingQ.RemoveElementAt(0);
+    delete outgoing;
+  }
+
+  return NS_OK;
+}
+
+void
+BluetoothDaemonConnectionIO::OnSocketCanSendWithoutBlocking()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
+  MOZ_ASSERT(!IsShutdownOnIOThread());
+
+  if (NS_WARN_IF(NS_FAILED(SendPendingData(GetFd())))) {
+    RemoveWatchers(WRITE_WATCHER);
+  }
+}
+
+void
+BluetoothDaemonConnectionIO::OnConnected()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
+
+  nsRefPtr<nsRunnable> r =
+    new SocketIOEventRunnable<BluetoothDaemonConnectionIO>(
+      this,
+      SocketIOEventRunnable<BluetoothDaemonConnectionIO>::CONNECT_SUCCESS);
+  NS_DispatchToMainThread(r);
+
+  AddWatchers(READ_WATCHER, true);
+  if (HasPendingData()) {
+    AddWatchers(WRITE_WATCHER, false);
+  }
+}
+
+void
+BluetoothDaemonConnectionIO::OnError(const char* aFunction, int aErrno)
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+
+  UnixFdWatcher::OnError(aFunction, aErrno);
+
+  // Clean up watchers, status, fd
+  Close();
+
+  // Tell the main thread we've errored
+  nsRefPtr<nsRunnable> r =
+    new SocketIOEventRunnable<BluetoothDaemonConnectionIO>(
+      this,
+      SocketIOEventRunnable<BluetoothDaemonConnectionIO>::CONNECT_ERROR);
+  NS_DispatchToMainThread(r);
+}
+
+//
+// I/O helper tasks
+//
+
+class BluetoothDaemonConnectTask MOZ_FINAL
+  : public SocketIOTask<BluetoothDaemonConnectionIO>
+{
+public:
+  BluetoothDaemonConnectTask(BluetoothDaemonConnectionIO* aIO)
+  : SocketIOTask<BluetoothDaemonConnectionIO>(aIO)
+  { }
+
+  void Run() MOZ_OVERRIDE
+  {
+    if (IsCanceled()) {
+      return;
+    }
+    GetIO()->Connect(sBluetoothdSocketName);
+  }
+};
+
+//
+// BluetoothDaemonConnection
+//
+
+BluetoothDaemonConnection::BluetoothDaemonConnection()
+: mIO(nullptr)
+{ }
+
+BluetoothDaemonConnection::~BluetoothDaemonConnection()
+{ }
+
+nsresult
+BluetoothDaemonConnection::ConnectSocket(BluetoothDaemonPDUConsumer* aConsumer)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mIO) {
+    CHROMIUM_LOG("Bluetooth daemon already connecting/connected!");
+    return NS_ERROR_FAILURE;
+  }
+
+  SetConnectionStatus(SOCKET_CONNECTING);
+
+  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+  mIO = new BluetoothDaemonConnectionIO(ioLoop, this, aConsumer);
+  ioLoop->PostTask(FROM_HERE, new BluetoothDaemonConnectTask(mIO));
+
+  return NS_OK;
+}
+
+void
+BluetoothDaemonConnection::CloseSocket()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mIO) {
+    CHROMIUM_LOG("Bluetooth daemon already disconnected!");
+    return;
+  }
+
+  XRE_GetIOMessageLoop()->PostTask(
+    FROM_HERE, new SocketIOShutdownTask<BluetoothDaemonConnectionIO>(mIO));
+
+  mIO = nullptr;
+
+  NotifyDisconnect();
+}
+
+nsresult
+BluetoothDaemonConnection::Send(BluetoothDaemonPDU* aPDU)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mIO) {
+    CHROMIUM_LOG("Bluetooth daemon already connecting/connected!");
+    return NS_ERROR_FAILURE;
+  }
+
+  XRE_GetIOMessageLoop()->PostTask(
+    FROM_HERE,
+    new SocketIOSendTask<BluetoothDaemonConnectionIO,
+                         BluetoothDaemonPDU>(mIO, aPDU));
+  return NS_OK;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/ipc/bluetooth/BluetoothDaemonConnection.h
@@ -0,0 +1,122 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_bluetooth_BluetoothDaemonConnection_h
+#define mozilla_ipc_bluetooth_BluetoothDaemonConnection_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/ipc/SocketBase.h"
+#include "nsError.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace ipc {
+
+class BluetoothDaemonConnectionIO;
+
+/*
+ * |BlutoothDaemonPDU| represents a single PDU that is transfered from or to
+ * the Bluetooth daemon. Each PDU contains exactly one command.
+ *
+ * A PDU as the following format
+ *
+ *    |    1    |    1    |        2       |    n    |
+ *    | service |  opcode | payload length | payload |
+ *
+ * Service and Opcode each require 1 byte, the payload length requires 2
+ * bytes, and the payload requires the number of bytes as stored in the
+ * payload-length field.
+ *
+ * Each service and opcode can have a different payload with individual
+ * length. For the exact details of the Bluetooth protocol, please refer
+ * to
+ *
+ *    https://git.kernel.org/cgit/bluetooth/bluez.git/tree/android/hal-ipc-api.txt?id=5.24
+ *
+ */
+class BluetoothDaemonPDU MOZ_FINAL : public UnixSocketIOBuffer
+{
+public:
+  enum {
+    OFF_SERVICE = 0,
+    OFF_OPCODE = 1,
+    OFF_LENGTH = 2,
+    OFF_PAYLOAD = 4,
+    HEADER_SIZE = OFF_PAYLOAD,
+    MAX_PAYLOAD_LENGTH = 1 << 16
+  };
+
+  BluetoothDaemonPDU(uint8_t aService, uint8_t aOpcode,
+                     uint16_t aPayloadSize);
+  BluetoothDaemonPDU(size_t aPayloadSize);
+
+  void SetUserData(void* aUserData)
+  {
+    mUserData = aUserData;
+  }
+
+  void* GetUserData() const
+  {
+    return mUserData;
+  }
+
+  ssize_t Send(int aFd);
+  ssize_t Receive(int aFd);
+
+  int AcquireFd();
+
+  nsresult UpdateHeader();
+
+private:
+  size_t GetPayloadSize() const;
+  void OnError(const char* aFunction, int aErrno);
+
+  void* mUserData;
+  ScopedClose mReceivedFd;
+};
+
+/*
+ * |BluetoothDaemonPDUConsumer| processes incoming PDUs from the Bluetooth
+ * daemon. Please note that its method |Handle| runs on a different than the
+ * main thread.
+ */
+class BluetoothDaemonPDUConsumer
+{
+public:
+  virtual ~BluetoothDaemonPDUConsumer();
+
+  virtual void Handle(BluetoothDaemonPDU& aPDU) = 0;
+  virtual void StoreUserData(const BluetoothDaemonPDU& aPDU) = 0;
+
+protected:
+  BluetoothDaemonPDUConsumer();
+};
+
+/*
+ * |BluetoothDaemonConnection| represents the socket to connect to the
+ * Bluetooth daemon. It offers connection establishment and sending
+ * PDUs. PDU receiving is performed by |BluetoothDaemonPDUConsumer|.
+ */
+class BluetoothDaemonConnection : public SocketBase
+{
+public:
+  BluetoothDaemonConnection();
+  virtual ~BluetoothDaemonConnection();
+
+  nsresult ConnectSocket(BluetoothDaemonPDUConsumer* aConsumer);
+  void     CloseSocket();
+
+  nsresult Send(BluetoothDaemonPDU* aPDU);
+
+private:
+  BluetoothDaemonConnectionIO* mIO;
+};
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/ipc/bluetooth/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla.ipc += [
+    'BluetoothDaemonConnection.h'
+]
+
+SOURCES += [
+    'BluetoothDaemonConnection.cpp'
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/ipc/moz.build
+++ b/ipc/moz.build
@@ -9,16 +9,19 @@ DIRS += [
     'glue',
     'ipdl',
     'testshell',
 ]
 
 if CONFIG['MOZ_B2G_RIL']:
     DIRS += ['ril']
 
+if CONFIG['MOZ_B2G_BT_BLUEDROID']:
+    DIRS += ['bluetooth']
+
 if CONFIG['MOZ_B2G_BT_BLUEZ']:
     DIRS += ['dbus']
 
 if CONFIG['MOZ_NFC']:
     DIRS += ['nfc']
 
 if CONFIG['MOZ_B2G_RIL'] or CONFIG['MOZ_B2G_BT'] or CONFIG['MOZ_NFC'] or CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     DIRS += ['unixfd', 'unixsocket']