Merge m-c to m-i
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 28 Nov 2014 15:13:22 -0800
changeset 243910 8a0c83fe66d18b738c5c822c47eed723201d708d
parent 243909 d101d9574541811011132b971e222e22b9ed0954 (current diff)
parent 243902 e90536aa55ddeb2452308b0d6b275fb3ead1fe5b (diff)
child 243911 c6be963121ae1120ffc952bc9c5c2de3e0ecbd3d
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to m-i
dom/bluetooth/bluedroid/BluetoothUtils.cpp
dom/bluetooth/bluedroid/BluetoothUtils.h
dom/bluetooth/bluez/BluetoothUtils.cpp
dom/bluetooth/bluez/BluetoothUtils.h
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,14 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Merge day clobber
\ No newline at end of file
+Bug 1105308 - Cleanup BluetoothUtils.{cpp,h}
+
+This patch set moves some files around and requires a rebuild
+of the build system's dependency information.
+
+Merge day clobber
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8b13bfc1d7d25cee4de55f332654fdba25b8460b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
@@ -92,17 +92,17 @@
   <project name="platform/prebuilt" path="prebuilt" revision="a4062cc40fcaa0776dc880ce591b4c515d36f420"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="507e46e553586bec971551322f20d066c80a0788"/>
   <project name="platform/system/core" path="system/core" revision="91e5551f88aea5aa64e1b4f8b4b52d7be2b28b64"/>
   <project name="platform/system/extras" path="system/extras" revision="0205c49fedf29620165c6b4e6db3d13739c93396"/>
   <project name="platform/system/media" path="system/media" revision="7f17e3995d1588cfcc309b56525652794b6513ef"/>
   <project name="platform/system/netd" path="system/netd" revision="3d298fde142bee3fc4f07f63f16f2d8ce42339c0"/>
   <project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
   <!-- Emulator specific things -->
-  <project name="android-development" path="development" remote="b2g" revision="c99e41d49f0b98eade30814e52c1de9e818def68"/>
+  <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0d5c43228006bae775c4cb57a6d3908484d41718"/>
   <project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="acba00cdb4596c6dcb61ed06f14cf4ec89623539"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="02c32feb2fe97037be0ac4dace3a6a5025ac895d"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8b13bfc1d7d25cee4de55f332654fdba25b8460b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
@@ -92,17 +92,17 @@
   <project name="platform/prebuilt" path="prebuilt" revision="a4062cc40fcaa0776dc880ce591b4c515d36f420"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="507e46e553586bec971551322f20d066c80a0788"/>
   <project name="platform/system/core" path="system/core" revision="91e5551f88aea5aa64e1b4f8b4b52d7be2b28b64"/>
   <project name="platform/system/extras" path="system/extras" revision="0205c49fedf29620165c6b4e6db3d13739c93396"/>
   <project name="platform/system/media" path="system/media" revision="7f17e3995d1588cfcc309b56525652794b6513ef"/>
   <project name="platform/system/netd" path="system/netd" revision="3d298fde142bee3fc4f07f63f16f2d8ce42339c0"/>
   <project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
   <!-- Emulator specific things -->
-  <project name="android-development" path="development" remote="b2g" revision="c99e41d49f0b98eade30814e52c1de9e818def68"/>
+  <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0d5c43228006bae775c4cb57a6d3908484d41718"/>
   <project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
   <project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="acba00cdb4596c6dcb61ed06f14cf4ec89623539"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="02c32feb2fe97037be0ac4dace3a6a5025ac895d"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "eb65c4355e0a16dc5cd203c5c007fd7e3bf0e4b2", 
+    "revision": "c65e0f2f00a05a78a57eecb1185c6420b7984d36", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7cc460af0f6f491d1afa6b6043cdea5025f0e15f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="47111fd2bbe8b0055c8a596959d701391d2a9953"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1820,17 +1820,17 @@ pref("media.gmp-gmpopenh264.provider.ena
 pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");
 
 #ifdef NIGHTLY_BUILD
 pref("browser.polaris.enabled", false);
 pref("privacy.trackingprotection.ui.enabled", false);
 #endif
 
 #ifdef NIGHTLY_BUILD
-pref("browser.tabs.remote.autostart.1", false);
+pref("browser.tabs.remote.autostart.1", true);
 #endif
 
 // Temporary pref to allow printing in e10s windows on some platforms.
 #ifdef UNIX_BUT_NOT_MAC
 pref("print.enable_e10s_testing", false);
 #else
 pref("print.enable_e10s_testing", true);
 #endif
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3305,17 +3305,21 @@
                 // We switched away or closed the browser before we timed
                 // out. We reject, which will cancel the tab switch.
                 aReject();
               }
             };
 
             let timeoutPromise = new Promise((aResolve, aReject) => {
               timeoutId = setTimeout(() => {
-                this._showBusySpinnerRemoteBrowser(toBrowser);
+                if (toBrowser.isRemoteBrowser) {
+                  // The browser's remoteness could have changed since we
+                  // setTimeout so only show the spinner if it's still remote.
+                  this._showBusySpinnerRemoteBrowser(toBrowser);
+                }
                 attemptTabSwitch(aResolve, aReject);
               }, kTabSwitchTimeout);
             });
 
             let paintPromise = new Promise((aResolve, aReject) => {
               let onRemotePaint = () => {
                 toBrowser.removeEventListener("MozAfterRemotePaint", onRemotePaint);
                 this._hideBusySpinnerRemoteBrowser(toBrowser);
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -410,17 +410,17 @@ support-files =
 [browser_selectTabAtIndex.js]
 [browser_star_hsts.js]
 [browser_subframe_favicons_not_used.js]
 [browser_tabDrop.js]
 skip-if = buildapp == 'mulet' || e10s
 [browser_tabMatchesInAwesomebar.js]
 skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls gBrowser.swapBrowsersAndCloseOther)
 [browser_tabMatchesInAwesomebar_perwindowpb.js]
-skip-if = e10s || true # Bug 1093373, Bug 1104755
+skip-if = e10s # Bug 1093373
 [browser_tab_drag_drop_perwindow.js]
 skip-if = buildapp == 'mulet'
 [browser_tab_dragdrop.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls gBrowser.swapBrowsersAndCloseOther)
 [browser_tab_dragdrop2.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls gBrowser.swapBrowsersAndCloseOther)
 [browser_tabbar_big_widgets.js]
 skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
--- a/browser/base/content/test/general/browser_tabMatchesInAwesomebar_perwindowpb.js
+++ b/browser/base/content/test/general/browser_tabMatchesInAwesomebar_perwindowpb.js
@@ -75,17 +75,18 @@ function test() {
         let controller = urlbar.controller;
 
         // Focus URL bar, enter value, and start searching.
         urlbar.focus();
         urlbar.value = testURL;
         controller.startSearch(testURL);
 
         // Wait for the Awesomebar popup to appear.
-        promisePopupShown(aDestWindow.gURLBar.popup).then(() => {
+        let popup = aDestWindow.gURLBar.popup;
+        promisePopupShown(popup).then(() => {
           function searchIsComplete() {
             return controller.searchStatus ==
               Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH;
           }
 
           // Wait until the search is complete.
           waitForCondition(searchIsComplete, function () {
             if (aExpectSwitch) {
@@ -101,18 +102,18 @@ function test() {
             } else {
               // If we don't expect a tab switch then wait for the tab to load.
               testTab.addEventListener("load", function onLoad() {
                 testTab.removeEventListener("load", onLoad, true);
                 executeSoon(aCallback);
               }, true);
             }
 
-            // Select the second match, if any.
-            if (controller.matchCount > 1) {
+            // Make sure the last match is selected.
+            while (popup.selectedIndex < controller.matchCount - 1) {
               controller.handleKeyNavigation(KeyEvent.DOM_VK_DOWN);
             }
 
             // Execute the selected action.
             controller.handleEnter(true);
           });
         });
 
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -360,31 +360,41 @@ function injectLoopAPI(targetWindow) {
       value: function(num, str) {
         return PluralForm.get(num, str);
       }
     },
 
     /**
      * Displays a confirmation dialog using the specified strings.
      *
-     * Callback parameters:
-     * - err null on success, non-null on unexpected failure to show the prompt.
-     * - {Boolean} True if the user chose the OK button.
+     * @param {Object}   options  Confirm dialog options
+     * @param {Function} callback Function that will be invoked once the operation
+     *                            finished. The first argument passed will be an
+     *                            `Error` object or `null`. The second argument
+     *                            will be the result of the operation, TRUE if
+     *                            the user chose the OK button.
      */
     confirm: {
       enumerable: true,
       writable: true,
-      value: function(bodyMessage, okButtonMessage, cancelButtonMessage, callback) {
-        try {
-          let buttonFlags =
+      value: function(options, callback) {
+        let buttonFlags;
+        if (options.okButton && options.cancelButton) {
+          buttonFlags =
             (Ci.nsIPrompt.BUTTON_POS_0 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING) +
             (Ci.nsIPrompt.BUTTON_POS_1 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING);
+        } else if (!options.okButton && !options.cancelButton) {
+          buttonFlags = Services.prompt.STD_YES_NO_BUTTONS;
+        } else {
+          callback(cloneValueInto(new Error("confirm: missing button options"), targetWindow));
+        }
 
+        try {
           let chosenButton = Services.prompt.confirmEx(null, "",
-            bodyMessage, buttonFlags, okButtonMessage, cancelButtonMessage,
+            options.message, buttonFlags, options.okButton, options.cancelButton,
             null, null, {});
 
           callback(null, chosenButton == 0);
         } catch (ex) {
           callback(cloneValueInto(ex, targetWindow));
         }
       }
     },
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -398,35 +398,35 @@ loop.contacts = (function(_, mozL10n) {
     },
 
     handleContactAction: function(contact, actionName) {
       switch (actionName) {
         case "edit":
           this.props.startForm("contacts_edit", contact);
           break;
         case "remove":
-          navigator.mozLoop.confirm(
-            mozL10n.get("confirm_delete_contact_alert"),
-            mozL10n.get("confirm_delete_contact_remove_button"),
-            mozL10n.get("confirm_delete_contact_cancel_button"),
-            (err, result) => {
+          navigator.mozLoop.confirm({
+            message: mozL10n.get("confirm_delete_contact_alert"),
+            okButton: mozL10n.get("confirm_delete_contact_remove_button"),
+            cancelButton: mozL10n.get("confirm_delete_contact_cancel_button")
+          }, (err, result) => {
+            if (err) {
+              throw err;
+            }
+
+            if (!result) {
+              return;
+            }
+
+            navigator.mozLoop.contacts.remove(contact._guid, err => {
               if (err) {
                 throw err;
               }
-
-              if (!result) {
-                return;
-              }
-
-              navigator.mozLoop.contacts.remove(contact._guid, err => {
-                if (err) {
-                  throw err;
-                }
-              });
             });
+          });
           break;
         case "block":
         case "unblock":
           // Invoke the API named like the action.
           navigator.mozLoop.contacts[actionName](contact._guid, err => {
             if (err) {
               throw err;
             }
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -398,35 +398,35 @@ loop.contacts = (function(_, mozL10n) {
     },
 
     handleContactAction: function(contact, actionName) {
       switch (actionName) {
         case "edit":
           this.props.startForm("contacts_edit", contact);
           break;
         case "remove":
-          navigator.mozLoop.confirm(
-            mozL10n.get("confirm_delete_contact_alert"),
-            mozL10n.get("confirm_delete_contact_remove_button"),
-            mozL10n.get("confirm_delete_contact_cancel_button"),
-            (err, result) => {
+          navigator.mozLoop.confirm({
+            message: mozL10n.get("confirm_delete_contact_alert"),
+            okButton: mozL10n.get("confirm_delete_contact_remove_button"),
+            cancelButton: mozL10n.get("confirm_delete_contact_cancel_button")
+          }, (err, result) => {
+            if (err) {
+              throw err;
+            }
+
+            if (!result) {
+              return;
+            }
+
+            navigator.mozLoop.contacts.remove(contact._guid, err => {
               if (err) {
                 throw err;
               }
-
-              if (!result) {
-                return;
-              }
-
-              navigator.mozLoop.contacts.remove(contact._guid, err => {
-                if (err) {
-                  throw err;
-                }
-              });
             });
+          });
           break;
         case "block":
         case "unblock":
           // Invoke the API named like the action.
           navigator.mozLoop.contacts[actionName](contact._guid, err => {
             if (err) {
               throw err;
             }
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -516,16 +516,75 @@ loop.panel = (function(_, mozL10n) {
       return (
         React.DOM.p({className: "user-identity"}, 
           this.props.displayName
         )
       );
     }
   });
 
+  var EditInPlace = React.createClass({displayName: 'EditInPlace',
+    mixins: [React.addons.LinkedStateMixin],
+
+    propTypes: {
+      onChange: React.PropTypes.func.isRequired,
+      text: React.PropTypes.string,
+    },
+
+    getDefaultProps: function() {
+      return {text: ""};
+    },
+
+    getInitialState: function() {
+      return {edit: false, text: this.props.text};
+    },
+
+    handleTextClick: function(event) {
+      event.stopPropagation();
+      event.preventDefault();
+      this.setState({edit: true}, function() {
+        this.getDOMNode().querySelector("input").select();
+      }.bind(this));
+    },
+
+    handleInputClick: function(event) {
+      event.stopPropagation();
+    },
+
+    handleFormSubmit: function(event) {
+      event.preventDefault();
+      this.props.onChange(this.state.text);
+      this.setState({edit: false});
+    },
+
+    cancelEdit: function(event) {
+      event.stopPropagation();
+      event.preventDefault();
+      this.setState({edit: false, text: this.props.text});
+    },
+
+    render: function() {
+      if (!this.state.edit) {
+        return (
+          React.DOM.span({className: "edit-in-place", onClick: this.handleTextClick, 
+                title: mozL10n.get("rooms_name_this_room_tooltip2")}, 
+            this.state.text
+          )
+        );
+      }
+      return (
+        React.DOM.form({onSubmit: this.handleFormSubmit}, 
+          React.DOM.input({type: "text", valueLink: this.linkState("text"), 
+                 onClick: this.handleInputClick, 
+                 onBlur: this.cancelEdit})
+        )
+      );
+    }
+  });
+
   /**
    * Room list entry.
    */
   var RoomEntry = React.createClass({displayName: 'RoomEntry',
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       room:       React.PropTypes.instanceOf(loop.store.Room).isRequired
     },
@@ -534,36 +593,58 @@ loop.panel = (function(_, mozL10n) {
       return { urlCopied: false };
     },
 
     shouldComponentUpdate: function(nextProps, nextState) {
       return (nextProps.room.ctime > this.props.room.ctime) ||
         (nextState.urlCopied !== this.state.urlCopied);
     },
 
-    handleClickRoomUrl: function(event) {
+    handleClickEntry: function(event) {
       event.preventDefault();
       this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
         roomToken: this.props.room.roomToken
       }));
     },
 
     handleCopyButtonClick: function(event) {
+      event.stopPropagation();
       event.preventDefault();
       this.props.dispatcher.dispatch(new sharedActions.CopyRoomUrl({
         roomUrl: this.props.room.roomUrl
       }));
       this.setState({urlCopied: true});
     },
 
     handleDeleteButtonClick: function(event) {
+      event.stopPropagation();
       event.preventDefault();
-      // XXX We should prompt end user for confirmation; see bug 1092953.
-      this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
-        roomToken: this.props.room.roomToken
+      navigator.mozLoop.confirm({
+        message: mozL10n.get("rooms_list_deleteConfirmation_label"),
+        okButton: null,
+        cancelButton: null
+      }, function(err, result) {
+        if (err) {
+          throw err;
+        }
+
+        if (!result) {
+          return;
+        }
+
+        this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
+          roomToken: this.props.room.roomToken
+        }));
+      }.bind(this));
+    },
+
+    renameRoom: function(newRoomName) {
+      this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
+        roomToken: this.props.room.roomToken,
+        newRoomName: newRoomName
       }));
     },
 
     handleMouseLeave: function(event) {
       this.setState({urlCopied: false});
     },
 
     _isActive: function() {
@@ -577,32 +658,29 @@ loop.panel = (function(_, mozL10n) {
         "room-active": this._isActive()
       });
       var copyButtonClasses = React.addons.classSet({
         "copy-link": true,
         "checked": this.state.urlCopied
       });
 
       return (
-        React.DOM.div({className: roomClasses, onMouseLeave: this.handleMouseLeave}, 
+        React.DOM.div({className: roomClasses, onMouseLeave: this.handleMouseLeave, 
+             onClick: this.handleClickEntry}, 
           React.DOM.h2(null, 
             React.DOM.span({className: "room-notification"}), 
-            room.roomName, 
+            EditInPlace({text: room.roomName, onChange: this.renameRoom}), 
             React.DOM.button({className: copyButtonClasses, 
               title: mozL10n.get("rooms_list_copy_url_tooltip"), 
               onClick: this.handleCopyButtonClick}), 
             React.DOM.button({className: "delete-link", 
               title: mozL10n.get("rooms_list_delete_tooltip"), 
               onClick: this.handleDeleteButtonClick})
           ), 
-          React.DOM.p(null, 
-            React.DOM.a({href: "#", onClick: this.handleClickRoomUrl}, 
-              room.roomUrl
-            )
-          )
+          React.DOM.p(null, React.DOM.a({href: "#"}, room.roomUrl))
         )
       );
     }
   });
 
   /**
    * Room list.
    */
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -516,16 +516,75 @@ loop.panel = (function(_, mozL10n) {
       return (
         <p className="user-identity">
           {this.props.displayName}
         </p>
       );
     }
   });
 
+  var EditInPlace = React.createClass({
+    mixins: [React.addons.LinkedStateMixin],
+
+    propTypes: {
+      onChange: React.PropTypes.func.isRequired,
+      text: React.PropTypes.string,
+    },
+
+    getDefaultProps: function() {
+      return {text: ""};
+    },
+
+    getInitialState: function() {
+      return {edit: false, text: this.props.text};
+    },
+
+    handleTextClick: function(event) {
+      event.stopPropagation();
+      event.preventDefault();
+      this.setState({edit: true}, function() {
+        this.getDOMNode().querySelector("input").select();
+      }.bind(this));
+    },
+
+    handleInputClick: function(event) {
+      event.stopPropagation();
+    },
+
+    handleFormSubmit: function(event) {
+      event.preventDefault();
+      this.props.onChange(this.state.text);
+      this.setState({edit: false});
+    },
+
+    cancelEdit: function(event) {
+      event.stopPropagation();
+      event.preventDefault();
+      this.setState({edit: false, text: this.props.text});
+    },
+
+    render: function() {
+      if (!this.state.edit) {
+        return (
+          <span className="edit-in-place" onClick={this.handleTextClick}
+                title={mozL10n.get("rooms_name_this_room_tooltip2")}>
+            {this.state.text}
+          </span>
+        );
+      }
+      return (
+        <form onSubmit={this.handleFormSubmit}>
+          <input type="text" valueLink={this.linkState("text")}
+                 onClick={this.handleInputClick}
+                 onBlur={this.cancelEdit} />
+        </form>
+      );
+    }
+  });
+
   /**
    * Room list entry.
    */
   var RoomEntry = React.createClass({
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       room:       React.PropTypes.instanceOf(loop.store.Room).isRequired
     },
@@ -534,36 +593,58 @@ loop.panel = (function(_, mozL10n) {
       return { urlCopied: false };
     },
 
     shouldComponentUpdate: function(nextProps, nextState) {
       return (nextProps.room.ctime > this.props.room.ctime) ||
         (nextState.urlCopied !== this.state.urlCopied);
     },
 
-    handleClickRoomUrl: function(event) {
+    handleClickEntry: function(event) {
       event.preventDefault();
       this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
         roomToken: this.props.room.roomToken
       }));
     },
 
     handleCopyButtonClick: function(event) {
+      event.stopPropagation();
       event.preventDefault();
       this.props.dispatcher.dispatch(new sharedActions.CopyRoomUrl({
         roomUrl: this.props.room.roomUrl
       }));
       this.setState({urlCopied: true});
     },
 
     handleDeleteButtonClick: function(event) {
+      event.stopPropagation();
       event.preventDefault();
-      // XXX We should prompt end user for confirmation; see bug 1092953.
-      this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
-        roomToken: this.props.room.roomToken
+      navigator.mozLoop.confirm({
+        message: mozL10n.get("rooms_list_deleteConfirmation_label"),
+        okButton: null,
+        cancelButton: null
+      }, function(err, result) {
+        if (err) {
+          throw err;
+        }
+
+        if (!result) {
+          return;
+        }
+
+        this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
+          roomToken: this.props.room.roomToken
+        }));
+      }.bind(this));
+    },
+
+    renameRoom: function(newRoomName) {
+      this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
+        roomToken: this.props.room.roomToken,
+        newRoomName: newRoomName
       }));
     },
 
     handleMouseLeave: function(event) {
       this.setState({urlCopied: false});
     },
 
     _isActive: function() {
@@ -577,32 +658,29 @@ loop.panel = (function(_, mozL10n) {
         "room-active": this._isActive()
       });
       var copyButtonClasses = React.addons.classSet({
         "copy-link": true,
         "checked": this.state.urlCopied
       });
 
       return (
-        <div className={roomClasses} onMouseLeave={this.handleMouseLeave}>
+        <div className={roomClasses} onMouseLeave={this.handleMouseLeave}
+             onClick={this.handleClickEntry}>
           <h2>
             <span className="room-notification" />
-            {room.roomName}
+            <EditInPlace text={room.roomName} onChange={this.renameRoom} />
             <button className={copyButtonClasses}
               title={mozL10n.get("rooms_list_copy_url_tooltip")}
               onClick={this.handleCopyButtonClick} />
             <button className="delete-link"
               title={mozL10n.get("rooms_list_delete_tooltip")}
               onClick={this.handleDeleteButtonClick} />
           </h2>
-          <p>
-            <a href="#" onClick={this.handleClickRoomUrl}>
-              {room.roomUrl}
-            </a>
-          </p>
+          <p><a href="#">{room.roomUrl}</a></p>
         </div>
       );
     }
   });
 
   /**
    * Room list.
    */
--- a/browser/components/loop/content/shared/css/panel.css
+++ b/browser/components/loop/content/shared/css/panel.css
@@ -199,19 +199,21 @@ body {
 }
 
 .room-list:empty {
   border-bottom-width: 0;
 }
 
 .room-list > .room-entry {
   padding: .5rem 1rem;
+  cursor: pointer;
 }
 
 .room-list > .room-entry > h2 {
+  display: inline-block;
   font-size: .85rem;
   color: #777;
 }
 
 .room-list > .room-entry.room-active > h2 {
   font-weight: bold;
   color: #000;
 }
@@ -293,26 +295,43 @@ body {
 
 .room-list > .room-entry > h2 > .copy-link.checked {
   background: transparent url(../img/icons-16x16.svg#checkmark);
   animation: pulse .150s;
   animation-timing-function: ease-in-out;
   top: 0px;
 }
 
-.room-list > .room-entry > h2 {
-  display: inline-block;
-}
-
 /* keep the various room-entry row pieces aligned with each other */
 .room-list > .room-entry > h2 > button,
 .room-list > .room-entry > h2 > span {
   vertical-align: middle;
 }
 
+/* Edit in place */
+
+.room-list > .room-entry > h2 > .edit-in-place {
+  cursor: text;
+}
+
+.room-list > .room-entry > h2 > .edit-in-place:hover {
+  background: #fefbc4;
+}
+
+.room-list > .room-entry > h2 > form {
+  display: inline-block;
+}
+
+.room-list > .room-entry > h2 > form > input {
+  border: none;
+  background: #fefbc4;
+  font-size: 13.6px;
+  padding: 0;
+}
+
 /* Buttons */
 
 .button-group {
   display: flex;
   flex-direction: row;
   width: 100%;
 }
 
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -49,17 +49,18 @@ describe("loop.panel", function() {
         },
         on: sandbox.stub()
       },
       rooms: {
         getAll: function(version, callback) {
           callback(null, []);
         },
         on: sandbox.stub()
-      }
+      },
+      confirm: sandbox.stub()
     };
 
     document.mozL10n.initialize(navigator.mozLoop);
     // XXX prevent a race whenever mozL10n hasn't been initialized yet
     setTimeout(done, 0);
   });
 
   afterEach(function() {
@@ -709,16 +710,52 @@ describe("loop.panel", function() {
         ctime: 1405517418
       };
     });
 
     function mountRoomEntry(props) {
       return TestUtils.renderIntoDocument(loop.panel.RoomEntry(props));
     }
 
+    describe("Edit room name", function() {
+      var roomEntry, domNode;
+
+      beforeEach(function() {
+        roomEntry = mountRoomEntry({
+          dispatcher: dispatcher,
+          deleteRoom: sandbox.stub(),
+          room: new loop.store.Room(roomData)
+        });
+        domNode = roomEntry.getDOMNode();
+
+        TestUtils.Simulate.click(domNode.querySelector(".edit-in-place"));
+      });
+
+      it("should render an edit form on room name click", function() {
+        expect(domNode.querySelector("form")).not.eql(null);
+        expect(domNode.querySelector("input").value).eql(roomData.roomName);
+      });
+
+      it("should dispatch a RenameRoom action when submitting the form",
+        function() {
+          var dispatch = sandbox.stub(dispatcher, "dispatch");
+
+          TestUtils.Simulate.change(domNode.querySelector("input"), {
+            target: {value: "New name"}
+          });
+          TestUtils.Simulate.submit(domNode.querySelector("form"));
+
+          sinon.assert.calledOnce(dispatch);
+          sinon.assert.calledWithExactly(dispatch, new sharedActions.RenameRoom({
+            roomToken: roomData.roomToken,
+            newRoomName: "New name"
+          }));
+        });
+    });
+
     describe("Copy button", function() {
       var roomEntry, copyButton;
 
       beforeEach(function() {
         roomEntry = mountRoomEntry({
           dispatcher: dispatcher,
           deleteRoom: sandbox.stub(),
           room: new loop.store.Room(roomData)
@@ -774,25 +811,37 @@ describe("loop.panel", function() {
         });
         deleteButton = roomEntry.getDOMNode().querySelector("button.delete-link");
       });
 
       it("should not display a delete button by default", function() {
         expect(deleteButton).to.not.equal(null);
       });
 
-      it("should call the delete function when clicked", function() {
+      it("should dispatch a delete action when confirmation is granted", function() {
         sandbox.stub(dispatcher, "dispatch");
 
+        navigator.mozLoop.confirm.callsArgWith(1, null, true);
         TestUtils.Simulate.click(deleteButton);
 
+        sinon.assert.calledOnce(navigator.mozLoop.confirm);
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.DeleteRoom({roomToken: roomData.roomToken}));
       });
+
+      it("should not dispatch an action when the confirmation is cancelled", function() {
+        sandbox.stub(dispatcher, "dispatch");
+
+        navigator.mozLoop.confirm.callsArgWith(1, null, false);
+        TestUtils.Simulate.click(deleteButton);
+
+        sinon.assert.calledOnce(navigator.mozLoop.confirm);
+        sinon.assert.notCalled(dispatcher.dispatch);
+      });
     });
 
     describe("Room URL click", function() {
       var roomEntry;
 
       it("should dispatch an OpenRoom action", function() {
         sandbox.stub(dispatcher, "dispatch");
         roomEntry = mountRoomEntry({
--- a/browser/components/loop/ui/fake-mozLoop.js
+++ b/browser/components/loop/ui/fake-mozLoop.js
@@ -46,19 +46,22 @@ var fakeRooms = [
 /**
  * Faking the mozLoop object which doesn't exist in regular web pages.
  * @type {Object}
  */
 navigator.mozLoop = {
   ensureRegistered: function() {},
   getAudioBlob: function(){},
   getLoopPref: function(pref) {
-    // Ensure UI for rooms is displayed in the showcase.
-    if (pref === "rooms.enabled") {
-      return true;
+    switch(pref) {
+      // Ensure UI for rooms is displayed in the showcase.
+      case "rooms.enabled":
+      // Ensure we skip FTE completely.
+      case "gettingStarted.seen":
+        return true;
     }
   },
   setLoopPref: function(){},
   releaseCallData: function() {},
   copyString: function() {},
   contacts: {
     getAll: function(callback) {
       callback(null, []);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2464,17 +2464,17 @@ let DefaultBrowserCheck = {
     }
   },
 };
 
 #ifdef E10S_TESTING_ONLY
 let E10SUINotification = {
   // Increase this number each time we want to roll out an
   // e10s testing period to Nightly users.
-  CURRENT_NOTICE_COUNT: 2,
+  CURRENT_NOTICE_COUNT: 3,
   CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
   PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
 
   checkStatus: function() {
     let skipE10sChecks = false;
     try {
       skipE10sChecks = (UpdateChannel.get() != "nightly") ||
                        Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y");
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -118,18 +118,25 @@ StyleEditorUI.prototype = {
   initialize: function() {
     return Task.spawn(function*() {
       let toolbox = gDevTools.getToolbox(this._target);
       yield toolbox.initInspector();
       this._walker = toolbox.walker;
 
       let hUtils = toolbox.highlighterUtils;
       if (hUtils.hasCustomHighlighter(SELECTOR_HIGHLIGHTER_TYPE)) {
-        this._highlighter =
-          yield hUtils.getHighlighterByType(SELECTOR_HIGHLIGHTER_TYPE);
+        try {
+          this._highlighter =
+            yield hUtils.getHighlighterByType(SELECTOR_HIGHLIGHTER_TYPE);
+        } catch (e) {
+          // The selectorHighlighter can't always be instantiated, for example
+          // it doesn't work with XUL windows (until bug 1094959 gets fixed).
+          console.warn("The selectorHighlighter couldn't be instantiated, " +
+            "elements matching hovered selectors will not be highlighted");
+        }
       }
     }.bind(this)).then(() => {
       this.createUI();
       this._debuggee.getStyleSheets().then((styleSheets) => {
         this._resetStyleSheetList(styleSheets); 
         this._target.on("will-navigate", this._clear);
         this._target.on("navigate", this._onNewDocument);
       });
--- a/browser/devtools/styleeditor/test/browser.ini
+++ b/browser/devtools/styleeditor/test/browser.ini
@@ -68,8 +68,9 @@ skip-if = e10s # Bug 1055333 - style edi
 [browser_styleeditor_reload.js]
 [browser_styleeditor_sv_keynav.js]
 [browser_styleeditor_sv_resize.js]
 [browser_styleeditor_selectstylesheet.js]
 [browser_styleeditor_sourcemaps.js]
 [browser_styleeditor_sourcemap_watching.js]
 skip-if = e10s # Bug 1055333 - style editor tests disabled with e10s
 [browser_styleeditor_transition_rule.js]
+[browser_styleeditor_xul.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_xul.js
@@ -0,0 +1,20 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the style-editor initializes correctly for XUL windows.
+
+waitForExplicitFinish();
+
+const TEST_URL = "about:config";
+
+let test = asyncTest(function*() {
+  let tab = yield addTab(TEST_URL);
+  let target = TargetFactory.forTab(tab);
+
+  let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
+  let panel = toolbox.getCurrentPanel();
+  yield panel.UI.once("editor-added");
+
+  ok(panel, "The style-editor panel did initialize correctly for the XUL window");
+});
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -260,16 +260,17 @@ skip-if = buildapp == 'mulet'
 [browser_webconsole_bug_804845_ctrl_key_nav.js]
 run-if = os == "mac"
 [browser_webconsole_bug_817834_add_edited_input_to_history.js]
 [browser_webconsole_bug_837351_securityerrors.js]
 skip-if = buildapp == 'mulet'
 [browser_webconsole_bug_846918_hsts_invalid-headers.js]
 skip-if = buildapp == 'mulet'
 [browser_webconsole_bug_915141_toggle_response_logging_with_keyboard.js]
+[browser_webconsole_filter_buttons_contextmenu.js]
 [browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
 [browser_webconsole_bug_1010953_cspro.js]
 [browser_webconsole_certificate_messages.js]
 [browser_webconsole_cached_autocomplete.js]
 [browser_webconsole_change_font_size.js]
 [browser_webconsole_chrome.js]
 [browser_webconsole_clickable_urls.js]
 [browser_webconsole_closure_inspection.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the filter button context menu logic works correctly.
+
+const TEST_URI = "http://example.com/";
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, testFilterButtons);
+  }, true);
+}
+
+function testFilterButtons(aHud) {
+  let hudBox = aHud.ui.rootElement;
+
+  testRightClick("net", hudBox, aHud)
+    .then(() => testRightClick("css", hudBox, aHud))
+    .then(() => testRightClick("js", hudBox, aHud))
+    .then(() => testRightClick("logging", hudBox, aHud))
+    .then(() => testRightClick("security", hudBox, aHud))
+    .then(finishTest);
+}
+
+function testRightClick(aCategory, hudBox, aHud) {
+  let deferred = promise.defer();
+  let selector = ".webconsole-filter-button[category=\"" + aCategory + "\"]";
+  let button = hudBox.querySelector(selector);
+  let mainButton = getMainButton(button, aHud);
+  let origCheckedState = button.getAttribute("aria-pressed");
+  let contextMenu = aHud.iframeWindow.document.getElementById(aCategory + "-contextmenu");
+
+  function verifyContextMenuIsClosed() {
+    info("verify the context menu is closed");
+    is(button.getAttribute("open"), false, "The context menu for the \"" + aCategory +
+      "\" button is closed");
+  }
+
+  function verifyOriginalCheckedState() {
+    info("verify the button has the original checked state");
+    is(button.getAttribute("aria-pressed"), origCheckedState,
+      "The button state should not have changed");
+  };
+
+  function verifyNewCheckedState() {
+    info("verify the button's checked state has changed");
+    isnot(button.getAttribute("aria-pressed"), origCheckedState,
+      "The button state should have changed");
+  };
+
+  function leftClickToClose() {
+    info("left click the button to close the contextMenu");
+    EventUtils.sendMouseEvent({type: "click"}, button);
+    executeSoon(() => {
+      verifyContextMenuIsClosed();
+      verifyOriginalCheckedState();
+      leftClickToChangeCheckedState();
+    });
+  }
+
+  function leftClickToChangeCheckedState() {
+    info("left click the mainbutton to change checked state");
+    EventUtils.sendMouseEvent({type: "click"}, mainButton);
+    executeSoon(() => {
+      verifyContextMenuIsClosed();
+      verifyNewCheckedState();
+      deferred.resolve();
+    });
+  }
+
+  verifyContextMenuIsClosed();
+  info("right click the button to open the context menu");
+  waitForContextMenu(contextMenu, mainButton, verifyOriginalCheckedState,
+                     leftClickToClose);
+  return deferred.promise;
+}
+
+function getMainButton(aTargetButton, aHud) {
+  let anonymousNodes = aHud.ui.document.getAnonymousNodes(aTargetButton);
+  let subbutton;
+
+  for (let i = 0; i < anonymousNodes.length; i++) {
+    let node = anonymousNodes[i];
+    if (node.classList.contains("toolbarbutton-menubutton-button")) {
+      subbutton = node;
+      break;
+    }
+  }
+
+  return subbutton;
+}
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -685,16 +685,19 @@ WebConsoleFrame.prototype = {
    *        "category", and "prefKey" properties, and optionally a "severities"
    *        property.
    */
   _initFilterButtons: function WCF__initFilterButtons()
   {
     let categories = this.document
                      .querySelectorAll(".webconsole-filter-button[category]");
     Array.forEach(categories, function(aButton) {
+      aButton.addEventListener("contextmenu", (aEvent) => {
+        aButton.open = true;
+      }, false);
       aButton.addEventListener("click", this._toggleFilter, false);
 
       let someChecked = false;
       let severities = aButton.querySelectorAll("menuitem[prefKey]");
       Array.forEach(severities, function(aMenuItem) {
         aMenuItem.addEventListener("command", this._toggleFilter, false);
 
         let prefKey = aMenuItem.getAttribute("prefKey");
@@ -806,17 +809,19 @@ WebConsoleFrame.prototype = {
    * @private
    * @param nsIDOMEvent aEvent
    *        The event that triggered the filter change.
    */
   _toggleFilter: function WCF__toggleFilter(aEvent)
   {
     let target = aEvent.target;
     let tagName = target.tagName;
-    if (tagName != aEvent.currentTarget.tagName) {
+    // Prevent toggle if generated from a contextmenu event (right click)
+    let isRightClick = (aEvent.button === 2); // right click is button 2;
+    if (tagName != aEvent.currentTarget.tagName || isRightClick) {
       return;
     }
 
     switch (tagName) {
       case "toolbarbutton": {
         let originalTarget = aEvent.originalTarget;
         let classes = originalTarget.classList;
 
@@ -5390,9 +5395,8 @@ ConsoleContextMenu.prototype = {
   destroy: function CCM_destroy()
   {
     this.popup.removeEventListener("popupshowing", this.build);
     this.popup = null;
     this.owner = null;
     this.lastClickedMessage = null;
   },
 };
-
--- a/browser/devtools/webconsole/webconsole.xul
+++ b/browser/devtools/webconsole/webconsole.xul
@@ -88,74 +88,74 @@ function goUpdateConsoleCommands() {
       <toolbar class="hud-console-filter-toolbar devtools-toolbar" mode="full">
         <hbox class="devtools-toolbarbutton-group">
           <toolbarbutton label="&btnPageNet.label;" type="menu-button"
                          category="net" class="devtools-toolbarbutton webconsole-filter-button"
                          tooltiptext="&btnPageNet.tooltip;"
                          accesskeyMacOSX="&btnPageNet.accesskeyMacOSX;"
                          accesskey="&btnPageNet.accesskey;"
                          tabindex="3">
-            <menupopup>
+            <menupopup id="net-contextmenu">
               <menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
                         prefKey="network"/>
               <menuitem label="&btnConsoleWarnings;" type="checkbox" autocheck="false"
                         prefKey="netwarn"/>
               <menuitem label="&btnConsoleLog;" type="checkbox" autocheck="false"
                         prefKey="networkinfo"/>
               <menuseparator id="saveBodiesSeparator" />
               <menuitem id="saveBodies" type="checkbox" label="&saveBodies.label;"
                         accesskey="&saveBodies.accesskey;"/>
             </menupopup>
           </toolbarbutton>
           <toolbarbutton label="&btnPageCSS.label;" type="menu-button"
                          category="css" class="devtools-toolbarbutton webconsole-filter-button"
                          tooltiptext="&btnPageCSS.tooltip2;"
                          accesskey="&btnPageCSS.accesskey;"
                          tabindex="4">
-            <menupopup>
+            <menupopup id="css-contextmenu">
               <menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
                         prefKey="csserror"/>
               <menuitem label="&btnConsoleWarnings;" type="checkbox"
                         autocheck="false" prefKey="cssparser"/>
               <menuitem label="&btnConsoleReflows;" type="checkbox"
                         autocheck="false" prefKey="csslog"/>
             </menupopup>
           </toolbarbutton>
           <toolbarbutton label="&btnPageJS.label;" type="menu-button"
                          category="js" class="devtools-toolbarbutton webconsole-filter-button"
                          tooltiptext="&btnPageJS.tooltip;"
                          accesskey="&btnPageJS.accesskey;"
                          tabindex="5">
-            <menupopup>
+            <menupopup id="js-contextmenu">
               <menuitem label="&btnConsoleErrors;" type="checkbox"
                         autocheck="false" prefKey="exception"/>
               <menuitem label="&btnConsoleWarnings;" type="checkbox"
                         autocheck="false" prefKey="jswarn"/>
               <menuitem label="&btnConsoleLog;" type="checkbox"
                         autocheck="false" prefKey="jslog"/>
             </menupopup>
           </toolbarbutton>
           <toolbarbutton label="&btnPageSecurity.label;" type="menu-button"
                          category="security" class="devtools-toolbarbutton webconsole-filter-button"
                          tooltiptext="&btnPageSecurity.tooltip;"
                          accesskey="&btnPageSecurity.accesskey;"
                          tabindex="6">
-            <menupopup>
+            <menupopup id="security-contextmenu">
               <menuitem label="&btnConsoleErrors;" type="checkbox"
                         autocheck="false" prefKey="secerror"/>
               <menuitem label="&btnConsoleWarnings;" type="checkbox"
                         autocheck="false" prefKey="secwarn"/>
             </menupopup>
           </toolbarbutton>
           <toolbarbutton label="&btnPageLogging.label;" type="menu-button"
                          category="logging" class="devtools-toolbarbutton webconsole-filter-button"
                          tooltiptext="&btnPageLogging.tooltip;"
                          accesskey="&btnPageLogging.accesskey3;"
                          tabindex="7">
-            <menupopup>
+            <menupopup id="logging-contextmenu">
               <menuitem label="&btnConsoleErrors;" type="checkbox"
                         autocheck="false" prefKey="error"/>
               <menuitem label="&btnConsoleWarnings;" type="checkbox"
                         autocheck="false" prefKey="warn"/>
               <menuitem label="&btnConsoleInfo;" type="checkbox" autocheck="false"
                         prefKey="info"/>
               <menuitem label="&btnConsoleLog;" type="checkbox" autocheck="false"
                         prefKey="log"/>
rename from dom/bluetooth/bluedroid/BluetoothUtils.cpp
rename to dom/bluetooth/BluetoothUtils.cpp
--- a/dom/bluetooth/bluedroid/BluetoothUtils.cpp
+++ b/dom/bluetooth/BluetoothUtils.cpp
@@ -1,41 +1,25 @@
 /* -*- 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 "base/basictypes.h"
-
+#include "BluetoothUtils.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
-#include "BluetoothServiceBluedroid.h"
-#include "BluetoothUtils.h"
 #include "jsapi.h"
-#include "mozilla/Scoped.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "nsContentUtils.h"
-#include "nsIScriptContext.h"
 #include "nsISystemMessagesInternal.h"
-#include "nsString.h"
-#include "nsTArray.h"
 #include "nsServiceManagerUtils.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
-uint16_t
-UuidToServiceClassInt(const BluetoothUuid& mUuid)
-{
-  // extract short UUID 0000xxxx-0000-1000-8000-00805f9b34fb
-  uint16_t shortUuid;
-  memcpy(&shortUuid, mUuid.mUuid + 2, sizeof(uint16_t));
-  return ntohs(shortUuid);
-}
-
 bool
 SetJsObject(JSContext* aContext,
             const BluetoothValue& aValue,
             JS::Handle<JSObject*> aObj)
 {
   MOZ_ASSERT(aContext && aObj);
 
   if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
@@ -118,16 +102,47 @@ BroadcastSystemMessage(const nsAString& 
   }
 
   systemMessenger->BroadcastMessage(aType, value,
                                     JS::UndefinedHandleValue);
 
   return true;
 }
 
+bool
+BroadcastSystemMessage(const nsAString& aType,
+                       const InfallibleTArray<BluetoothNamedValue>& aData)
+{
+  mozilla::AutoSafeJSContext cx;
+  NS_ASSERTION(!::JS_IsExceptionPending(cx),
+      "Shouldn't get here when an exception is pending!");
+
+  JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(),
+                                             JS::NullPtr()));
+  if (!obj) {
+    BT_WARNING("Failed to new JSObject for system message!");
+    return false;
+  }
+
+  if (!SetJsObject(cx, aData, obj)) {
+    BT_WARNING("Failed to set properties of system message!");
+    return false;
+  }
+
+  nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
+    do_GetService("@mozilla.org/system-message-internal;1");
+  NS_ENSURE_TRUE(systemMessenger, false);
+
+  JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
+  systemMessenger->BroadcastMessage(aType, value,
+                                    JS::UndefinedHandleValue);
+
+  return true;
+}
+
 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()) {
rename from dom/bluetooth/bluedroid/BluetoothUtils.h
rename to dom/bluetooth/BluetoothUtils.h
--- a/dom/bluetooth/bluedroid/BluetoothUtils.h
+++ b/dom/bluetooth/BluetoothUtils.h
@@ -1,38 +1,39 @@
 /* -*- 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_dom_bluetooth_bluetoothutils_h__
-#define mozilla_dom_bluetooth_bluetoothutils_h__
+#ifndef mozilla_dom_bluetooth_bluetoothutils_h
+#define mozilla_dom_bluetooth_bluetoothutils_h
 
 #include "BluetoothCommon.h"
 #include "js/TypeDecls.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothNamedValue;
 class BluetoothValue;
 class BluetoothReplyRunnable;
 
-uint16_t
-UuidToServiceClassInt(const BluetoothUuid& mUuid);
-
 bool
 SetJsObject(JSContext* aContext,
             const BluetoothValue& aValue,
             JS::Handle<JSObject*> aObj);
 
 bool
 BroadcastSystemMessage(const nsAString& aType,
                        const BluetoothValue& aData);
 
+bool
+BroadcastSystemMessage(const nsAString& aType,
+                       const InfallibleTArray<BluetoothNamedValue>& aData);
+
 void
 DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
                        const BluetoothValue& aValue,
                        const nsAString& aErrorStr);
 
 void
 DispatchStatusChangedEvent(const nsAString& aType,
                            const nsAString& aDeviceAddress,
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -1093,16 +1093,25 @@ BluetoothServiceBluedroid::IgnoreWaiting
 {
 }
 
 void
 BluetoothServiceBluedroid::ToggleCalls(BluetoothReplyRunnable* aRunnable)
 {
 }
 
+uint16_t
+BluetoothServiceBluedroid::UuidToServiceClassInt(const BluetoothUuid& mUuid)
+{
+  // extract short UUID 0000xxxx-0000-1000-8000-00805f9b34fb
+  uint16_t shortUuid;
+  memcpy(&shortUuid, mUuid.mUuid + 2, sizeof(uint16_t));
+  return ntohs(shortUuid);
+}
+
 //
 // Bluetooth notifications
 //
 
 /* |ProfileDeinitResultHandler| collect the results of all profile
  * result handlers and calls |Proceed| after all results handlers
  * have been run.
  */
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.h
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.h
@@ -216,14 +216,16 @@ protected:
   static nsresult StartGonkBluetooth();
   static nsresult StopGonkBluetooth();
   static bool EnsureBluetoothHalLoad();
 
   static void ClassToIcon(uint32_t aClass, nsAString& aRetIcon);
 
   static ControlPlayStatus PlayStatusStringToControlPlayStatus(
     const nsAString& aPlayStatus);
+
+  uint16_t UuidToServiceClassInt(const BluetoothUuid& mUuid);
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
 
--- a/dom/bluetooth/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp
@@ -387,16 +387,47 @@ BluetoothDBusService::BluetoothDBusServi
 }
 
 BluetoothDBusService::~BluetoothDBusService()
 {
   sStopBluetoothMonitor = nullptr;
   sGetPropertyMonitor = nullptr;
 }
 
+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, according
+  // 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, according
+  // to the example above, it's /org/bluez/2906/hci0.
+  nsString address(aObjectPath);
+  int addressHead = address.RFind("/") + 5;
+
+  MOZ_ASSERT(addressHead + BLUETOOTH_ADDRESS_LENGTH == (int)address.Length());
+
+  address.Cut(0, addressHead);
+  address.ReplaceChar('_', ':');
+
+  return address;
+}
+
 static bool
 GetConnectedDevicesFilter(const BluetoothValue& aValue)
 {
   // We don't have to filter device here
   return true;
 }
 
 static bool
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -685,16 +685,38 @@ BluetoothHfpManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sInShutdown = true;
   Disconnect(nullptr);
   DisconnectSco();
   sBluetoothHfpManager = nullptr;
 }
 
+void
+BluetoothHfpManager::ParseAtCommand(const nsACString& aAtCommand,
+                                    const int aStart,
+                                    nsTArray<nsCString>& aRetValues)
+{
+  int length = aAtCommand.Length();
+  int begin = aStart;
+
+  for (int i = aStart; i < length; ++i) {
+    // Use ',' as separator
+    if (aAtCommand[i] == ',') {
+      nsCString tmp(nsDependentCSubstring(aAtCommand, begin, i - begin));
+      aRetValues.AppendElement(tmp);
+
+      begin = i + 1;
+    }
+  }
+
+  nsCString tmp(nsDependentCSubstring(aAtCommand, begin));
+  aRetValues.AppendElement(tmp);
+}
+
 // Virtual function of class SocketConsumer
 void
 BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
                                        nsAutoPtr<UnixSocketRawData>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSocket);
 
--- a/dom/bluetooth/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.h
@@ -127,16 +127,19 @@ public:
   // CDMA-specific functions
   void UpdateSecondNumber(const nsAString& aNumber);
   void AnswerWaitingCall();
   void IgnoreWaitingCall();
   void ToggleCalls();
 #endif
 
 private:
+  void ParseAtCommand(const nsACString& aAtCommand, const int aStart,
+                      nsTArray<nsCString>& aRetValues);
+
   class CloseScoTask;
   class GetVolumeTask;
 #ifdef MOZ_B2G_RIL
   class RespondToBLDNTask;
   class SendRingIndicatorTask;
 #endif
 
   friend class CloseScoTask;
deleted file mode 100644
--- a/dom/bluetooth/bluez/BluetoothUtils.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-/* -*- 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 "base/basictypes.h"
-
-#include "BluetoothReplyRunnable.h"
-#include "BluetoothService.h"
-#include "BluetoothUtils.h"
-#include "jsapi.h"
-#include "mozilla/Scoped.h"
-#include "mozilla/dom/bluetooth/BluetoothTypes.h"
-#include "nsContentUtils.h"
-#include "nsIScriptContext.h"
-#include "nsISystemMessagesInternal.h"
-#include "nsString.h"
-#include "nsTArray.h"
-#include "nsServiceManagerUtils.h"
-
-BEGIN_BLUETOOTH_NAMESPACE
-
-bool
-SetJsObject(JSContext* aContext,
-            const BluetoothValue& aValue,
-            JS::Handle<JSObject*> aObj)
-{
-  MOZ_ASSERT(aContext && aObj);
-
-  if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
-    BT_WARNING("SetJsObject: Invalid parameter type");
-    return false;
-  }
-
-  const nsTArray<BluetoothNamedValue>& arr =
-    aValue.get_ArrayOfBluetoothNamedValue();
-
-  for (uint32_t i = 0; i < arr.Length(); i++) {
-    JS::Rooted<JS::Value> val(aContext);
-    const BluetoothValue& v = arr[i].value();
-
-    switch(v.type()) {
-       case BluetoothValue::TnsString: {
-        JSString* jsData = JS_NewUCStringCopyN(aContext,
-                                     v.get_nsString().BeginReading(),
-                                     v.get_nsString().Length());
-        NS_ENSURE_TRUE(jsData, false);
-        val = STRING_TO_JSVAL(jsData);
-        break;
-      }
-      case BluetoothValue::Tuint32_t:
-        val = INT_TO_JSVAL(v.get_uint32_t());
-        break;
-      case BluetoothValue::Tbool:
-        val = BOOLEAN_TO_JSVAL(v.get_bool());
-        break;
-      default:
-        BT_WARNING("SetJsObject: Parameter is not handled");
-        break;
-    }
-
-    if (!JS_SetProperty(aContext, aObj,
-                        NS_ConvertUTF16toUTF8(arr[i].name()).get(),
-                        val)) {
-      BT_WARNING("Failed to set property");
-      return false;
-    }
-  }
-
-  return true;
-}
-
-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, according
-  // to the example above, it's /org/bluez/2906/hci0.
-  nsString devicePath(aAdapterPath);
-  devicePath.AppendLiteral("/dev_");
-  devicePath.Append(aDeviceAddress);
-  devicePath.ReplaceChar(':', '_');
-  return devicePath;
-}
-
-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, according
-  // to the example above, it's /org/bluez/2906/hci0.
-  nsString address(aObjectPath);
-  int addressHead = address.RFind("/") + 5;
-
-  MOZ_ASSERT(addressHead + BLUETOOTH_ADDRESS_LENGTH == (int)address.Length());
-
-  address.Cut(0, addressHead);
-  address.ReplaceChar('_', ':');
-
-  return address;
-}
-
-bool
-BroadcastSystemMessage(const nsAString& aType,
-                       const InfallibleTArray<BluetoothNamedValue>& aData)
-{
-  mozilla::AutoSafeJSContext cx;
-  NS_ASSERTION(!::JS_IsExceptionPending(cx),
-      "Shouldn't get here when an exception is pending!");
-
-  JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(),
-                                             JS::NullPtr()));
-  if (!obj) {
-    BT_WARNING("Failed to new JSObject for system message!");
-    return false;
-  }
-
-  if (!SetJsObject(cx, aData, obj)) {
-    BT_WARNING("Failed to set properties of system message!");
-    return false;
-  }
-
-  nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
-    do_GetService("@mozilla.org/system-message-internal;1");
-  NS_ENSURE_TRUE(systemMessenger, false);
-
-  JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
-  systemMessenger->BroadcastMessage(aType, value,
-                                    JS::UndefinedHandleValue);
-
-  return true;
-}
-
-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));
-  } else {
-    MOZ_ASSERT(aValue.type() != BluetoothValue::T__None);
-    reply = new BluetoothReply(BluetoothReplySuccess(aValue));
-  }
-
-  aRunnable->SetReply(reply);
-  if (NS_FAILED(NS_DispatchToMainThread(aRunnable))) {
-    BT_WARNING("Failed to dispatch to main thread!");
-  }
-}
-
-void
-ParseAtCommand(const nsACString& aAtCommand, const int aStart,
-               nsTArray<nsCString>& aRetValues)
-{
-  int length = aAtCommand.Length();
-  int begin = aStart;
-
-  for (int i = aStart; i < length; ++i) {
-    // Use ',' as separator
-    if (aAtCommand[i] == ',') {
-      nsCString tmp(nsDependentCSubstring(aAtCommand, begin, i - begin));
-      aRetValues.AppendElement(tmp);
-
-      begin = i + 1;
-    }
-  }
-
-  nsCString tmp(nsDependentCSubstring(aAtCommand, begin));
-  aRetValues.AppendElement(tmp);
-}
-
-void
-DispatchStatusChangedEvent(const nsAString& aType,
-                           const nsAString& aAddress,
-                           bool aStatus)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  InfallibleTArray<BluetoothNamedValue> data;
-  data.AppendElement(
-    BluetoothNamedValue(NS_LITERAL_STRING("address"), nsString(aAddress)));
-  data.AppendElement(
-    BluetoothNamedValue(NS_LITERAL_STRING("status"), aStatus));
-
-  BluetoothSignal signal(nsString(aType), NS_LITERAL_STRING(KEY_ADAPTER), data);
-
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->DistributeSignal(signal);
-}
-
-END_BLUETOOTH_NAMESPACE
deleted file mode 100644
--- a/dom/bluetooth/bluez/BluetoothUtils.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- 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_dom_bluetooth_bluetoothutils_h__
-#define mozilla_dom_bluetooth_bluetoothutils_h__
-
-#include "BluetoothCommon.h"
-#include "js/TypeDecls.h"
-
-BEGIN_BLUETOOTH_NAMESPACE
-
-class BluetoothNamedValue;
-class BluetoothValue;
-class BluetoothReplyRunnable;
-
-bool
-SetJsObject(JSContext* aContext,
-            const BluetoothValue& aValue,
-            JS::Handle<JSObject*> aObj);
-
-nsString
-GetObjectPathFromAddress(const nsAString& aAdapterPath,
-                         const nsAString& aDeviceAddress);
-
-nsString
-GetAddressFromObjectPath(const nsAString& aObjectPath);
-
-bool
-BroadcastSystemMessage(const nsAString& aType,
-                       const InfallibleTArray<BluetoothNamedValue>& aData);
-
-void
-DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
-                       const BluetoothValue& aValue,
-                       const nsAString& aErrorStr);
-
-void
-ParseAtCommand(const nsACString& aAtCommand, const int aStart,
-               nsTArray<nsCString>& aRetValues);
-
-void
-DispatchStatusChangedEvent(const nsAString& aType,
-                           const nsAString& aDeviceAddress,
-                           bool aStatus);
-
-END_BLUETOOTH_NAMESPACE
-
-#endif
--- a/dom/bluetooth/moz.build
+++ b/dom/bluetooth/moz.build
@@ -10,16 +10,17 @@ if CONFIG['MOZ_B2G_BT']:
         'BluetoothDevice.cpp',
         'BluetoothHidManager.cpp',
         'BluetoothInterface.cpp',
         'BluetoothManager.cpp',
         'BluetoothProfileController.cpp',
         'BluetoothPropertyContainer.cpp',
         'BluetoothReplyRunnable.cpp',
         'BluetoothService.cpp',
+        'BluetoothUtils.cpp',
         'BluetoothUuid.cpp',
         'ipc/BluetoothChild.cpp',
         'ipc/BluetoothParent.cpp',
         'ipc/BluetoothServiceChildProcess.cpp',
         'ObexBase.cpp'
     ]
 
     if CONFIG['MOZ_B2G_RIL']:
@@ -31,18 +32,17 @@ if CONFIG['MOZ_B2G_BT']:
         if CONFIG['MOZ_B2G_BT_BLUEZ']:
             CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
             SOURCES += [
                 'bluez/BluetoothA2dpManager.cpp',
                 'bluez/BluetoothDBusService.cpp',
                 'bluez/BluetoothHfpManager.cpp',
                 'bluez/BluetoothOppManager.cpp',
                 'bluez/BluetoothSocket.cpp',
-                'bluez/BluetoothUnixSocketConnector.cpp',
-                'bluez/BluetoothUtils.cpp',
+                'bluez/BluetoothUnixSocketConnector.cpp'
             ]
             LOCAL_INCLUDES += [
                 'bluez',
             ]
             DEFINES['MOZ_B2G_BT_BLUEZ'] = True
         elif CONFIG['MOZ_B2G_BT_BLUEDROID']:
             SOURCES += [
                 'bluedroid/BluetoothA2dpHALInterface.cpp',
@@ -55,18 +55,17 @@ if CONFIG['MOZ_B2G_BT']:
                 'bluedroid/BluetoothDaemonSocketInterface.cpp',
                 'bluedroid/BluetoothHALHelpers.cpp',
                 'bluedroid/BluetoothHALInterface.cpp',
                 'bluedroid/BluetoothHandsfreeHALInterface.cpp',
                 'bluedroid/BluetoothOppManager.cpp',
                 'bluedroid/BluetoothServiceBluedroid.cpp',
                 'bluedroid/BluetoothSocket.cpp',
                 'bluedroid/BluetoothSocketHALInterface.cpp',
-                'bluedroid/BluetoothSocketMessageWatcher.cpp',
-                'bluedroid/BluetoothUtils.cpp',
+                'bluedroid/BluetoothSocketMessageWatcher.cpp'
             ]
             LOCAL_INCLUDES += [
                 'bluedroid',
             ]
 
             if CONFIG['MOZ_B2G_RIL']:
                 SOURCES += [
                     'bluedroid/hfp/BluetoothHfpManager.cpp',
--- a/dom/bluetooth2/BluetoothService.cpp
+++ b/dom/bluetooth2/BluetoothService.cpp
@@ -140,44 +140,17 @@ GetAllBluetoothActors(InfallibleTArray<B
 
 BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled)
   : mEnabled(aEnabled)
 { }
 
 NS_METHOD
 BluetoothService::ToggleBtAck::Run()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // This is requested in Bug 836516. With settings this property, WLAN
-  // firmware could be aware of Bluetooth has been turned on/off, so that the
-  // mecahnism of handling coexistence of WIFI and Bluetooth could be started.
-  //
-  // In the future, we may have our own way instead of setting a system
-  // property to let firmware developers be able to sense that Bluetooth has
-  // been toggled.
-#if defined(MOZ_WIDGET_GONK)
-  if (property_set(PROP_BLUETOOTH_ENABLED, mEnabled ? "true" : "false") != 0) {
-    BT_WARNING("Failed to set bluetooth enabled property");
-  }
-#endif
-
-  NS_ENSURE_TRUE(sBluetoothService, NS_OK);
-
-  if (sInShutdown) {
-    sBluetoothService = nullptr;
-    return NS_OK;
-  }
-
-  // Update mEnabled of BluetoothService object since
-  // StartInternal/StopInternal have been already done.
-  sBluetoothService->SetEnabled(mEnabled);
-  sToggleInProgress = false;
-
-  sBluetoothService->FireAdapterStateChanged(mEnabled);
+  BluetoothService::AcknowledgeToggleBt(mEnabled);
 
   return NS_OK;
 }
 
 class BluetoothService::StartupTask MOZ_FINAL : public nsISettingsServiceCallback
 {
 public:
   NS_DECL_ISUPPORTS
@@ -704,8 +677,50 @@ BluetoothService::FireAdapterStateChange
   InfallibleTArray<BluetoothNamedValue> props;
   BT_APPEND_NAMED_VALUE(props, "State", aEnable);
   BluetoothValue value(props);
 
   BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
                          NS_LITERAL_STRING(KEY_ADAPTER), value);
   DistributeSignal(signal);
 }
+
+void
+BluetoothService::AcknowledgeToggleBt(bool aEnabled)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+#if defined(MOZ_WIDGET_GONK)
+  // This is requested in Bug 836516. With settings this property, WLAN
+  // firmware could be aware of Bluetooth has been turned on/off, so that the
+  // mecahnism of handling coexistence of WIFI and Bluetooth could be started.
+  //
+  // In the future, we may have our own way instead of setting a system
+  // property to let firmware developers be able to sense that Bluetooth has
+  // been toggled.
+  if (property_set(PROP_BLUETOOTH_ENABLED, aEnabled ? "true" : "false") != 0) {
+    BT_WARNING("Failed to set bluetooth enabled property");
+  }
+#endif
+
+  if (sInShutdown) {
+    sBluetoothService = nullptr;
+    return;
+  }
+
+  NS_ENSURE_TRUE_VOID(sBluetoothService);
+
+  sBluetoothService->CompleteToggleBt(aEnabled);
+}
+
+void
+BluetoothService::CompleteToggleBt(bool aEnabled)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Update |mEnabled| of |BluetoothService| object since
+  // |StartInternal| and |StopInternal| have been already
+  // done.
+  SetEnabled(aEnabled);
+  sToggleInProgress = false;
+
+  FireAdapterStateChanged(aEnabled);
+}
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -310,16 +310,18 @@ public:
   IsEnabled() const
   {
     return mEnabled;
   }
 
   bool
   IsToggling() const;
 
+  static void AcknowledgeToggleBt(bool aEnabled);
+
   void FireAdapterStateChanged(bool aEnable);
   nsresult EnableDisable(bool aEnable,
                          BluetoothReplyRunnable* aRunnable);
 
   /**
    * Platform specific startup functions go here. Usually deals with member
    * variables, so not static. Guaranteed to be called outside of main thread.
    *
@@ -387,16 +389,18 @@ protected:
   // Called by ToggleBtAck.
   void
   SetEnabled(bool aEnabled);
 
   // Called by Get().
   static BluetoothService*
   Create();
 
+  void CompleteToggleBt(bool aEnabled);
+
   typedef nsClassHashtable<nsStringHashKey, BluetoothSignalObserverList >
   BluetoothSignalObserverTable;
 
   BluetoothSignalObserverTable mBluetoothSignalObserverTable;
 
   bool mEnabled;
 };
 
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
@@ -0,0 +1,1240 @@
+/* -*- 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 "BluetoothDaemonHandsfreeInterface.h"
+#include "BluetoothDaemonSetupInterface.h"
+#include "mozilla/unused.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+//
+// Handsfree module
+//
+
+BluetoothHandsfreeNotificationHandler*
+  BluetoothDaemonHandsfreeModule::sNotificationHandler;
+
+void
+BluetoothDaemonHandsfreeModule::SetNotificationHandler(
+  BluetoothHandsfreeNotificationHandler* aNotificationHandler)
+{
+  sNotificationHandler = aNotificationHandler;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::Send(BluetoothDaemonPDU* aPDU,
+                                     BluetoothHandsfreeResultHandler* aRes)
+{
+  aRes->AddRef(); // Keep reference for response
+  return Send(aPDU, static_cast<void*>(aRes));
+}
+
+void
+BluetoothDaemonHandsfreeModule::HandleSvc(const BluetoothDaemonPDUHeader& aHeader,
+                                          BluetoothDaemonPDU& aPDU, void* aUserData)
+{
+  static void (BluetoothDaemonHandsfreeModule::* const HandleOp[])(
+    const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&, void*) = {
+    INIT_ARRAY_AT(0, &BluetoothDaemonHandsfreeModule::HandleRsp),
+    INIT_ARRAY_AT(1, &BluetoothDaemonHandsfreeModule::HandleNtf),
+  };
+
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  // Negate twice to map bit to 0/1
+  unsigned long isNtf = !!(aHeader.mOpcode & 0x80);
+
+  (this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData);
+}
+
+// Commands
+//
+
+nsresult
+BluetoothDaemonHandsfreeModule::ConnectCmd(
+  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_CONNECT,
+                           6)); // Address
+
+  nsresult rv = PackPDU(
+    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::DisconnectCmd(
+  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_DISCONNECT,
+                           6)); // Address
+
+  nsresult rv = PackPDU(
+    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::ConnectAudioCmd(
+  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_CONNECT_AUDIO,
+                           6)); // Address
+
+  nsresult rv = PackPDU(
+    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::DisconnectAudioCmd(
+  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_DISCONNECT_AUDIO,
+                           6)); // Address
+
+  nsresult rv = PackPDU(
+    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::StartVoiceRecognitionCmd(
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_START_VOICE_RECOGNITION,
+                           0)); // No payload
+
+  nsresult rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::StopVoiceRecognitionCmd(
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_STOP_VOICE_RECOGNITION,
+                           0)); // No payload
+
+  nsresult rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::VolumeControlCmd(
+  BluetoothHandsfreeVolumeType aType, int aVolume,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_VOLUME_CONTROL,
+                           1 + // Volume type
+                           1)); // Volume
+
+  nsresult rv = PackPDU(aType, PackConversion<int, uint8_t>(aVolume), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::DeviceStatusNotificationCmd(
+  BluetoothHandsfreeNetworkState aNtkState,
+  BluetoothHandsfreeServiceType aSvcType, int aSignal, int aBattChg,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_DEVICE_STATUS_NOTIFICATION,
+                           1 + // Network state
+                           1 + // Service type
+                           1 + // Signal strength
+                           1)); // Battery level
+
+  nsresult rv = PackPDU(aNtkState, aSvcType,
+                        PackConversion<int, uint8_t>(aSignal),
+                        PackConversion<int, uint8_t>(aBattChg), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::CopsResponseCmd(
+  const char* aCops, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_COPS_RESPONSE,
+                           0)); // Dynamically allocated
+
+  nsresult rv = PackPDU(PackCString0(nsDependentCString(aCops)), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::CindResponseCmd(
+  int aSvc, int aNumActive, int aNumHeld,
+  BluetoothHandsfreeCallState aCallSetupState,
+  int aSignal, int aRoam, int aBattChg,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_CIND_RESPONSE,
+                           1 + // Service
+                           1 + // # Active
+                           1 + // # Held
+                           1 + // Call state
+                           1 + // Signal strength
+                           1 + // Roaming
+                           1)); // Battery level
+
+  nsresult rv = PackPDU(PackConversion<int, uint8_t>(aSvc),
+                        PackConversion<int, uint8_t>(aNumActive),
+                        PackConversion<int, uint8_t>(aNumHeld),
+                        aCallSetupState,
+                        PackConversion<int, uint8_t>(aSignal),
+                        PackConversion<int, uint8_t>(aRoam),
+                        PackConversion<int, uint8_t>(aBattChg), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::FormattedAtResponseCmd(
+  const char* aRsp, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_FORMATTED_AT_RESPONSE,
+                           0)); // Dynamically allocated
+
+  nsresult rv = PackPDU(PackCString0(nsDependentCString(aRsp)), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::AtResponseCmd(
+  BluetoothHandsfreeAtResponse aResponseCode, int aErrorCode,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_AT_RESPONSE,
+                           1 + // AT Response code
+                           1)); // Error code
+
+  nsresult rv = PackPDU(aResponseCode,
+                        PackConversion<int, uint8_t>(aErrorCode), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::ClccResponseCmd(
+  int aIndex,
+  BluetoothHandsfreeCallDirection aDir, BluetoothHandsfreeCallState aState,
+  BluetoothHandsfreeCallMode aMode, BluetoothHandsfreeCallMptyType aMpty,
+  const nsAString& aNumber, BluetoothHandsfreeCallAddressType aType,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  NS_ConvertUTF16toUTF8 number(aNumber);
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_CLCC_RESPONSE,
+                           1 + // Call index
+                           1 + // Call direction
+                           1 + // Call state
+                           1 + // Call mode
+                           1 + // Call MPTY
+                           1 + // Address type
+                           number.Length() + 1)); // Number string + \0
+
+  nsresult rv = PackPDU(PackConversion<int, uint8_t>(aIndex),
+                        aDir, aState, aMode, aMpty, aType,
+                        PackCString0(number), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+nsresult
+BluetoothDaemonHandsfreeModule::PhoneStateChangeCmd(
+  int aNumActive, int aNumHeld, BluetoothHandsfreeCallState aCallSetupState,
+  const nsAString& aNumber, BluetoothHandsfreeCallAddressType aType,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  NS_ConvertUTF16toUTF8 number(aNumber);
+
+  nsAutoPtr<BluetoothDaemonPDU> pdu(
+    new BluetoothDaemonPDU(SERVICE_ID, OPCODE_PHONE_STATE_CHANGE,
+                           1 + // # Active
+                           1 + // # Held
+                           1 + // Call state
+                           1 + // Address type
+                           number.Length() + 1)); // Number string + \0
+
+  nsresult rv = PackPDU(PackConversion<int, uint8_t>(aNumActive),
+                        PackConversion<int, uint8_t>(aNumHeld),
+                        aCallSetupState, aType,
+                        PackCString0(NS_ConvertUTF16toUTF8(aNumber)), *pdu);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = Send(pdu, aRes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  unused << pdu.forget();
+  return NS_OK;
+}
+
+// Responses
+//
+
+void
+BluetoothDaemonHandsfreeModule::ErrorRsp(
+  const BluetoothDaemonPDUHeader& aHeader,
+  BluetoothDaemonPDU& aPDU, BluetoothHandsfreeResultHandler* aRes)
+{
+  ErrorRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::OnError, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::ConnectRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::Connect, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::DisconnectRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::Disconnect, UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::ConnectAudioRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::ConnectAudio,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::DisconnectAudioRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::DisconnectAudio,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::StartVoiceRecognitionRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::StartVoiceRecognition,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::StopVoiceRecognitionRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::StopVoiceRecognition,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::VolumeControlRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::VolumeControl,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::DeviceStatusNotificationRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::DeviceStatusNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::CopsResponseRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::CopsResponse,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::CindResponseRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::CindResponse,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::FormattedAtResponseRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::FormattedAtResponse,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::AtResponseRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::AtResponse,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::ClccResponseRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::ClccResponse,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::PhoneStateChangeRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  ResultRunnable::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::PhoneStateChange,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::HandleRsp(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  void* aUserData)
+{
+  static void (BluetoothDaemonHandsfreeModule::* const HandleRsp[])(
+    const BluetoothDaemonPDUHeader&,
+    BluetoothDaemonPDU&,
+    BluetoothHandsfreeResultHandler*) = {
+    INIT_ARRAY_AT(OPCODE_ERROR,
+      &BluetoothDaemonHandsfreeModule::ErrorRsp),
+    INIT_ARRAY_AT(OPCODE_CONNECT,
+      &BluetoothDaemonHandsfreeModule::ConnectRsp),
+    INIT_ARRAY_AT(OPCODE_DISCONNECT,
+      &BluetoothDaemonHandsfreeModule::DisconnectRsp),
+    INIT_ARRAY_AT(OPCODE_CONNECT_AUDIO,
+      &BluetoothDaemonHandsfreeModule::ConnectAudioRsp),
+    INIT_ARRAY_AT(OPCODE_DISCONNECT_AUDIO,
+      &BluetoothDaemonHandsfreeModule::DisconnectAudioRsp),
+    INIT_ARRAY_AT(OPCODE_START_VOICE_RECOGNITION,
+      &BluetoothDaemonHandsfreeModule::StartVoiceRecognitionRsp),
+    INIT_ARRAY_AT(OPCODE_STOP_VOICE_RECOGNITION,
+      &BluetoothDaemonHandsfreeModule::StopVoiceRecognitionRsp),
+    INIT_ARRAY_AT(OPCODE_VOLUME_CONTROL,
+      &BluetoothDaemonHandsfreeModule::VolumeControlRsp),
+    INIT_ARRAY_AT(OPCODE_DEVICE_STATUS_NOTIFICATION,
+      &BluetoothDaemonHandsfreeModule::DeviceStatusNotificationRsp),
+    INIT_ARRAY_AT(OPCODE_COPS_RESPONSE,
+      &BluetoothDaemonHandsfreeModule::CopsResponseRsp),
+    INIT_ARRAY_AT(OPCODE_CIND_RESPONSE,
+      &BluetoothDaemonHandsfreeModule::CindResponseRsp),
+    INIT_ARRAY_AT(OPCODE_FORMATTED_AT_RESPONSE,
+      &BluetoothDaemonHandsfreeModule::FormattedAtResponseRsp),
+    INIT_ARRAY_AT(OPCODE_AT_RESPONSE,
+      &BluetoothDaemonHandsfreeModule::AtResponseRsp),
+    INIT_ARRAY_AT(OPCODE_CLCC_RESPONSE,
+      &BluetoothDaemonHandsfreeModule::ClccResponseRsp),
+    INIT_ARRAY_AT(OPCODE_PHONE_STATE_CHANGE,
+      &BluetoothDaemonHandsfreeModule::PhoneStateChangeRsp)
+  };
+
+  MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
+
+  if (NS_WARN_IF(!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(HandleRsp))) ||
+      NS_WARN_IF(!HandleRsp[aHeader.mOpcode])) {
+    return;
+  }
+
+  nsRefPtr<BluetoothHandsfreeResultHandler> res =
+    already_AddRefed<BluetoothHandsfreeResultHandler>(
+      static_cast<BluetoothHandsfreeResultHandler*>(aUserData));
+
+  if (!res) {
+    return; // Return early if no result handler has been set for response
+  }
+
+  (this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
+}
+
+// Notifications
+//
+
+// Returns the current notification handler to a notification runnable
+class BluetoothDaemonHandsfreeModule::NotificationHandlerWrapper MOZ_FINAL
+{
+public:
+  typedef BluetoothHandsfreeNotificationHandler ObjectType;
+
+  static ObjectType* GetInstance()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    return sNotificationHandler;
+  }
+};
+
+// Init operator class for ConnectionStateNotification
+class BluetoothDaemonHandsfreeModule::ConnectionStateInitOp MOZ_FINAL
+  : private PDUInitOp
+{
+public:
+  ConnectionStateInitOp(BluetoothDaemonPDU& aPDU)
+    : PDUInitOp(aPDU)
+  { }
+
+  nsresult
+  operator () (BluetoothHandsfreeConnectionState& aArg1,
+               nsString& aArg2) const
+  {
+    BluetoothDaemonPDU& pdu = GetPDU();
+
+    /* Read state */
+    nsresult rv = UnpackPDU(pdu, aArg1);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    /* Read address */
+    rv = UnpackPDU(
+      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    WarnAboutTrailingData();
+    return NS_OK;
+  }
+};
+
+void
+BluetoothDaemonHandsfreeModule::ConnectionStateNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  ConnectionStateNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::ConnectionStateNotification,
+    ConnectionStateInitOp(aPDU));
+}
+
+// Init operator class for AudioStateNotification
+class BluetoothDaemonHandsfreeModule::AudioStateInitOp MOZ_FINAL
+  : private PDUInitOp
+{
+public:
+  AudioStateInitOp(BluetoothDaemonPDU& aPDU)
+    : PDUInitOp(aPDU)
+  { }
+
+  nsresult
+  operator () (BluetoothHandsfreeAudioState& aArg1,
+               nsString& aArg2) const
+  {
+    BluetoothDaemonPDU& pdu = GetPDU();
+
+    /* Read state */
+    nsresult rv = UnpackPDU(pdu, aArg1);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    /* Read address */
+    rv = UnpackPDU(
+      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    WarnAboutTrailingData();
+    return NS_OK;
+  }
+};
+
+void
+BluetoothDaemonHandsfreeModule::AudioStateNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  AudioStateNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::AudioStateNotification,
+    AudioStateInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::VoiceRecognitionNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  VoiceRecognitionNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::VoiceRecognitionNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::AnswerCallNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  AnswerCallNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::AnswerCallNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::HangupCallNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  HangupCallNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::HangupCallNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+// Init operator class for VolumeNotification
+class BluetoothDaemonHandsfreeModule::VolumeInitOp MOZ_FINAL
+  : private PDUInitOp
+{
+public:
+  VolumeInitOp(BluetoothDaemonPDU& aPDU)
+    : PDUInitOp(aPDU)
+  { }
+
+  nsresult
+  operator () (BluetoothHandsfreeVolumeType& aArg1, int& aArg2) const
+  {
+    BluetoothDaemonPDU& pdu = GetPDU();
+
+    /* Read volume type */
+    nsresult rv = UnpackPDU(pdu, aArg1);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    /* Read volume */
+    rv = UnpackPDU(pdu, UnpackConversion<uint8_t, int>(aArg2));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    WarnAboutTrailingData();
+    return NS_OK;
+  }
+};
+
+void
+BluetoothDaemonHandsfreeModule::VolumeNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  VolumeNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::VolumeNotification,
+    VolumeInitOp(aPDU));
+}
+
+// Init operator class for DialCallNotification
+class BluetoothDaemonHandsfreeModule::DialCallInitOp MOZ_FINAL
+  : private PDUInitOp
+{
+public:
+  DialCallInitOp(BluetoothDaemonPDU& aPDU)
+    : PDUInitOp(aPDU)
+  { }
+
+  nsresult
+  operator () (nsString& aArg1) const
+  {
+    /* Read number */
+    nsresult rv = UnpackPDU(GetPDU(), UnpackString0(aArg1));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    WarnAboutTrailingData();
+    return NS_OK;
+  }
+};
+
+void
+BluetoothDaemonHandsfreeModule::DialCallNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  DialCallNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::DialCallNotification,
+    DialCallInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::DtmfNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  DtmfNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::DtmfNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::NRECNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  NRECNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::NRECNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::CallHoldNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  CallHoldNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::CallHoldNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::CnumNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  CnumNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::CnumNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::CindNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  CindNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::CindNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::CopsNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  CopsNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::CopsNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::ClccNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  ClccNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::ClccNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+// Init operator class for UnknownAtNotification
+class BluetoothDaemonHandsfreeModule::UnknownAtInitOp MOZ_FINAL
+  : private PDUInitOp
+{
+public:
+  UnknownAtInitOp(BluetoothDaemonPDU& aPDU)
+    : PDUInitOp(aPDU)
+  { }
+
+  nsresult
+  operator () (nsCString& aArg1) const
+  {
+    /* Read string */
+    nsresult rv = UnpackPDU(GetPDU(), UnpackCString0(aArg1));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    WarnAboutTrailingData();
+    return NS_OK;
+  }
+};
+
+void
+BluetoothDaemonHandsfreeModule::UnknownAtNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  UnknownAtNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::UnknownAtNotification,
+    UnknownAtInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::KeyPressedNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU)
+{
+  KeyPressedNotification::Dispatch(
+    &BluetoothHandsfreeNotificationHandler::KeyPressedNotification,
+    UnpackPDUInitOp(aPDU));
+}
+
+void
+BluetoothDaemonHandsfreeModule::HandleNtf(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  void* aUserData)
+{
+  static void (BluetoothDaemonHandsfreeModule::* const HandleNtf[])(
+    const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&) = {
+    INIT_ARRAY_AT(0, &BluetoothDaemonHandsfreeModule::ConnectionStateNtf),
+    INIT_ARRAY_AT(1, &BluetoothDaemonHandsfreeModule::AudioStateNtf),
+    INIT_ARRAY_AT(2, &BluetoothDaemonHandsfreeModule::VoiceRecognitionNtf),
+    INIT_ARRAY_AT(3, &BluetoothDaemonHandsfreeModule::AnswerCallNtf),
+    INIT_ARRAY_AT(4, &BluetoothDaemonHandsfreeModule::HangupCallNtf),
+    INIT_ARRAY_AT(5, &BluetoothDaemonHandsfreeModule::VolumeNtf),
+    INIT_ARRAY_AT(6, &BluetoothDaemonHandsfreeModule::DialCallNtf),
+    INIT_ARRAY_AT(7, &BluetoothDaemonHandsfreeModule::DtmfNtf),
+    INIT_ARRAY_AT(8, &BluetoothDaemonHandsfreeModule::NRECNtf),
+    INIT_ARRAY_AT(9, &BluetoothDaemonHandsfreeModule::CallHoldNtf),
+    INIT_ARRAY_AT(10, &BluetoothDaemonHandsfreeModule::CnumNtf),
+    INIT_ARRAY_AT(11, &BluetoothDaemonHandsfreeModule::CindNtf),
+    INIT_ARRAY_AT(12, &BluetoothDaemonHandsfreeModule::CopsNtf),
+    INIT_ARRAY_AT(13, &BluetoothDaemonHandsfreeModule::ClccNtf),
+    INIT_ARRAY_AT(14, &BluetoothDaemonHandsfreeModule::UnknownAtNtf),
+    INIT_ARRAY_AT(15, &BluetoothDaemonHandsfreeModule::KeyPressedNtf)
+  };
+
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  uint8_t index = aHeader.mOpcode - 0x81;
+
+  if (NS_WARN_IF(!(index < MOZ_ARRAY_LENGTH(HandleNtf))) ||
+      NS_WARN_IF(!HandleNtf[index])) {
+    return;
+  }
+
+  (this->*(HandleNtf[index]))(aHeader, aPDU);
+}
+
+//
+// Handsfree interface
+//
+
+BluetoothDaemonHandsfreeInterface::BluetoothDaemonHandsfreeInterface(
+  BluetoothDaemonHandsfreeModule* aModule)
+  : mModule(aModule)
+{ }
+
+BluetoothDaemonHandsfreeInterface::~BluetoothDaemonHandsfreeInterface()
+{ }
+
+class BluetoothDaemonHandsfreeInterface::InitResultHandler MOZ_FINAL
+  : public BluetoothSetupResultHandler
+{
+public:
+  InitResultHandler(BluetoothHandsfreeResultHandler* aRes)
+    : mRes(aRes)
+  {
+    MOZ_ASSERT(mRes);
+  }
+
+  void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mRes->OnError(aStatus);
+  }
+
+  void RegisterModule() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mRes->Init();
+  }
+
+private:
+  nsRefPtr<BluetoothHandsfreeResultHandler> mRes;
+};
+
+void
+BluetoothDaemonHandsfreeInterface::Init(
+  BluetoothHandsfreeNotificationHandler* aNotificationHandler,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  // Set notification handler _before_ registering the module. It could
+  // happen that we receive notifications, before the result handler runs.
+  mModule->SetNotificationHandler(aNotificationHandler);
+
+  InitResultHandler* res;
+
+  if (aRes) {
+    res = new InitResultHandler(aRes);
+  } else {
+    // We don't need a result handler if the caller is not interested.
+    res = nullptr;
+  }
+
+  nsresult rv = mModule->RegisterModule(
+    BluetoothDaemonHandsfreeModule::SERVICE_ID, MODE_NARROWBAND_SPEECH, res);
+
+  if (NS_FAILED(rv) && aRes) {
+    DispatchError(aRes, STATUS_FAIL);
+  }
+}
+
+class BluetoothDaemonHandsfreeInterface::CleanupResultHandler MOZ_FINAL
+  : public BluetoothSetupResultHandler
+{
+public:
+  CleanupResultHandler(BluetoothDaemonHandsfreeModule* aModule,
+                       BluetoothHandsfreeResultHandler* aRes)
+    : mModule(aModule)
+    , mRes(aRes)
+  {
+    MOZ_ASSERT(mModule);
+  }
+
+  void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mRes) {
+      mRes->OnError(aStatus);
+    }
+  }
+
+  void UnregisterModule() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    // Clear notification handler _after_ module has been
+    // unregistered. While unregistering the module, we might
+    // still receive notifications.
+    mModule->SetNotificationHandler(nullptr);
+
+    if (mRes) {
+      mRes->Cleanup();
+    }
+  }
+
+private:
+  BluetoothDaemonHandsfreeModule* mModule;
+  nsRefPtr<BluetoothHandsfreeResultHandler> mRes;
+};
+
+void
+BluetoothDaemonHandsfreeInterface::Cleanup(
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  mModule->UnregisterModule(BluetoothDaemonHandsfreeModule::SERVICE_ID,
+                            new CleanupResultHandler(mModule, aRes));
+}
+
+/* Connect / Disconnect */
+
+void
+BluetoothDaemonHandsfreeInterface::Connect(
+  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->ConnectCmd(aBdAddr, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::Disconnect(
+  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->DisconnectCmd(aBdAddr, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::ConnectAudio(
+  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->ConnectAudioCmd(aBdAddr, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::DisconnectAudio(
+  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->DisconnectAudioCmd(aBdAddr, aRes);
+}
+
+/* Voice Recognition */
+
+void
+BluetoothDaemonHandsfreeInterface::StartVoiceRecognition(
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->StartVoiceRecognitionCmd(aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::StopVoiceRecognition(
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->StopVoiceRecognitionCmd(aRes);
+}
+
+/* Volume */
+
+void
+BluetoothDaemonHandsfreeInterface::VolumeControl(
+  BluetoothHandsfreeVolumeType aType, int aVolume,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->VolumeControlCmd(aType, aVolume, aRes);
+}
+
+/* Device status */
+
+void
+BluetoothDaemonHandsfreeInterface::DeviceStatusNotification(
+  BluetoothHandsfreeNetworkState aNtkState,
+  BluetoothHandsfreeServiceType aSvcType, int aSignal, int aBattChg,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->DeviceStatusNotificationCmd(aNtkState, aSvcType, aSignal,
+                                       aBattChg, aRes);
+}
+
+/* Responses */
+
+void
+BluetoothDaemonHandsfreeInterface::CopsResponse(
+  const char* aCops, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->CopsResponseCmd(aCops, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::CindResponse(
+  int aSvc, int aNumActive, int aNumHeld,
+  BluetoothHandsfreeCallState aCallSetupState,
+  int aSignal, int aRoam, int aBattChg,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->CindResponseCmd(aSvc, aNumActive, aNumHeld, aCallSetupState,
+                           aSignal, aRoam, aBattChg, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::FormattedAtResponse(
+  const char* aRsp, BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->FormattedAtResponseCmd(aRsp, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::AtResponse(
+  BluetoothHandsfreeAtResponse aResponseCode, int aErrorCode,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->AtResponseCmd(aResponseCode, aErrorCode, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::ClccResponse(
+  int aIndex, BluetoothHandsfreeCallDirection aDir,
+  BluetoothHandsfreeCallState aState,
+  BluetoothHandsfreeCallMode aMode,
+  BluetoothHandsfreeCallMptyType aMpty,
+  const nsAString& aNumber,
+  BluetoothHandsfreeCallAddressType aType,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->ClccResponseCmd(aIndex, aDir, aState, aMode, aMpty, aNumber,
+                           aType, aRes);
+}
+
+/* Phone State */
+
+void
+BluetoothDaemonHandsfreeInterface::PhoneStateChange(
+  int aNumActive, int aNumHeld,
+  BluetoothHandsfreeCallState aCallSetupState,
+  const nsAString& aNumber,
+  BluetoothHandsfreeCallAddressType aType,
+  BluetoothHandsfreeResultHandler* aRes)
+{
+  MOZ_ASSERT(mModule);
+
+  mModule->PhoneStateChangeCmd(aNumActive, aNumHeld, aCallSetupState, aNumber,
+                               aType, aRes);
+}
+
+void
+BluetoothDaemonHandsfreeInterface::DispatchError(
+  BluetoothHandsfreeResultHandler* aRes, BluetoothStatus aStatus)
+{
+  BluetoothResultRunnable1<BluetoothHandsfreeResultHandler, void,
+                           BluetoothStatus, BluetoothStatus>::Dispatch(
+    aRes, &BluetoothHandsfreeResultHandler::OnError,
+    ConstantInitOp1<BluetoothStatus>(aStatus));
+}
+
+END_BLUETOOTH_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/bluedroid/BluetoothDaemonHandsfreeInterface.h
@@ -0,0 +1,412 @@
+/* -*- 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_dom_bluetooth_bluetoothdaemonhandsfreeinterface_h
+#define mozilla_dom_bluetooth_bluetoothdaemonhandsfreeinterface_h
+
+#include "BluetoothDaemonHelpers.h"
+#include "BluetoothInterface.h"
+#include "BluetoothInterfaceHelpers.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothSetupResultHandler;
+
+class BluetoothDaemonHandsfreeModule
+{
+public:
+  enum {
+    SERVICE_ID = 0x05
+  };
+
+  enum {
+    OPCODE_ERROR = 0x00,
+    OPCODE_CONNECT = 0x01,
+    OPCODE_DISCONNECT = 0x02,
+    OPCODE_CONNECT_AUDIO = 0x03,
+    OPCODE_DISCONNECT_AUDIO = 0x04,
+    OPCODE_START_VOICE_RECOGNITION = 0x05,
+    OPCODE_STOP_VOICE_RECOGNITION =0x06,
+    OPCODE_VOLUME_CONTROL = 0x07,
+    OPCODE_DEVICE_STATUS_NOTIFICATION = 0x08,
+    OPCODE_COPS_RESPONSE = 0x09,
+    OPCODE_CIND_RESPONSE = 0x0a,
+    OPCODE_FORMATTED_AT_RESPONSE = 0x0b,
+    OPCODE_AT_RESPONSE = 0x0c,
+    OPCODE_CLCC_RESPONSE = 0x0d,
+    OPCODE_PHONE_STATE_CHANGE = 0x0e
+  };
+
+  virtual nsresult Send(BluetoothDaemonPDU* aPDU, void* aUserData) = 0;
+
+  virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
+                                  BluetoothSetupResultHandler* aRes) = 0;
+
+  virtual nsresult UnregisterModule(uint8_t aId,
+                                    BluetoothSetupResultHandler* aRes) = 0;
+
+  void SetNotificationHandler(
+    BluetoothHandsfreeNotificationHandler* aNotificationHandler);
+
+  //
+  // Commands
+  //
+
+  nsresult ConnectCmd(const nsAString& aBdAddr,
+                      BluetoothHandsfreeResultHandler* aRes);
+  nsresult DisconnectCmd(const nsAString& aBdAddr,
+                         BluetoothHandsfreeResultHandler* aRes);
+  nsresult ConnectAudioCmd(const nsAString& aBdAddr,
+                           BluetoothHandsfreeResultHandler* aRes);
+  nsresult DisconnectAudioCmd(const nsAString& aBdAddr,
+                              BluetoothHandsfreeResultHandler* aRes);
+
+  /* Voice Recognition */
+
+  nsresult StartVoiceRecognitionCmd(BluetoothHandsfreeResultHandler* aRes);
+  nsresult StopVoiceRecognitionCmd(BluetoothHandsfreeResultHandler* aRes);
+
+  /* Volume */
+
+  nsresult VolumeControlCmd(BluetoothHandsfreeVolumeType aType, int aVolume,
+                            BluetoothHandsfreeResultHandler* aRes);
+
+  /* Device status */
+
+  nsresult DeviceStatusNotificationCmd(
+    BluetoothHandsfreeNetworkState aNtkState,
+    BluetoothHandsfreeServiceType aSvcType,
+    int aSignal, int aBattChg,
+    BluetoothHandsfreeResultHandler* aRes);
+
+  /* Responses */
+
+  nsresult CopsResponseCmd(const char* aCops,
+                           BluetoothHandsfreeResultHandler* aRes);
+  nsresult CindResponseCmd(int aSvc, int aNumActive, int aNumHeld,
+                           BluetoothHandsfreeCallState aCallSetupState,
+                           int aSignal, int aRoam, int aBattChg,
+                           BluetoothHandsfreeResultHandler* aRes);
+  nsresult FormattedAtResponseCmd(const char* aRsp,
+                                  BluetoothHandsfreeResultHandler* aRes);
+  nsresult AtResponseCmd(BluetoothHandsfreeAtResponse aResponseCode,
+                         int aErrorCode,
+                         BluetoothHandsfreeResultHandler* aRes);
+  nsresult ClccResponseCmd(int aIndex, BluetoothHandsfreeCallDirection aDir,
+                           BluetoothHandsfreeCallState aState,
+                           BluetoothHandsfreeCallMode aMode,
+                           BluetoothHandsfreeCallMptyType aMpty,
+                           const nsAString& aNumber,
+                           BluetoothHandsfreeCallAddressType aType,
+                           BluetoothHandsfreeResultHandler* aRes);
+
+  /* Phone State */
+
+  nsresult PhoneStateChangeCmd(int aNumActive, int aNumHeld,
+                               BluetoothHandsfreeCallState aCallSetupState,
+                               const nsAString& aNumber,
+                               BluetoothHandsfreeCallAddressType aType,
+                               BluetoothHandsfreeResultHandler* aRes);
+
+protected:
+  nsresult Send(BluetoothDaemonPDU* aPDU,
+                BluetoothHandsfreeResultHandler* aRes);
+
+  void HandleSvc(const BluetoothDaemonPDUHeader& aHeader,
+                 BluetoothDaemonPDU& aPDU, void* aUserData);
+
+  //
+  // Responses
+  //
+
+  typedef BluetoothResultRunnable0<BluetoothHandsfreeResultHandler, void>
+    ResultRunnable;
+
+  typedef BluetoothResultRunnable1<BluetoothHandsfreeResultHandler, void,
+                                   BluetoothStatus, BluetoothStatus>
+    ErrorRunnable;
+
+  void ErrorRsp(const BluetoothDaemonPDUHeader& aHeader,
+                BluetoothDaemonPDU& aPDU,
+                BluetoothHandsfreeResultHandler* aRes);
+
+  void ConnectRsp(const BluetoothDaemonPDUHeader& aHeader,
+                  BluetoothDaemonPDU& aPDU,
+                  BluetoothHandsfreeResultHandler* aRes);
+
+  void DisconnectRsp(const BluetoothDaemonPDUHeader& aHeader,
+                     BluetoothDaemonPDU& aPDU,
+                     BluetoothHandsfreeResultHandler* aRes);
+
+  void ConnectAudioRsp(const BluetoothDaemonPDUHeader& aHeader,
+                       BluetoothDaemonPDU& aPDU,
+                       BluetoothHandsfreeResultHandler* aRes);
+
+  void DisconnectAudioRsp(const BluetoothDaemonPDUHeader& aHeader,
+                          BluetoothDaemonPDU& aPDU,
+                          BluetoothHandsfreeResultHandler* aRes);
+
+  void StartVoiceRecognitionRsp(const BluetoothDaemonPDUHeader& aHeader,
+                                BluetoothDaemonPDU& aPDU,
+                                BluetoothHandsfreeResultHandler* aRes);
+
+  void StopVoiceRecognitionRsp(const BluetoothDaemonPDUHeader& aHeader,
+                               BluetoothDaemonPDU& aPDU,
+                               BluetoothHandsfreeResultHandler* aRes);
+
+  void VolumeControlRsp(const BluetoothDaemonPDUHeader& aHeader,
+                        BluetoothDaemonPDU& aPDU,
+                        BluetoothHandsfreeResultHandler* aRes);
+
+  void DeviceStatusNotificationRsp(const BluetoothDaemonPDUHeader& aHeader,
+                                   BluetoothDaemonPDU& aPDU,
+                                   BluetoothHandsfreeResultHandler* aRes);
+
+  void CopsResponseRsp(const BluetoothDaemonPDUHeader& aHeader,
+                       BluetoothDaemonPDU& aPDU,
+                       BluetoothHandsfreeResultHandler* aRes);
+
+  void CindResponseRsp(const BluetoothDaemonPDUHeader& aHeader,
+                       BluetoothDaemonPDU& aPDU,
+                       BluetoothHandsfreeResultHandler* aRes);
+
+  void FormattedAtResponseRsp(const BluetoothDaemonPDUHeader& aHeader,
+                              BluetoothDaemonPDU& aPDU,
+                              BluetoothHandsfreeResultHandler* aRes);
+
+  void AtResponseRsp(const BluetoothDaemonPDUHeader& aHeader,
+                     BluetoothDaemonPDU& aPDU,
+                     BluetoothHandsfreeResultHandler* aRes);
+
+  void ClccResponseRsp(const BluetoothDaemonPDUHeader& aHeader,
+                       BluetoothDaemonPDU& aPDU,
+                       BluetoothHandsfreeResultHandler* aRes);
+
+  void PhoneStateChangeRsp(const BluetoothDaemonPDUHeader& aHeader,
+                           BluetoothDaemonPDU& aPDU,
+                           BluetoothHandsfreeResultHandler* aRes);
+
+  void HandleRsp(const BluetoothDaemonPDUHeader& aHeader,
+                 BluetoothDaemonPDU& aPDU,
+                 void* aUserData);
+
+  //
+  // Notifications
+  //
+
+  class NotificationHandlerWrapper;
+
+  typedef BluetoothNotificationRunnable2<NotificationHandlerWrapper, void,
+                                         BluetoothHandsfreeConnectionState,
+                                         nsString,
+                                         BluetoothHandsfreeConnectionState,
+                                         const nsAString&>
+    ConnectionStateNotification;
+
+  typedef BluetoothNotificationRunnable2<NotificationHandlerWrapper, void,
+                                         BluetoothHandsfreeAudioState,
+                                         nsString,
+                                         BluetoothHandsfreeAudioState,
+                                         const nsAString&>
+    AudioStateNotification;
+
+  typedef BluetoothNotificationRunnable1<NotificationHandlerWrapper, void,
+    BluetoothHandsfreeVoiceRecognitionState>
+    VoiceRecognitionNotification;
+
+  typedef BluetoothNotificationRunnable0<NotificationHandlerWrapper, void>
+    AnswerCallNotification;
+
+  typedef BluetoothNotificationRunnable0<NotificationHandlerWrapper, void>
+    HangupCallNotification;
+
+  typedef BluetoothNotificationRunnable2<NotificationHandlerWrapper, void,
+                                         BluetoothHandsfreeVolumeType, int>
+    VolumeNotification;
+
+  typedef BluetoothNotificationRunnable1<NotificationHandlerWrapper, void,
+                                         nsString, const nsAString&>
+    DialCallNotification;
+
+  typedef BluetoothNotificationRunnable1<NotificationHandlerWrapper, void,
+                                         char>
+    DtmfNotification;
+
+  typedef BluetoothNotificationRunnable1<NotificationHandlerWrapper, void,
+                                         BluetoothHandsfreeNRECState>
+    NRECNotification;
+
+  typedef BluetoothNotificationRunnable1<NotificationHandlerWrapper, void,
+                                         BluetoothHandsfreeCallHoldType>
+    CallHoldNotification;
+
+  typedef BluetoothNotificationRunnable0<NotificationHandlerWrapper, void>
+    CnumNotification;
+
+  typedef BluetoothNotificationRunnable0<NotificationHandlerWrapper, void>
+    CindNotification;
+
+  typedef BluetoothNotificationRunnable0<NotificationHandlerWrapper, void>
+    CopsNotification;
+
+  typedef BluetoothNotificationRunnable0<NotificationHandlerWrapper, void>
+    ClccNotification;
+
+  typedef BluetoothNotificationRunnable1<NotificationHandlerWrapper, void,
+                                         nsCString, const nsACString&>
+    UnknownAtNotification;
+
+  typedef BluetoothNotificationRunnable0<NotificationHandlerWrapper, void>
+    KeyPressedNotification;
+
+  class AudioStateInitOp;
+  class ConnectionStateInitOp;
+  class DialCallInitOp;
+  class VolumeInitOp;
+  class UnknownAtInitOp;
+
+  void ConnectionStateNtf(const BluetoothDaemonPDUHeader& aHeader,
+                          BluetoothDaemonPDU& aPDU);
+
+  void AudioStateNtf(const BluetoothDaemonPDUHeader& aHeader,
+                     BluetoothDaemonPDU& aPDU);
+
+  void VoiceRecognitionNtf(const BluetoothDaemonPDUHeader& aHeader,
+                           BluetoothDaemonPDU& aPDU);
+
+  void AnswerCallNtf(const BluetoothDaemonPDUHeader& aHeader,
+                     BluetoothDaemonPDU& aPDU);
+
+  void HangupCallNtf(const BluetoothDaemonPDUHeader& aHeader,
+                     BluetoothDaemonPDU& aPDU);
+
+  void VolumeNtf(const BluetoothDaemonPDUHeader& aHeader,
+                 BluetoothDaemonPDU& aPDU);
+
+  void DialCallNtf(const BluetoothDaemonPDUHeader& aHeader,
+                   BluetoothDaemonPDU& aPDU);
+
+  void DtmfNtf(const BluetoothDaemonPDUHeader& aHeader,
+               BluetoothDaemonPDU& aPDU);
+
+  void NRECNtf(const BluetoothDaemonPDUHeader& aHeader,
+               BluetoothDaemonPDU& aPDU);
+
+  void CallHoldNtf(const BluetoothDaemonPDUHeader& aHeader,
+                   BluetoothDaemonPDU& aPDU);
+
+  void CnumNtf(const BluetoothDaemonPDUHeader& aHeader,
+               BluetoothDaemonPDU& aPDU);
+
+  void CindNtf(const BluetoothDaemonPDUHeader& aHeader,
+               BluetoothDaemonPDU& aPDU);
+
+  void CopsNtf(const BluetoothDaemonPDUHeader& aHeader,
+               BluetoothDaemonPDU& aPDU);
+
+  void ClccNtf(const BluetoothDaemonPDUHeader& aHeader,
+               BluetoothDaemonPDU& aPDU);
+
+  void UnknownAtNtf(const BluetoothDaemonPDUHeader& aHeader,
+                    BluetoothDaemonPDU& aPDU);
+
+  void KeyPressedNtf(const BluetoothDaemonPDUHeader& aHeader,
+                     BluetoothDaemonPDU& aPDU);
+
+  void HandleNtf(const BluetoothDaemonPDUHeader& aHeader,
+                 BluetoothDaemonPDU& aPDU,
+                 void* aUserData);
+
+  static BluetoothHandsfreeNotificationHandler* sNotificationHandler;
+};
+
+class BluetoothDaemonHandsfreeInterface MOZ_FINAL
+  : public BluetoothHandsfreeInterface
+{
+  class CleanupResultHandler;
+  class InitResultHandler;
+
+  enum {
+    MODE_HEADSET = 0x00,
+    MODE_NARROWBAND_SPEECH = 0x01,
+    MODE_NARROWBAND_WIDEBAND_SPEECH = 0x02
+  };
+
+public:
+  BluetoothDaemonHandsfreeInterface(BluetoothDaemonHandsfreeModule* aModule);
+  ~BluetoothDaemonHandsfreeInterface();
+
+  void Init(
+    BluetoothHandsfreeNotificationHandler* aNotificationHandler,
+    BluetoothHandsfreeResultHandler* aRes);
+  void Cleanup(BluetoothHandsfreeResultHandler* aRes);
+
+  /* Connect / Disconnect */
+
+  void Connect(const nsAString& aBdAddr,
+               BluetoothHandsfreeResultHandler* aRes);
+  void Disconnect(const nsAString& aBdAddr,
+                  BluetoothHandsfreeResultHandler* aRes);
+  void ConnectAudio(const nsAString& aBdAddr,
+                    BluetoothHandsfreeResultHandler* aRes);
+  void DisconnectAudio(const nsAString& aBdAddr,
+                       BluetoothHandsfreeResultHandler* aRes);
+
+  /* Voice Recognition */
+
+  void StartVoiceRecognition(BluetoothHandsfreeResultHandler* aRes);
+  void StopVoiceRecognition(BluetoothHandsfreeResultHandler* aRes);
+
+  /* Volume */
+
+  void VolumeControl(BluetoothHandsfreeVolumeType aType, int aVolume,
+                     BluetoothHandsfreeResultHandler* aRes);
+
+  /* Device status */
+
+  void DeviceStatusNotification(BluetoothHandsfreeNetworkState aNtkState,
+                                BluetoothHandsfreeServiceType aSvcType,
+                                int aSignal, int aBattChg,
+                                BluetoothHandsfreeResultHandler* aRes);
+
+  /* Responses */
+
+  void CopsResponse(const char* aCops,
+                    BluetoothHandsfreeResultHandler* aRes);
+  void CindResponse(int aSvc, int aNumActive, int aNumHeld,
+                    BluetoothHandsfreeCallState aCallSetupState,
+                    int aSignal, int aRoam, int aBattChg,
+                    BluetoothHandsfreeResultHandler* aRes);
+  void FormattedAtResponse(const char* aRsp,
+                           BluetoothHandsfreeResultHandler* aRes);
+  void AtResponse(BluetoothHandsfreeAtResponse aResponseCode, int aErrorCode,
+                  BluetoothHandsfreeResultHandler* aRes);
+  void ClccResponse(int aIndex, BluetoothHandsfreeCallDirection aDir,
+                    BluetoothHandsfreeCallState aState,
+                    BluetoothHandsfreeCallMode aMode,
+                    BluetoothHandsfreeCallMptyType aMpty,
+                    const nsAString& aNumber,
+                    BluetoothHandsfreeCallAddressType aType,
+                    BluetoothHandsfreeResultHandler* aRes);
+
+  /* Phone State */
+
+  void PhoneStateChange(int aNumActive, int aNumHeld,
+                        BluetoothHandsfreeCallState aCallSetupState,
+                        const nsAString& aNumber,
+                        BluetoothHandsfreeCallAddressType aType,
+                        BluetoothHandsfreeResultHandler* aRes);
+
+private:
+  void DispatchError(BluetoothHandsfreeResultHandler* aRes,
+                     BluetoothStatus aStatus);
+
+  BluetoothDaemonHandsfreeModule* mModule;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.cpp
@@ -41,16 +41,28 @@ Convert(bool aIn, BluetoothScanMode& aOu
     aOut = SCAN_MODE_NONE; // silences compiler warning
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sScanMode[aIn];
   return NS_OK;
 }
 
 nsresult
+Convert(int aIn, uint8_t& aOut)
+{
+  if (NS_WARN_IF(aIn < std::numeric_limits<uint8_t>::min()) ||
+      NS_WARN_IF(aIn > std::numeric_limits<uint8_t>::max())) {
+    aOut = 0; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = static_cast<uint8_t>(aIn);
+  return NS_OK;
+}
+
+nsresult
 Convert(int aIn, int16_t& aOut)
 {
   if (NS_WARN_IF(aIn < std::numeric_limits<int16_t>::min()) ||
       NS_WARN_IF(aIn > std::numeric_limits<int16_t>::max())) {
     aOut = 0; // silences compiler warning
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = static_cast<int16_t>(aIn);
@@ -67,16 +79,30 @@ Convert(uint8_t aIn, bool& aOut)
   if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sBool))) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sBool[aIn];
   return NS_OK;
 }
 
 nsresult
+Convert(uint8_t aIn, char& aOut)
+{
+  aOut = static_cast<char>(aIn);
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, int& aOut)
+{
+  aOut = static_cast<int>(aIn);
+  return NS_OK;
+}
+
+nsresult
 Convert(uint8_t aIn, BluetoothAclState& aOut)
 {
   static const BluetoothAclState sAclState[] = {
     CONVERT(0x00, ACL_STATE_CONNECTED),
     CONVERT(0x01, ACL_STATE_DISCONNECTED),
   };
   if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAclState))) {
     return NS_ERROR_ILLEGAL_VALUE;
@@ -96,16 +122,107 @@ Convert(uint8_t aIn, BluetoothBondState&
   if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sBondState))) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sBondState[aIn];
   return NS_OK;
 }
 
 nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeAudioState& aOut)
+{
+  static const BluetoothHandsfreeAudioState sAudioState[] = {
+    CONVERT(0x00, HFP_AUDIO_STATE_DISCONNECTED),
+    CONVERT(0x01, HFP_AUDIO_STATE_CONNECTING),
+    CONVERT(0x02, HFP_AUDIO_STATE_CONNECTED),
+    CONVERT(0x03, HFP_AUDIO_STATE_DISCONNECTING)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAudioState))) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sAudioState[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeCallHoldType& aOut)
+{
+  static const BluetoothHandsfreeCallHoldType sCallHoldType[] = {
+    CONVERT(0x00, HFP_CALL_HOLD_RELEASEHELD),
+    CONVERT(0x01, HFP_CALL_HOLD_RELEASEACTIVE_ACCEPTHELD),
+    CONVERT(0x02, HFP_CALL_HOLD_HOLDACTIVE_ACCEPTHELD),
+    CONVERT(0x03, HFP_CALL_HOLD_ADDHELDTOCONF)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sCallHoldType))) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sCallHoldType[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeConnectionState& aOut)
+{
+  static const BluetoothHandsfreeConnectionState sConnectionState[] = {
+    CONVERT(0x00, HFP_CONNECTION_STATE_DISCONNECTED),
+    CONVERT(0x01, HFP_CONNECTION_STATE_CONNECTING),
+    CONVERT(0x02, HFP_CONNECTION_STATE_CONNECTED),
+    CONVERT(0x03, HFP_CONNECTION_STATE_SLC_CONNECTED),
+    CONVERT(0x04, HFP_CONNECTION_STATE_DISCONNECTING)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sConnectionState))) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sConnectionState[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeNRECState& aOut)
+{
+  static const BluetoothHandsfreeNRECState sNRECState[] = {
+    CONVERT(0x00, HFP_NREC_STOPPED),
+    CONVERT(0x01, HFP_NREC_STARTED)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sNRECState))) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sNRECState[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeVoiceRecognitionState& aOut)
+{
+  static const BluetoothHandsfreeVoiceRecognitionState sState[] = {
+    CONVERT(0x00, HFP_VOICE_RECOGNITION_STOPPED),
+    CONVERT(0x01, HFP_VOICE_RECOGNITION_STOPPED)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sState))) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sState[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeVolumeType& aOut)
+{
+  static const BluetoothHandsfreeVolumeType sVolumeType[] = {
+    CONVERT(0x00, HFP_VOLUME_TYPE_SPEAKER),
+    CONVERT(0x01, HFP_VOLUME_TYPE_MICROPHONE)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sVolumeType))) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sVolumeType[aIn];
+  return NS_OK;
+}
+
+nsresult
 Convert(int32_t aIn, BluetoothTypeOfDevice& aOut)
 {
   static const BluetoothTypeOfDevice sTypeOfDevice[] = {
     CONVERT(0x00, static_cast<BluetoothTypeOfDevice>(0)), // invalid, required by gcc
     CONVERT(0x01, TYPE_OF_DEVICE_BREDR),
     CONVERT(0x02, TYPE_OF_DEVICE_BLE),
     CONVERT(0x03, TYPE_OF_DEVICE_DUAL)
   };
@@ -369,16 +486,157 @@ Convert(const BluetoothAddress& aIn, nsA
   }
 
   aOut = NS_ConvertUTF8toUTF16(str);
 
   return NS_OK;
 }
 
 nsresult
+Convert(BluetoothHandsfreeAtResponse aIn, uint8_t& aOut)
+{
+  static const uint8_t sAtResponse[] = {
+    CONVERT(HFP_AT_RESPONSE_ERROR, 0x00),
+    CONVERT(HFP_AT_RESPONSE_OK, 0x01)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sAtResponse))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sAtResponse[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeCallAddressType aIn, uint8_t& aOut)
+{
+  static const uint8_t sCallAddressType[] = {
+    CONVERT(HFP_CALL_ADDRESS_TYPE_UNKNOWN, 0x81),
+    CONVERT(HFP_CALL_ADDRESS_TYPE_INTERNATIONAL, 0x91)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sCallAddressType))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sCallAddressType[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeCallDirection aIn, uint8_t& aOut)
+{
+  static const uint8_t sCallDirection[] = {
+    CONVERT(HFP_CALL_DIRECTION_OUTGOING, 0x00),
+    CONVERT(HFP_CALL_DIRECTION_INCOMING, 0x01)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sCallDirection))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sCallDirection[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeCallState aIn, uint8_t& aOut)
+{
+  static const uint8_t sCallState[] = {
+    CONVERT(HFP_CALL_STATE_ACTIVE, 0x00),
+    CONVERT(HFP_CALL_STATE_HELD, 0x01),
+    CONVERT(HFP_CALL_STATE_DIALING, 0x02),
+    CONVERT(HFP_CALL_STATE_ALERTING, 0x03),
+    CONVERT(HFP_CALL_STATE_INCOMING, 0x04),
+    CONVERT(HFP_CALL_STATE_WAITING, 0x05),
+    CONVERT(HFP_CALL_STATE_IDLE, 0x06)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sCallState))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sCallState[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeCallMode aIn, uint8_t& aOut)
+{
+  static const uint8_t sCallMode[] = {
+    CONVERT(HFP_CALL_MODE_VOICE, 0x00),
+    CONVERT(HFP_CALL_MODE_DATA, 0x01),
+    CONVERT(HFP_CALL_MODE_FAX, 0x02)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sCallMode))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sCallMode[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeCallMptyType aIn, uint8_t& aOut)
+{
+  static const uint8_t sCallMptyType[] = {
+    CONVERT(HFP_CALL_MPTY_TYPE_SINGLE, 0x00),
+    CONVERT(HFP_CALL_MPTY_TYPE_MULTI, 0x01)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sCallMptyType))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sCallMptyType[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeNetworkState aIn, uint8_t& aOut)
+{
+  static const uint8_t sNetworkState[] = {
+    CONVERT(HFP_NETWORK_STATE_NOT_AVAILABLE, 0x00),
+    CONVERT(HFP_NETWORK_STATE_AVAILABLE, 0x01)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sNetworkState))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sNetworkState[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeServiceType aIn, uint8_t& aOut)
+{
+  static const uint8_t sServiceType[] = {
+    CONVERT(HFP_SERVICE_TYPE_HOME, 0x00),
+    CONVERT(HFP_SERVICE_TYPE_ROAMING, 0x01)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sServiceType))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sServiceType[aIn];
+  return NS_OK;
+}
+
+nsresult
+Convert(BluetoothHandsfreeVolumeType aIn, uint8_t& aOut)
+{
+  static const uint8_t sVolumeType[] = {
+    CONVERT(HFP_VOLUME_TYPE_SPEAKER, 0x00),
+    CONVERT(HFP_VOLUME_TYPE_MICROPHONE, 0x01)
+  };
+  if (NS_WARN_IF(aIn >= MOZ_ARRAY_LENGTH(sVolumeType))) {
+    aOut = 0x00; // silences compiler warning
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sVolumeType[aIn];
+  return NS_OK;
+}
+
+nsresult
 Convert(BluetoothPropertyType aIn, uint8_t& aOut)
 {
   static const uint8_t sPropertyType[] = {
     CONVERT(PROPERTY_UNKNOWN, 0x00),
     CONVERT(PROPERTY_BDNAME, 0x01),
     CONVERT(PROPERTY_BDADDR, 0x02),
     CONVERT(PROPERTY_UUIDS, 0x03),
     CONVERT(PROPERTY_CLASS_OF_DEVICE, 0x04),
@@ -502,16 +760,79 @@ PackPDU(const BluetoothConfigurationPara
 
 nsresult
 PackPDU(const BluetoothDaemonPDUHeader& aIn, BluetoothDaemonPDU& aPDU)
 {
   return PackPDU(aIn.mService, aIn.mOpcode, aIn.mLength, aPDU);
 }
 
 nsresult
+PackPDU(const BluetoothHandsfreeAtResponse& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeAtResponse, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallAddressType& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeCallAddressType, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallDirection& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeCallDirection, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallMode& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeCallMode, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallMptyType& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeCallMptyType, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallState& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeCallState, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeNetworkState& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeNetworkState, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeServiceType& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeServiceType, uint8_t>(aIn), aPDU);
+}
+
+nsresult
+PackPDU(const BluetoothHandsfreeVolumeType& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackConversion<BluetoothHandsfreeVolumeType, uint8_t>(aIn), aPDU);
+}
+
+nsresult
 PackPDU(const BluetoothNamedValue& aIn, BluetoothDaemonPDU& aPDU)
 {
   nsresult rv = PackPDU(
     PackConversion<nsString, BluetoothPropertyType>(aIn.name()), aPDU);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
@@ -587,16 +908,22 @@ PackPDU(BluetoothSocketType aIn, Bluetoo
 
 nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, bool& aOut)
 {
   return UnpackPDU(aPDU, UnpackConversion<uint8_t, bool>(aOut));
 }
 
 nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, char& aOut)
+{
+  return UnpackPDU(aPDU, UnpackConversion<uint8_t, char>(aOut));
+}
+
+nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAclState& aOut)
 {
   return UnpackPDU(aPDU, UnpackConversion<uint8_t, BluetoothAclState>(aOut));
 }
 
 nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothBondState& aOut)
 {
@@ -606,16 +933,60 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, Blue
 nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothTypeOfDevice& aOut)
 {
   return UnpackPDU(
     aPDU, UnpackConversion<int32_t, BluetoothTypeOfDevice>(aOut));
 }
 
 nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeAudioState& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeAudioState>(aOut));
+}
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeCallHoldType& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeCallHoldType>(aOut));
+}
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeConnectionState& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeConnectionState>(aOut));
+}
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeNRECState& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeNRECState>(aOut));
+}
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU,
+          BluetoothHandsfreeVoiceRecognitionState& aOut)
+{
+  return UnpackPDU(
+    aPDU,
+    UnpackConversion<uint8_t, BluetoothHandsfreeVoiceRecognitionState>(aOut));
+}
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeVolumeType& aOut)
+{
+  return UnpackPDU(
+    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeVolumeType>(aOut));
+}
+
+nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothProperty& aOut)
 {
   nsresult rv = UnpackPDU(aPDU, aOut.mType);
   if (NS_FAILED(rv)) {
     return rv;
   }
   uint16_t len;
   rv = UnpackPDU(aPDU, len);
@@ -748,9 +1119,69 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, Blue
 }
 
 nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothStatus& aOut)
 {
   return UnpackPDU(aPDU, UnpackConversion<uint8_t, BluetoothStatus>(aOut));
 }
 
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, nsDependentCString& aOut)
+{
+  // We get a pointer to the first character in the PDU, a length
+  // of 1 ensures we consume the \0 byte. With 'str' pointing to
+  // the string in the PDU, we can copy the actual bytes.
+
+  const char* str = reinterpret_cast<const char*>(aPDU.Consume(1));
+  if (NS_WARN_IF(!str)) {
+    return NS_ERROR_ILLEGAL_VALUE; // end of PDU
+  }
+
+  const char* end = static_cast<char*>(memchr(str, '\0', aPDU.GetSize()));
+  if (NS_WARN_IF(!end)) {
+    return NS_ERROR_ILLEGAL_VALUE; // no string terminator
+  }
+
+  ptrdiff_t len = end - str;
+
+  const uint8_t* rest = aPDU.Consume(len);
+  if (NS_WARN_IF(!rest)) {
+    // We couldn't consume bytes that should have been there.
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  aOut.Rebind(str, len);
+
+  return NS_OK;
+}
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackCString0& aOut)
+{
+  nsDependentCString cstring;
+
+  nsresult rv = UnpackPDU(aPDU, cstring);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  aOut.mString->AssignASCII(cstring.get(), cstring.Length());
+
+  return NS_OK;
+}
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackString0& aOut)
+{
+  nsDependentCString cstring;
+
+  nsresult rv = UnpackPDU(aPDU, cstring);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  *aOut.mString = NS_ConvertUTF8toUTF16(cstring);
+
+  return NS_OK;
+}
+
 END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.h
+++ b/dom/bluetooth2/bluedroid/BluetoothDaemonHelpers.h
@@ -79,28 +79,55 @@ struct BluetoothServiceName {
 
 nsresult
 Convert(bool aIn, uint8_t& aOut);
 
 nsresult
 Convert(bool aIn, BluetoothScanMode& aOut);
 
 nsresult
+Convert(int aIn, uint8_t& aOut);
+
+nsresult
 Convert(int aIn, int16_t& aOut);
 
 nsresult
 Convert(uint8_t aIn, bool& aOut);
 
 nsresult
+Convert(uint8_t aIn, char& aOut);
+
+nsresult
+Convert(uint8_t aIn, int& aOut);
+
+nsresult
 Convert(uint8_t aIn, BluetoothAclState& aOut);
 
 nsresult
 Convert(uint8_t aIn, BluetoothBondState& aOut);
 
 nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeAudioState& aOut);
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeCallHoldType& aOut);
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeConnectionState& aOut);
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeNRECState& aOut);
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeVoiceRecognitionState& aOut);
+
+nsresult
+Convert(uint8_t aIn, BluetoothHandsfreeVolumeType& aOut);
+
+nsresult
 Convert(uint8_t aIn, BluetoothTypeOfDevice& aOut);
 
 nsresult
 Convert(uint8_t aIn, BluetoothPropertyType& aOut);
 
 nsresult
 Convert(uint8_t aIn, BluetoothScanMode& aOut);
 
@@ -133,16 +160,43 @@ Convert(const nsAString& aIn, BluetoothS
 
 nsresult
 Convert(BluetoothAclState aIn, bool& aOut);
 
 nsresult
 Convert(const BluetoothAddress& aIn, nsAString& aOut);
 
 nsresult
+Convert(BluetoothHandsfreeAtResponse aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeCallAddressType aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeCallDirection aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeCallState aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeCallMode aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeCallMptyType aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeNetworkState aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeServiceType aIn, uint8_t& aOut);
+
+nsresult
+Convert(BluetoothHandsfreeVolumeType aIn, uint8_t& aOut);
+
+nsresult
 Convert(BluetoothPropertyType aIn, uint8_t& aOut);
 
 nsresult
 Convert(const BluetoothRemoteName& aIn, nsAString& aOut);
 
 nsresult
 Convert(BluetoothScanMode aIn, uint8_t& aOut);
 
@@ -188,16 +242,43 @@ PackPDU(const BluetoothAddress& aIn, Blu
 
 nsresult
 PackPDU(const BluetoothConfigurationParameter& aIn, BluetoothDaemonPDU& aPDU);
 
 nsresult
 PackPDU(const BluetoothDaemonPDUHeader& aIn, BluetoothDaemonPDU& aPDU);
 
 nsresult
+PackPDU(const BluetoothHandsfreeAtResponse& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallAddressType& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallDirection& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallMode& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallMptyType& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeCallState& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeNetworkState& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeServiceType& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
+PackPDU(const BluetoothHandsfreeVolumeType& aIn, BluetoothDaemonPDU& aPDU);
+
+nsresult
 PackPDU(const BluetoothNamedValue& aIn, BluetoothDaemonPDU& aPDU);
 
 nsresult
 PackPDU(const BluetoothPinCode& aIn, BluetoothDaemonPDU& aPDU);
 
 nsresult
 PackPDU(BluetoothPropertyType aIn, BluetoothDaemonPDU& aPDU);
 
@@ -274,16 +355,39 @@ PackPDU(const PackArray<T>& aIn, Bluetoo
 template<>
 inline nsresult
 PackPDU<uint8_t>(const PackArray<uint8_t>& aIn, BluetoothDaemonPDU& aPDU)
 {
   /* Write raw bytes in one pass */
   return aPDU.Write(aIn.mData, aIn.mLength);
 }
 
+/* |PackCString0| is a helper for packing 0-terminated C string,
+ * including the \0 character. Pass an instance of this structure
+ * as the first argument to |PackPDU| to pack a string.
+ */
+struct PackCString0
+{
+  PackCString0(const nsCString& aString)
+  : mString(aString)
+  { }
+
+  const nsCString& mString;
+};
+
+/* This implementation of |PackPDU| packs a 0-terminated C string.
+ */
+inline nsresult
+PackPDU(const PackCString0& aIn, BluetoothDaemonPDU& aPDU)
+{
+  return PackPDU(
+    PackArray<uint8_t>(reinterpret_cast<const uint8_t*>(aIn.mString.get()),
+                       aIn.mString.Length() + 1), aPDU);
+}
+
 template <typename T1, typename T2>
 inline nsresult
 PackPDU(const T1& aIn1, const T2& aIn2, BluetoothDaemonPDU& aPDU)
 {
   nsresult rv = PackPDU(aIn1, aPDU);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -347,16 +451,51 @@ PackPDU(const T1& aIn1, const T2& aIn2, 
   }
   rv = PackPDU(aIn4, aPDU);
   if (NS_FAILED(rv)) {
     return rv;
   }
   return PackPDU(aIn5, aPDU);
 }
 
+template <typename T1, typename T2, typename T3,
+          typename T4, typename T5, typename T6,
+          typename T7>
+inline nsresult
+PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3,
+        const T4& aIn4, const T5& aIn5, const T6& aIn6,
+        const T7& aIn7, BluetoothDaemonPDU& aPDU)
+{
+  nsresult rv = PackPDU(aIn1, aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = PackPDU(aIn2, aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = PackPDU(aIn3, aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = PackPDU(aIn4, aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = PackPDU(aIn5, aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = PackPDU(aIn6, aPDU);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return PackPDU(aIn7, aPDU);
+}
+
 //
 // Unpacking
 //
 
 inline nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, int8_t& aOut)
 {
   return aPDU.Read(aOut);
@@ -385,16 +524,19 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, uint
 {
   return aPDU.Read(aOut);
 }
 
 nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, bool& aOut);
 
 nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, char& aOut);
+
+nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAclState& aOut);
 
 inline nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAddress& aOut)
 {
   return aPDU.Read(aOut.mAddr, sizeof(aOut.mAddr));
 }
 
@@ -414,16 +556,35 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, Blue
   }
   return UnpackPDU(aPDU, aOut.mLength);
 }
 
 nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothTypeOfDevice& aOut);
 
 nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeAudioState& aOut);
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeCallHoldType& aOut);
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeConnectionState& aOut);
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeNRECState& aOut);
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU,
+          BluetoothHandsfreeVoiceRecognitionState& aOut);
+
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeVolumeType& aOut);
+
+nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothRemoteInfo& aOut);
 
 inline nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothRemoteName& aOut)
 {
   return aPDU.Read(aOut.mName, sizeof(aOut.mName));
 }
 
@@ -446,16 +607,19 @@ nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothStatus& aOut);
 
 inline nsresult
 UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothUuid& aOut)
 {
   return aPDU.Read(aOut.mUuid, sizeof(aOut.mUuid));
 }
 
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, nsDependentCString& aOut);
+
 /* |UnpackConversion| is a helper for convering unpacked values. Pass
  * an instance of this structure to |UnpackPDU| to read a value from
  * the PDU in the input type and convert it to the output type.
  */
 template<typename Tin, typename Tout>
 struct UnpackConversion {
   UnpackConversion(Tout& aOut)
   : mOut(aOut)
@@ -537,16 +701,54 @@ UnpackPDU(BluetoothDaemonPDU& aPDU, nsTA
     nsresult rv = UnpackPDU(aPDU, aOut[i]);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
   return NS_OK;
 }
 
+/* |UnpackCString0| is a helper for unpacking 0-terminated C string,
+ * including the \0 character. Pass an instance of this structure
+ * as the first argument to |UnpackPDU| to unpack a string.
+ */
+struct UnpackCString0
+{
+  UnpackCString0(nsCString& aString)
+    : mString(&aString)
+  { }
+
+  nsCString* mString; // non-null by construction
+};
+
+/* This implementation of |UnpackPDU| unpacks a 0-terminated C string.
+ */
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackCString0& aOut);
+
+/* |UnpackString0| is a helper for unpacking 0-terminated C string,
+ * including the \0 character. Pass an instance of this structure
+ * as the first argument to |UnpackPDU| to unpack a C string and convert
+ * it to wide-character encoding.
+ */
+struct UnpackString0
+{
+  UnpackString0(nsString& aString)
+    : mString(&aString)
+  { }
+
+  nsString* mString; // non-null by construction
+};
+
+/* This implementation of |UnpackPDU| unpacks a 0-terminated C string
+ * and converts it to wide-character encoding.
+ */
+nsresult
+UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackString0& aOut);
+
 //
 // Init operators
 //
 
 // |PDUInitOP| provides functionality for init operators that unpack PDUs.
 class PDUInitOp
 {
 protected:
--- a/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "BluetoothDaemonInterface.h"
+#include "BluetoothDaemonHandsfreeInterface.h"
 #include "BluetoothDaemonHelpers.h"
 #include "BluetoothDaemonSetupInterface.h"
 #include "BluetoothDaemonSocketInterface.h"
 #include "BluetoothInterfaceHelpers.h"
 #include "mozilla/unused.h"
 
 using namespace mozilla::ipc;
 
@@ -1451,20 +1452,27 @@ private:
 // Protocol handling
 //
 
 class BluetoothDaemonProtocol MOZ_FINAL
   : public BluetoothDaemonPDUConsumer
   , public BluetoothDaemonSetupModule
   , public BluetoothDaemonCoreModule
   , public BluetoothDaemonSocketModule
+  , public BluetoothDaemonHandsfreeModule
 {
 public:
   BluetoothDaemonProtocol(BluetoothDaemonConnection* aConnection);
 
+  nsresult RegisterModule(uint8_t aId, uint8_t aMode,
+                          BluetoothSetupResultHandler* aRes) MOZ_OVERRIDE;
+
+  nsresult UnregisterModule(uint8_t aId,
+                            BluetoothSetupResultHandler* aRes) MOZ_OVERRIDE;
+
   // Outgoing PDUs
   //
 
   nsresult Send(BluetoothDaemonPDU* aPDU, void* aUserData) MOZ_OVERRIDE;
 
   void StoreUserData(const BluetoothDaemonPDU& aPDU) MOZ_OVERRIDE;
 
   // Incoming PUDs
@@ -1476,29 +1484,45 @@ public:
 
 private:
   void HandleSetupSvc(const BluetoothDaemonPDUHeader& aHeader,
                       BluetoothDaemonPDU& aPDU, void* aUserData);
   void HandleCoreSvc(const BluetoothDaemonPDUHeader& aHeader,
                      BluetoothDaemonPDU& aPDU, void* aUserData);
   void HandleSocketSvc(const BluetoothDaemonPDUHeader& aHeader,
                        BluetoothDaemonPDU& aPDU, void* aUserData);
+  void HandleHandsfreeSvc(const BluetoothDaemonPDUHeader& aHeader,
+                          BluetoothDaemonPDU& aPDU, void* aUserData);
 
   BluetoothDaemonConnection* mConnection;
   nsTArray<void*> mUserDataQ;
 };
 
 BluetoothDaemonProtocol::BluetoothDaemonProtocol(
   BluetoothDaemonConnection* aConnection)
   : mConnection(aConnection)
 {
   MOZ_ASSERT(mConnection);
 }
 
 nsresult
+BluetoothDaemonProtocol::RegisterModule(uint8_t aId, uint8_t aMode,
+                                        BluetoothSetupResultHandler* aRes)
+{
+  return BluetoothDaemonSetupModule::RegisterModuleCmd(aId, aMode, aRes);
+}
+
+nsresult
+BluetoothDaemonProtocol::UnregisterModule(uint8_t aId,
+                                          BluetoothSetupResultHandler* aRes)
+{
+  return BluetoothDaemonSetupModule::UnregisterModuleCmd(aId, aRes);
+}
+
+nsresult
 BluetoothDaemonProtocol::Send(BluetoothDaemonPDU* aPDU, void* aUserData)
 {
   MOZ_ASSERT(aPDU);
 
   aPDU->SetUserData(aUserData);
   aPDU->UpdateHeader();
   return mConnection->Send(aPDU); // Forward PDU to command channel
 }
@@ -1523,26 +1547,38 @@ void
 BluetoothDaemonProtocol::HandleSocketSvc(
   const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
   void* aUserData)
 {
   BluetoothDaemonSocketModule::HandleSvc(aHeader, aPDU, aUserData);
 }
 
 void
+BluetoothDaemonProtocol::HandleHandsfreeSvc(
+  const BluetoothDaemonPDUHeader& aHeader, BluetoothDaemonPDU& aPDU,
+  void* aUserData)
+{
+  BluetoothDaemonHandsfreeModule::HandleSvc(aHeader, aPDU, aUserData);
+}
+
+void
 BluetoothDaemonProtocol::Handle(BluetoothDaemonPDU& aPDU)
 {
   static void (BluetoothDaemonProtocol::* const HandleSvc[])(
     const BluetoothDaemonPDUHeader&, BluetoothDaemonPDU&, void*) = {
     INIT_ARRAY_AT(BluetoothDaemonSetupModule::SERVICE_ID,
       &BluetoothDaemonProtocol::HandleSetupSvc),
     INIT_ARRAY_AT(BluetoothDaemonCoreModule::SERVICE_ID,
       &BluetoothDaemonProtocol::HandleCoreSvc),
     INIT_ARRAY_AT(BluetoothDaemonSocketModule::SERVICE_ID,
-      &BluetoothDaemonProtocol::HandleSocketSvc)
+      &BluetoothDaemonProtocol::HandleSocketSvc),
+    INIT_ARRAY_AT(0x03, nullptr), // HID host
+    INIT_ARRAY_AT(0x04, nullptr), // PAN
+    INIT_ARRAY_AT(BluetoothDaemonHandsfreeModule::SERVICE_ID,
+      &BluetoothDaemonProtocol::HandleHandsfreeSvc)
   };
 
   BluetoothDaemonPDUHeader header;
 
   if (NS_FAILED(UnpackPDU(aPDU, header)) ||
       NS_WARN_IF(!(header.mService < MOZ_ARRAY_LENGTH(HandleSvc))) ||
       NS_WARN_IF(!(HandleSvc[header.mService]))) {
     return;
@@ -2105,17 +2141,23 @@ BluetoothDaemonInterface::GetBluetoothSo
   mSocketInterface = new BluetoothDaemonSocketInterface(mProtocol);
 
   return mSocketInterface;
 }
 
 BluetoothHandsfreeInterface*
 BluetoothDaemonInterface::GetBluetoothHandsfreeInterface()
 {
-  return nullptr;
+  if (mHandsfreeInterface) {
+    return mHandsfreeInterface;
+  }
+
+  mHandsfreeInterface = new BluetoothDaemonHandsfreeInterface(mProtocol);
+
+  return mHandsfreeInterface;
 }
 
 BluetoothA2dpInterface*
 BluetoothDaemonInterface::GetBluetoothA2dpInterface()
 {
   return nullptr;
 }
 
--- a/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothDaemonInterface.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_dom_bluetooth_bluedroid_bluetoothdaemoninterface_h__
 #define mozilla_dom_bluetooth_bluedroid_bluetoothdaemoninterface_h__
 
 #include "BluetoothInterface.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothDaemonChannel;
+class BluetoothDaemonHandsfreeInterface;
 class BluetoothDaemonProtocol;
 class BluetoothDaemonSocketInterface;
 
 class BluetoothDaemonInterface MOZ_FINAL : public BluetoothInterface
 {
 public:
   class CleanupResultHandler;
   class InitResultHandler;
@@ -121,13 +122,14 @@ private:
 
   nsAutoPtr<BluetoothDaemonChannel> mCmdChannel;
   nsAutoPtr<BluetoothDaemonChannel> mNtfChannel;
   nsAutoPtr<BluetoothDaemonProtocol> mProtocol;
 
   nsTArray<nsRefPtr<BluetoothResultHandler> > mResultHandlerQ;
 
   nsAutoPtr<BluetoothDaemonSocketInterface> mSocketInterface;
+  nsAutoPtr<BluetoothDaemonHandsfreeInterface> mHandsfreeInterface;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
@@ -72,155 +72,21 @@ static nsTArray<nsRefPtr<BluetoothReplyR
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sFetchUuidsRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
 
 /**
- *  Classes only used in this file
- */
-
-class SetupAfterEnabledTask MOZ_FINAL : public nsRunnable
-{
-public:
-  class SetAdapterPropertyResultHandler MOZ_FINAL
-  : public BluetoothResultHandler
-  {
-  public:
-    void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
-    {
-      BT_LOGR("Fail to set: BT_SCAN_MODE_CONNECTABLE");
-    }
-  };
-
-  NS_IMETHOD
-  Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // Bluetooth just enabled, clear profile controllers and runnable arrays.
-    sControllerArray.Clear();
-    sChangeDiscoveryRunnableArray.Clear();
-    sSetPropertyRunnableArray.Clear();
-    sGetDeviceRunnableArray.Clear();
-    sFetchUuidsRunnableArray.Clear();
-    sBondingRunnableArray.Clear();
-    sUnbondingRunnableArray.Clear();
-
-    // Bluetooth scan mode is SCAN_MODE_CONNECTABLE by default, i.e., It should
-    // be connectable and non-discoverable.
-    NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
-    sBtInterface->SetAdapterProperty(
-      BluetoothNamedValue(NS_ConvertUTF8toUTF16("Discoverable"), false),
-      new SetAdapterPropertyResultHandler());
-
-    // Trigger BluetoothOppManager to listen
-    BluetoothOppManager* opp = BluetoothOppManager::Get();
-    if (!opp || !opp->Listen()) {
-      BT_LOGR("Fail to start BluetoothOppManager listening");
-    }
-
-    return NS_OK;
-  }
-};
-
-/* |ProfileDeinitResultHandler| collects the results of all profile
- * result handlers and calls |Proceed| after all results handlers
- * have been run.
- */
-class ProfileDeinitResultHandler MOZ_FINAL
-: public BluetoothProfileResultHandler
-{
-public:
-  ProfileDeinitResultHandler(unsigned char aNumProfiles)
-  : mNumProfiles(aNumProfiles)
-  {
-    MOZ_ASSERT(mNumProfiles);
-  }
-
-  void Deinit() MOZ_OVERRIDE
-  {
-    if (!(--mNumProfiles)) {
-      Proceed();
-    }
-  }
-
-  void OnError(nsresult aResult) MOZ_OVERRIDE
-  {
-    if (!(--mNumProfiles)) {
-      Proceed();
-    }
-  }
-
-private:
-  void Proceed() const
-  {
-    sBtInterface->Cleanup(nullptr);
-  }
-
-  unsigned char mNumProfiles;
-};
-
-class CleanupTask MOZ_FINAL : public nsRunnable
-{
-public:
-  NS_IMETHOD
-  Run()
-  {
-    static void (* const sDeinitManager[])(BluetoothProfileResultHandler*) = {
-      BluetoothHfpManager::DeinitHfpInterface,
-      BluetoothA2dpManager::DeinitA2dpInterface,
-      BluetoothGattManager::DeinitGattInterface
-    };
-
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // Return error if BluetoothService is unavailable
-    BluetoothService* bs = BluetoothService::Get();
-    NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
-
-    // Cleanup static adapter properties and notify adapter.
-    sAdapterBdAddress.Truncate();
-    sAdapterBdName.Truncate();
-
-    InfallibleTArray<BluetoothNamedValue> props;
-    BT_APPEND_NAMED_VALUE(props, "Name", sAdapterBdName);
-    BT_APPEND_NAMED_VALUE(props, "Address", sAdapterBdAddress);
-    if (sAdapterDiscoverable) {
-      sAdapterDiscoverable = false;
-      BT_APPEND_NAMED_VALUE(props, "Discoverable", false);
-    }
-    if (sAdapterDiscovering) {
-      sAdapterDiscovering = false;
-      BT_APPEND_NAMED_VALUE(props, "Discovering", false);
-    }
-
-    BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
-                           NS_LITERAL_STRING(KEY_ADAPTER), props);
-    bs->DistributeSignal(signal);
-
-    // Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
-    nsRefPtr<ProfileDeinitResultHandler> res =
-      new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager));
-
-    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDeinitManager); ++i) {
-      sDeinitManager[i](res);
-    }
-
-    return NS_OK;
-  }
-};
-
-/**
  *  Static callback functions
  */
-static ControlPlayStatus
-PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus)
+ControlPlayStatus
+BluetoothServiceBluedroid::PlayStatusStringToControlPlayStatus(
+  const nsAString& aPlayStatus)
 {
   ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
   if (aPlayStatus.EqualsLiteral("STOPPED")) {
     playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
   } else if (aPlayStatus.EqualsLiteral("PLAYING")) {
     playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING;
   } else if (aPlayStatus.EqualsLiteral("PAUSED")) {
     playStatus = ControlPlayStatus::PLAYSTATUS_PAUSED;
@@ -233,51 +99,49 @@ PlayStatusStringToControlPlayStatus(cons
   }
 
   return playStatus;
 }
 
 /**
  *  Static functions
  */
-static bool
-EnsureBluetoothHalLoad()
+bool
+BluetoothServiceBluedroid::EnsureBluetoothHalLoad()
 {
   sBtInterface = BluetoothInterface::GetInstance();
   NS_ENSURE_TRUE(sBtInterface, false);
 
   return true;
 }
 
-class EnableResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::EnableResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     BT_LOGR("BluetoothInterface::Enable failed: %d", aStatus);
 
-    nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      BT_WARNING("Failed to dispatch to main thread!");
-    }
+    BluetoothService::AcknowledgeToggleBt(false);
   }
 };
 
 /* |ProfileInitResultHandler| collects the results of all profile
  * result handlers and calls |Proceed| after all results handlers
  * have been run.
  */
-class ProfileInitResultHandler MOZ_FINAL
-: public BluetoothProfileResultHandler
+class BluetoothServiceBluedroid::ProfileInitResultHandler MOZ_FINAL
+  : public BluetoothProfileResultHandler
 {
 public:
   ProfileInitResultHandler(unsigned char aNumProfiles)
-  : mNumProfiles(aNumProfiles)
+    : mNumProfiles(aNumProfiles)
   {
     MOZ_ASSERT(mNumProfiles);
   }
 
   void Init() MOZ_OVERRIDE
   {
     if (!(--mNumProfiles)) {
       Proceed();
@@ -295,17 +159,18 @@ private:
   void Proceed() const
   {
     sBtInterface->Enable(new EnableResultHandler());
   }
 
   unsigned char mNumProfiles;
 };
 
-class InitResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::InitResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   void Init() MOZ_OVERRIDE
   {
     static void (* const sInitManager[])(BluetoothProfileResultHandler*) = {
       BluetoothHfpManager::InitHfpInterface,
       BluetoothA2dpManager::InitA2dpInterface,
       BluetoothGattManager::InitGattInterface
@@ -327,91 +192,81 @@ public:
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     BT_LOGR("BluetoothInterface::Init failed: %d", aStatus);
 
     sBtInterface = nullptr;
 
-    nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      BT_WARNING("Failed to dispatch to main thread!");
-    }
+    BluetoothService::AcknowledgeToggleBt(false);
   }
 };
 
-static nsresult
-StartGonkBluetooth()
+nsresult
+BluetoothServiceBluedroid::StartGonkBluetooth()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
 
   if (bs->IsEnabled()) {
     // Keep current enable status
-    nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      BT_WARNING("Failed to dispatch to main thread!");
-    }
+    BluetoothService::AcknowledgeToggleBt(true);
     return NS_OK;
   }
 
   sBtInterface->Init(reinterpret_cast<BluetoothServiceBluedroid*>(bs),
                      new InitResultHandler());
 
   return NS_OK;
 }
 
-class DisableResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::DisableResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     BT_LOGR("BluetoothInterface::Disable failed: %d", aStatus);
 
-    nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      BT_WARNING("Failed to dispatch to main thread!");
-    }
+    BluetoothService::AcknowledgeToggleBt(true);
   }
 };
 
-static nsresult
-StopGonkBluetooth()
+nsresult
+BluetoothServiceBluedroid::StopGonkBluetooth()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
 
   if (!bs->IsEnabled()) {
     // Keep current enable status
-    nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      BT_WARNING("Failed to dispatch to main thread!");
-    }
+    BluetoothService::AcknowledgeToggleBt(false);
     return NS_OK;
   }
 
   sBtInterface->Disable(new DisableResultHandler());
 
   return NS_OK;
 }
 
-static void
-ReplyStatusError(BluetoothReplyRunnable* aBluetoothReplyRunnable,
-                 BluetoothStatus aStatusCode, const nsAString& aCustomMsg)
+void
+BluetoothServiceBluedroid::ReplyStatusError(
+  BluetoothReplyRunnable* aBluetoothReplyRunnable,
+  BluetoothStatus aStatusCode, const nsAString& aCustomMsg)
 {
   MOZ_ASSERT(aBluetoothReplyRunnable, "Reply runnable is nullptr");
 
   BT_LOGR("error code(%d)", aStatusCode);
 
   nsAutoString replyError;
   replyError.Assign(aCustomMsg);
 
@@ -455,22 +310,17 @@ BluetoothServiceBluedroid::StartInternal
 
   // aRunnable will be a nullptr while startup
   if(aRunnable) {
     sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
   }
 
   nsresult ret = StartGonkBluetooth();
   if (NS_FAILED(ret)) {
-    nsRefPtr<nsRunnable> runnable =
-      new BluetoothService::ToggleBtAck(false);
-
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      BT_WARNING("Failed to dispatch to main thread!");
-    }
+    BluetoothService::AcknowledgeToggleBt(false);
 
     // Reject Promise
     if(aRunnable) {
       DispatchBluetoothReply(aRunnable, BluetoothValue(),
                              NS_LITERAL_STRING(ERR_START_BLUETOOTH));
       sChangeAdapterStateRunnableArray.RemoveElement(aRunnable);
     }
 
@@ -487,22 +337,17 @@ BluetoothServiceBluedroid::StopInternal(
 
   // aRunnable will be a nullptr during starup and shutdown
   if(aRunnable) {
     sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
   }
 
   nsresult ret = StopGonkBluetooth();
   if (NS_FAILED(ret)) {
-    nsRefPtr<nsRunnable> runnable =
-      new BluetoothService::ToggleBtAck(true);
-
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      BT_WARNING("Failed to dispatch to main thread!");
-    }
+    BluetoothService::AcknowledgeToggleBt(true);
 
     // Reject Promise
     if(aRunnable) {
       DispatchBluetoothReply(aRunnable, BluetoothValue(),
                              NS_LITERAL_STRING(ERR_STOP_BLUETOOTH));
       sChangeAdapterStateRunnableArray.RemoveElement(aRunnable);
     }
 
@@ -549,18 +394,19 @@ BluetoothServiceBluedroid::GetAdaptersIn
     BT_APPEND_NAMED_VALUE(adaptersProperties.get_ArrayOfBluetoothNamedValue(),
                           "Adapter", properties);
   }
 
   DispatchBluetoothReply(aRunnable, adaptersProperties, EmptyString());
   return NS_OK;
 }
 
-class GetRemoteDevicePropertiesResultHandler MOZ_FINAL
-: public BluetoothResultHandler
+class BluetoothServiceBluedroid::GetRemoteDevicePropertiesResultHandler
+  MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   GetRemoteDevicePropertiesResultHandler(const nsAString& aDeviceAddress)
   : mDeviceAddress(aDeviceAddress)
   { }
 
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
@@ -648,17 +494,18 @@ BluetoothServiceBluedroid::GetPairedDevi
     // Retrieve all properties of devices
     sBtInterface->GetRemoteDeviceProperties(aDeviceAddress[i],
       new GetRemoteDevicePropertiesResultHandler(aDeviceAddress[i]));
   }
 
   return NS_OK;
 }
 
-class StartDiscoveryResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::StartDiscoveryResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   StartDiscoveryResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   { }
 
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
@@ -680,17 +527,18 @@ BluetoothServiceBluedroid::StartDiscover
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
   sBtInterface->StartDiscovery(new StartDiscoveryResultHandler(aRunnable));
 
   return NS_OK;
 }
 
-class CancelDiscoveryResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::CancelDiscoveryResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   CancelDiscoveryResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   { }
 
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
@@ -712,17 +560,18 @@ BluetoothServiceBluedroid::StopDiscovery
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
   sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
 
   return NS_OK;
 }
 
-class GetRemoteServicesResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::GetRemoteServicesResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   GetRemoteServicesResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   { }
 
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
@@ -754,17 +603,18 @@ BluetoothServiceBluedroid::FetchUuidsInt
   sFetchUuidsRunnableArray.AppendElement(aRunnable);
 
   sBtInterface->GetRemoteServices(aDeviceAddress,
     new GetRemoteServicesResultHandler(aRunnable));
 
   return NS_OK;
 }
 
-class SetAdapterPropertyResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::SetAdapterPropertyResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   SetAdapterPropertyResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   { }
 
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
@@ -805,17 +655,18 @@ BluetoothServiceBluedroid::GetServiceCha
 bool
 BluetoothServiceBluedroid::UpdateSdpRecords(
   const nsAString& aDeviceAddress,
   BluetoothProfileManagerBase* aManager)
 {
   return true;
 }
 
-class CreateBondResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::CreateBondResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   CreateBondResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   {
     MOZ_ASSERT(mRunnable);
   }
 
@@ -840,17 +691,18 @@ BluetoothServiceBluedroid::CreatePairedD
 
   sBondingRunnableArray.AppendElement(aRunnable);
 
   sBtInterface->CreateBond(aDeviceAddress,
                            new CreateBondResultHandler(aRunnable));
   return NS_OK;
 }
 
-class RemoveBondResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::RemoveBondResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   RemoveBondResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   {
     MOZ_ASSERT(mRunnable);
   }
 
@@ -875,17 +727,18 @@ BluetoothServiceBluedroid::RemoveDeviceI
   sUnbondingRunnableArray.AppendElement(aRunnable);
 
   sBtInterface->RemoveBond(aDeviceAddress,
                            new RemoveBondResultHandler(aRunnable));
 
   return NS_OK;
 }
 
-class PinReplyResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::PinReplyResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   PinReplyResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   { }
 
   void PinReply() MOZ_OVERRIDE
   {
@@ -917,17 +770,18 @@ BluetoothServiceBluedroid::SetPinCodeInt
 void
 BluetoothServiceBluedroid::SetPasskeyInternal(
   const nsAString& aDeviceAddress, uint32_t aPasskey,
   BluetoothReplyRunnable* aRunnable)
 {
   return;
 }
 
-class SspReplyResultHandler MOZ_FINAL : public BluetoothResultHandler
+class BluetoothServiceBluedroid::SspReplyResultHandler MOZ_FINAL
+  : public BluetoothResultHandler
 {
 public:
   SspReplyResultHandler(BluetoothReplyRunnable* aRunnable)
   : mRunnable(aRunnable)
   { }
 
   void SspReply() MOZ_OVERRIDE
   {
@@ -953,35 +807,36 @@ BluetoothServiceBluedroid::SetPairingCon
 
   ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
 
   sBtInterface->SspReply(aDeviceAddress,
                          NS_ConvertUTF8toUTF16("PasskeyConfirmation"),
                          aConfirm, 0, new SspReplyResultHandler(aRunnable));
 }
 
-static void
-NextBluetoothProfileController()
+void
+BluetoothServiceBluedroid::NextBluetoothProfileController()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Remove the completed task at the head
   NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty());
   sControllerArray.RemoveElementAt(0);
 
   // Start the next task if task array is not empty
   if (!sControllerArray.IsEmpty()) {
     sControllerArray[0]->StartSession();
   }
 }
 
-static void
-ConnectDisconnect(bool aConnect, const nsAString& aDeviceAddress,
-                  BluetoothReplyRunnable* aRunnable,
-                  uint16_t aServiceUuid, uint32_t aCod = 0)
+void
+BluetoothServiceBluedroid::ConnectDisconnect(
+  bool aConnect, const nsAString& aDeviceAddress,
+  BluetoothReplyRunnable* aRunnable,
+  uint16_t aServiceUuid, uint32_t aCod)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   BluetoothProfileController* controller =
     new BluetoothProfileController(aConnect, aDeviceAddress, aRunnable,
                                    NextBluetoothProfileController,
                                    aServiceUuid, aCod);
@@ -1215,42 +1070,137 @@ void
 BluetoothServiceBluedroid::ToggleCalls(BluetoothReplyRunnable* aRunnable)
 {
 }
 
 //
 // Bluetooth notifications
 //
 
+/* |ProfileDeinitResultHandler| collects the results of all profile
+ * result handlers and calls |Proceed| after all results handlers
+ * have been run.
+ */
+class BluetoothServiceBluedroid::ProfileDeinitResultHandler MOZ_FINAL
+  : public BluetoothProfileResultHandler
+{
+public:
+  ProfileDeinitResultHandler(unsigned char aNumProfiles)
+    : mNumProfiles(aNumProfiles)
+  {
+    MOZ_ASSERT(mNumProfiles);
+  }
+
+  void Deinit() MOZ_OVERRIDE
+  {
+    if (!(--mNumProfiles)) {
+      Proceed();
+    }
+  }
+
+  void OnError(nsresult aResult) MOZ_OVERRIDE
+  {
+    if (!(--mNumProfiles)) {
+      Proceed();
+    }
+  }
+
+private:
+  void Proceed() const
+  {
+    sBtInterface->Cleanup(nullptr);
+  }
+
+  unsigned char mNumProfiles;
+};
+
+class BluetoothServiceBluedroid::SetAdapterPropertyDiscoverableResultHandler
+  MOZ_FINAL
+  : public BluetoothResultHandler
+{
+public:
+  void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
+  {
+    BT_LOGR("Fail to set: BT_SCAN_MODE_CONNECTABLE");
+  }
+};
+
 void
 BluetoothServiceBluedroid::AdapterStateChangedNotification(bool aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   BT_LOGR("BT_STATE: %d", aState);
 
   sAdapterEnabled = aState;
 
-  if (!sAdapterEnabled &&
-      NS_FAILED(NS_DispatchToMainThread(new CleanupTask()))) {
-    BT_WARNING("Failed to dispatch to main thread!");
-    return;
+  if (!sAdapterEnabled) {
+    static void (* const sDeinitManager[])(BluetoothProfileResultHandler*) = {
+      BluetoothHfpManager::DeinitHfpInterface,
+      BluetoothA2dpManager::DeinitA2dpInterface,
+      BluetoothGattManager::DeinitGattInterface
+    };
+
+    // Return error if BluetoothService is unavailable
+    BluetoothService* bs = BluetoothService::Get();
+    NS_ENSURE_TRUE_VOID(bs);
+
+    // Cleanup static adapter properties and notify adapter.
+    sAdapterBdAddress.Truncate();
+    sAdapterBdName.Truncate();
+
+    InfallibleTArray<BluetoothNamedValue> props;
+    BT_APPEND_NAMED_VALUE(props, "Name", sAdapterBdName);
+    BT_APPEND_NAMED_VALUE(props, "Address", sAdapterBdAddress);
+    if (sAdapterDiscoverable) {
+      sAdapterDiscoverable = false;
+      BT_APPEND_NAMED_VALUE(props, "Discoverable", false);
+    }
+    if (sAdapterDiscovering) {
+      sAdapterDiscovering = false;
+      BT_APPEND_NAMED_VALUE(props, "Discovering", false);
+    }
+
+    BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
+                           NS_LITERAL_STRING(KEY_ADAPTER), props);
+    bs->DistributeSignal(signal);
+
+    // Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
+    nsRefPtr<ProfileDeinitResultHandler> res =
+      new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager));
+
+    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDeinitManager); ++i) {
+      sDeinitManager[i](res);
+    }
   }
 
-  nsRefPtr<nsRunnable> runnable =
-    new BluetoothService::ToggleBtAck(sAdapterEnabled);
-  if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-    BT_WARNING("Failed to dispatch to main thread!");
-    return;
-  }
+  BluetoothService::AcknowledgeToggleBt(sAdapterEnabled);
+
+  if (sAdapterEnabled) {
+    // Bluetooth just enabled, clear profile controllers and runnable arrays.
+    sControllerArray.Clear();
+    sChangeDiscoveryRunnableArray.Clear();
+    sSetPropertyRunnableArray.Clear();
+    sGetDeviceRunnableArray.Clear();
+    sFetchUuidsRunnableArray.Clear();
+    sBondingRunnableArray.Clear();
+    sUnbondingRunnableArray.Clear();
 
-  if (sAdapterEnabled &&
-      NS_FAILED(NS_DispatchToMainThread(new SetupAfterEnabledTask()))) {
-    BT_WARNING("Failed to dispatch to main thread!");
-    return;
+    // Bluetooth scan mode is SCAN_MODE_CONNECTABLE by default, i.e., It should
+    // be connectable and non-discoverable.
+    NS_ENSURE_TRUE_VOID(sBtInterface);
+    sBtInterface->SetAdapterProperty(
+      BluetoothNamedValue(NS_ConvertUTF8toUTF16("Discoverable"), false),
+      new SetAdapterPropertyDiscoverableResultHandler());
+
+    // Trigger BluetoothOppManager to listen
+    BluetoothOppManager* opp = BluetoothOppManager::Get();
+    if (!opp || !opp->Listen()) {
+      BT_LOGR("Fail to start BluetoothOppManager listening");
+    }
   }
 
   // Resolve promise if existed
   if (!sChangeAdapterStateRunnableArray.IsEmpty()) {
     DispatchBluetoothReply(sChangeAdapterStateRunnableArray[0],
                            BluetoothValue(true), EmptyString());
 
     sChangeAdapterStateRunnableArray.RemoveElementAt(0);
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
@@ -11,16 +11,32 @@
 #include "BluetoothInterface.h"
 #include "BluetoothService.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothServiceBluedroid : public BluetoothService
                                 , public BluetoothNotificationHandler
 {
+  class CancelDiscoveryResultHandler;
+  class CreateBondResultHandler;
+  class DisableResultHandler;
+  class EnableResultHandler;
+  class GetRemoteDevicePropertiesResultHandler;
+  class GetRemoteServicesResultHandler;
+  class InitResultHandler;
+  class PinReplyResultHandler;
+  class ProfileDeinitResultHandler;
+  class ProfileInitResultHandler;
+  class RemoveBondResultHandler;
+  class SetAdapterPropertyDiscoverableResultHandler;
+  class SetAdapterPropertyResultHandler;
+  class SspReplyResultHandler;
+  class StartDiscoveryResultHandler;
+
 public:
   BluetoothServiceBluedroid();
   ~BluetoothServiceBluedroid();
 
   virtual nsresult StartInternal(BluetoothReplyRunnable* aRunnable);
   virtual nsresult StopInternal(BluetoothReplyRunnable* aRunnable);
 
   virtual nsresult
@@ -188,14 +204,30 @@ public:
                                            const nsAString& aRemoteBdAddr,
                                            bool aState) MOZ_OVERRIDE;
 
   virtual void DutModeRecvNotification(uint16_t aOpcode,
                                        const uint8_t* aBuf,
                                        uint8_t aLen) MOZ_OVERRIDE;
   virtual void LeTestModeNotification(BluetoothStatus aStatus,
                                       uint16_t aNumPackets) MOZ_OVERRIDE;
+
+protected:
+  static nsresult StartGonkBluetooth();
+  static nsresult StopGonkBluetooth();
+  static bool EnsureBluetoothHalLoad();
+
+  static void ConnectDisconnect(bool aConnect,
+                                const nsAString& aDeviceAddress,
+                                BluetoothReplyRunnable* aRunnable,
+                                uint16_t aServiceUuid, uint32_t aCod = 0);
+  static void NextBluetoothProfileController();
+  static ControlPlayStatus PlayStatusStringToControlPlayStatus(
+    const nsAString& aPlayStatus);
+  static void ReplyStatusError(BluetoothReplyRunnable* aReplyRunnable,
+                               BluetoothStatus aStatusCode,
+                               const nsAString& aCustomMsg);
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
 
--- a/dom/bluetooth2/moz.build
+++ b/dom/bluetooth2/moz.build
@@ -46,16 +46,17 @@ if CONFIG['MOZ_B2G_BT']:
                 'bluez',
             ]
             DEFINES['MOZ_B2G_BT_BLUEZ'] = True
         elif CONFIG['MOZ_B2G_BT_BLUEDROID']:
             SOURCES += [
                 'bluedroid/BluetoothA2dpHALInterface.cpp',
                 'bluedroid/BluetoothA2dpManager.cpp',
                 'bluedroid/BluetoothAvrcpHALInterface.cpp',
+                'bluedroid/BluetoothDaemonHandsfreeInterface.cpp',
                 'bluedroid/BluetoothDaemonHelpers.cpp',
                 'bluedroid/BluetoothDaemonInterface.cpp',
                 'bluedroid/BluetoothDaemonSetupInterface.cpp',
                 'bluedroid/BluetoothDaemonSocketInterface.cpp',
                 'bluedroid/BluetoothGattHALInterface.cpp',
                 'bluedroid/BluetoothGattManager.cpp',
                 'bluedroid/BluetoothHALHelpers.cpp',
                 'bluedroid/BluetoothHALInterface.cpp',
--- a/dom/media/omx/OmxDecoder.cpp
+++ b/dom/media/omx/OmxDecoder.cpp
@@ -569,42 +569,44 @@ bool OmxDecoder::ReadVideo(VideoFrame *a
     } else {
       seekMode = MediaSource::ReadOptions::SEEK_NEXT_SYNC;
     }
 
     bool findNextBuffer = true;
     while (findNextBuffer) {
       options.setSeekTo(aTimeUs, seekMode);
       findNextBuffer = false;
-      err = mVideoSource->read(&mVideoBuffer, &options);
-      {
+      if (mIsVideoSeeking) {
+        err = mVideoSource->read(&mVideoBuffer, &options);
         Mutex::Autolock autoLock(mSeekLock);
         mIsVideoSeeking = false;
         PostReleaseVideoBuffer(nullptr, FenceHandle());
       }
+      else {
+	err = mVideoSource->read(&mVideoBuffer);
+      }
+
       // If there is no next Keyframe, jump to the previous key frame.
       if (err == ERROR_END_OF_STREAM && seekMode == MediaSource::ReadOptions::SEEK_NEXT_SYNC) {
         seekMode = MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
         findNextBuffer = true;
         {
           Mutex::Autolock autoLock(mSeekLock);
           mIsVideoSeeking = true;
         }
         continue;
       } else if (err != OK) {
         ALOG("Unexpected error when seeking to %lld", aTimeUs);
         break;
       }
+      // For some codecs, the length of first decoded frame after seek is 0.
+      // Need to ignore it and continue to find the next one
       if (mVideoBuffer->range_length() == 0) {
         ReleaseVideoBuffer();
         findNextBuffer = true;
-        {
-          Mutex::Autolock autoLock(mSeekLock);
-          mIsVideoSeeking = true;
-        }
       }
     }
     aDoSeek = false;
   } else {
     err = mVideoSource->read(&mVideoBuffer);
   }
 
   aFrame->mSize = 0;
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -992,17 +992,17 @@ NetworkManager.prototype = {
     }
 #endif
 
     if (this.active) {
       this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = this.active.name;
     } else {
       let mobile = this.getNetworkInterface(
         Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
-      if (mobile) {
+      if (mobile && mobile.name) {
         this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = mobile.name;
       }
     }
     gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
   },
 
   getUSBTetheringParameters: function(enable, tetheringinterface) {
     let interfaceIp;
@@ -1137,17 +1137,17 @@ NetworkManager.prototype = {
       }.bind(this, config, callback));
       return;
     }
 #endif
 
     let mobile = this.getNetworkInterface(
       Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
     // Update the real interface name
-    if (mobile) {
+    if (mobile && mobile.name) {
       this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = mobile.name;
     }
 
     this.enableWifiTethering(true, config, callback);
   },
 
   // Enable/disable USB tethering by sending commands to netd.
   setUSBTethering: function(enable, tetheringInterface, callback) {
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2330,17 +2330,21 @@ bool AsyncPanZoomController::SnapBackIfO
     StartOverscrollAnimation(ParentLayerPoint(0, 0));
     return true;
   }
   return false;
 }
 
 bool AsyncPanZoomController::IsMovingFast() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
-  return (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold());
+  if (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
+    APZC_LOG("%p is moving fast\n", this);
+    return true;
+  }
+  return false;
 }
 
 bool AsyncPanZoomController::IsPannable() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
   return mX.CanScroll() || mY.CanScroll();
 }
 
 int32_t AsyncPanZoomController::GetLastTouchIdentifier() const {
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -78,26 +78,29 @@ void Axis::UpdateWithTouchAtDevicePoint(
     if (gfxPrefs::APZCurveThreshold() > 0.0f && gfxPrefs::APZCurveThreshold() < gfxPrefs::APZMaxVelocity()) {
       float curveThreshold = ToLocalVelocity(gfxPrefs::APZCurveThreshold());
       if (newVelocity > curveThreshold) {
         // here, 0 < curveThreshold < newVelocity <= maxVelocity, so we apply the curve
         float scale = maxVelocity - curveThreshold;
         float funcInput = (newVelocity - curveThreshold) / scale;
         float funcOutput = gVelocityCurveFunction->GetValue(funcInput);
         float curvedVelocity = (funcOutput * scale) + curveThreshold;
-        AXIS_LOG("Curving up velocity from %f to %f\n", newVelocity, curvedVelocity);
+        AXIS_LOG("%p|%s curving up velocity from %f to %f\n",
+          mAsyncPanZoomController, Name(), newVelocity, curvedVelocity);
         newVelocity = curvedVelocity;
       }
     }
 
     if (velocityIsNegative) {
       newVelocity = -newVelocity;
     }
   }
 
+  AXIS_LOG("%p|%s updating velocity to %f with touch\n",
+    mAsyncPanZoomController, Name(), newVelocity);
   mVelocity = newVelocity;
   mPos = aPos;
   mPosTimeMs = aTimestampMs;
 
   // Limit queue size pased on pref
   mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, mVelocity));
   if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
     mVelocityQueue.RemoveElementAt(0);
@@ -134,16 +137,18 @@ bool Axis::AdjustDisplacement(ParentLaye
   displacement += consumedOverscroll;
 
   // Split the requested displacement into an allowed displacement that does
   // not overscroll, and an overscroll amount.
   aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement);
   if (aOverscrollAmountOut != 0.0f) {
     // No need to have a velocity along this axis anymore; it won't take us
     // anywhere, so we're just spinning needlessly.
+    AXIS_LOG("%p|%s has overscrolled, clearing velocity\n",
+      mAsyncPanZoomController, Name());
     mVelocity = 0.0f;
     displacement -= aOverscrollAmountOut;
   }
   aDisplacementOut = displacement;
   return fabsf(consumedOverscroll) > EPSILON;
 }
 
 ParentLayerCoord Axis::ApplyResistance(ParentLayerCoord aRequestedOverscroll) const {
@@ -212,16 +217,18 @@ bool Axis::SampleOverscrollAnimation(con
 
   // Apply spring force.
   float springForce = -1 * kSpringStiffness * mOverscroll;
   // Assume unit mass, so force = acceleration.
   mVelocity += springForce * aDelta.ToMilliseconds();
 
   // Apply dampening.
   mVelocity *= pow(double(1 - kSpringFriction), aDelta.ToMilliseconds());
+  AXIS_LOG("%p|%s sampled overscroll animation, leaving velocity at %f\n",
+    mAsyncPanZoomController, Name(), mVelocity);
 
   // Adjust the amount of overscroll based on the velocity.
   // Note that we allow for oscillations. mInUnderscroll tracks whether
   // we are currently in a state where we have overshot and the spring is
   // displaced in the other direction.
   float oldOverscroll = mOverscroll;
   mOverscroll += (mVelocity * aDelta.ToMilliseconds());
   bool signChange = (oldOverscroll * mOverscroll) < 0;
@@ -233,16 +240,18 @@ bool Axis::SampleOverscrollAnimation(con
 
   // If both the velocity and the displacement fall below a threshold, stop
   // the animation so we don't continue doing tiny oscillations that aren't
   // noticeable.
   if (fabs(mOverscroll) < gfxPrefs::APZOverscrollStopDistanceThreshold() &&
       fabs(mVelocity) < gfxPrefs::APZOverscrollStopVelocityThreshold()) {
     // "Jump" to the at-rest state. The jump shouldn't be noticeable as the
     // velocity and overscroll are already low.
+    AXIS_LOG("%p|%s oscillation dropped below threshold, going to rest\n",
+      mAsyncPanZoomController, Name());
     mOverscroll = 0;
     mVelocity = 0;
     mInUnderscroll = false;
     return false;
   }
 
   // Otherwise, continue the animation.
   return true;
@@ -280,22 +289,26 @@ void Axis::EndTouch(uint32_t aTimestampM
       count++;
       mVelocity += mVelocityQueue[0].second;
     }
     mVelocityQueue.RemoveElementAt(0);
   }
   if (count > 1) {
     mVelocity /= count;
   }
+  AXIS_LOG("%p|%s ending touch, computed velocity %f\n",
+    mAsyncPanZoomController, Name(), mVelocity);
 }
 
 void Axis::CancelTouch() {
   // mVelocityQueue is controller-thread only
   AsyncPanZoomController::AssertOnControllerThread();
 
+  AXIS_LOG("%p|%s cancelling touch, clearing velocity queue\n",
+    mAsyncPanZoomController, Name());
   mVelocity = 0.0f;
   while (!mVelocityQueue.IsEmpty()) {
     mVelocityQueue.RemoveElementAt(0);
   }
 }
 
 bool Axis::CanScroll() const {
   return GetPageLength() - GetCompositionLength() > COORDINATE_EPSILON;
@@ -312,16 +325,18 @@ bool Axis::FlingApplyFrictionOrCancel(co
     // If the velocity is very low, just set it to 0 and stop the fling,
     // otherwise we'll just asymptotically approach 0 and the user won't
     // actually see any changes.
     mVelocity = 0.0f;
     return false;
   } else {
     mVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds()));
   }
+  AXIS_LOG("%p|%s reduced velocity to %f due to friction\n",
+    mAsyncPanZoomController, Name(), mVelocity);
   return true;
 }
 
 ParentLayerCoord Axis::DisplacementWillOverscrollAmount(ParentLayerCoord aDisplacement) const {
   ParentLayerCoord newOrigin = GetOrigin() + aDisplacement;
   ParentLayerCoord newCompositionEnd = GetCompositionEnd() + aDisplacement;
   // If the current pan plus a displacement takes the window to the left of or
   // above the current page rect.
@@ -368,16 +383,18 @@ CSSCoord Axis::ScaleWillOverscrollAmount
   return 0;
 }
 
 float Axis::GetVelocity() const {
   return mAxisLocked ? 0 : mVelocity;
 }
 
 void Axis::SetVelocity(float aVelocity) {
+  AXIS_LOG("%p|%s direct-setting velocity to %f\n",
+    mAsyncPanZoomController, Name(), aVelocity);
   mVelocity = aVelocity;
 }
 
 ParentLayerCoord Axis::GetCompositionEnd() const {
   return GetOrigin() + GetCompositionLength();
 }
 
 ParentLayerCoord Axis::GetPageEnd() const {
@@ -436,16 +453,21 @@ ParentLayerCoord AxisX::GetRectOffset(co
   return aRect.x;
 }
 
 ScreenPoint AxisX::MakePoint(ScreenCoord aCoord) const
 {
   return ScreenPoint(aCoord, 0);
 }
 
+const char* AxisX::Name() const
+{
+  return "X";
+}
+
 AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
   : Axis(aAsyncPanZoomController)
 {
 
 }
 
 ParentLayerCoord AxisY::GetPointOffset(const ParentLayerPoint& aPoint) const
 {
@@ -462,10 +484,15 @@ ParentLayerCoord AxisY::GetRectOffset(co
   return aRect.y;
 }
 
 ScreenPoint AxisY::MakePoint(ScreenCoord aCoord) const
 {
   return ScreenPoint(0, aCoord);
 }
 
+const char* AxisY::Name() const
+{
+  return "Y";
+}
+
 }
 }
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -233,16 +233,18 @@ public:
   ParentLayerCoord GetPos() const { return mPos; }
 
   virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const = 0;
   virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const = 0;
   virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const = 0;
 
   virtual ScreenPoint MakePoint(ScreenCoord aCoord) const = 0;
 
+  virtual const char* Name() const = 0;
+
 protected:
   ParentLayerCoord mPos;
   uint32_t mPosTimeMs;
   ParentLayerCoord mStartPos;
   float mVelocity;      // Units: ParentLayerCoords per millisecond
   bool mAxisLocked;     // Whether movement on this axis is locked.
   AsyncPanZoomController* mAsyncPanZoomController;
   ParentLayerCoord mOverscroll;  // See GetOverscroll().
@@ -265,23 +267,25 @@ protected:
 
 class AxisX : public Axis {
 public:
   explicit AxisX(AsyncPanZoomController* mAsyncPanZoomController);
   virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const MOZ_OVERRIDE;
   virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const MOZ_OVERRIDE;
   virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const MOZ_OVERRIDE;
   virtual ScreenPoint MakePoint(ScreenCoord aCoord) const MOZ_OVERRIDE;
+  virtual const char* Name() const MOZ_OVERRIDE;
 };
 
 class AxisY : public Axis {
 public:
   explicit AxisY(AsyncPanZoomController* mAsyncPanZoomController);
   virtual ParentLayerCoord GetPointOffset(const ParentLayerPoint& aPoint) const MOZ_OVERRIDE;
   virtual ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const MOZ_OVERRIDE;
   virtual ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const MOZ_OVERRIDE;
   virtual ScreenPoint MakePoint(ScreenCoord aCoord) const MOZ_OVERRIDE;
+  virtual const char* Name() const MOZ_OVERRIDE;
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -32,16 +32,17 @@ InputBlockState::InputBlockState(const n
 bool
 InputBlockState::SetConfirmedTargetApzc(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
 {
   if (mTargetConfirmed) {
     return false;
   }
   mTargetConfirmed = true;
 
+  TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc.get());
   if (mTargetApzc == aTargetApzc) {
     // The confirmed target is the same as the tentative one, so we're done.
     return true;
   }
 
   // Log enabled by default for now, we will put it in a TBS_LOG eventually
   // once this code is more baked
   printf_stderr("%p replacing unconfirmed target %p with real target %p\n",
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -55,25 +55,27 @@ InputQueue::ReceiveInputEvent(const nsRe
     if (block == CurrentTouchBlock()) {
       // XXX using the chain from |block| here may be wrong in cases where the
       // target isn't confirmed and the real target turns out to be something
       // else. For now assume this is rare enough that it's not an issue.
       if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
         // If we're already in a fast fling, then we want the touch event to stop the fling
         // and to disallow the touch event from being used as part of a fling.
         block->SetDuringFastMotion();
+        INPQ_LOG("block %p tagged as fast-motion\n", block);
       }
       block->GetOverscrollHandoffChain()->CancelAnimations();
     }
 
     bool waitForMainThread = !aTargetConfirmed;
     if (!gfxPrefs::LayoutEventRegionsEnabled()) {
       waitForMainThread |= aTarget->NeedToWaitForContent();
     }
     if (block->IsDuringFastMotion()) {
+      block->SetConfirmedTargetApzc(aTarget);
       waitForMainThread = false;
     }
     if (waitForMainThread) {
       // We either don't know for sure if aTarget is the right APZC, or we may
       // need to wait to give content the opportunity to prevent-default the
       // touch events. Either way we schedule a timeout so the main thread stuff
       // can run.
       ScheduleMainThreadTimeout(aTarget, block->GetBlockId());
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1800,18 +1800,18 @@ nsLayoutUtils::GetNearestScrollableFrame
       ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles();
       if ((aFlags & SCROLLABLE_INCLUDE_HIDDEN) ||
           ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN ||
           ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN)
         return scrollableFrame;
     }
     if (aFlags & SCROLLABLE_ALWAYS_MATCH_ROOT) {
       nsPresContext* pc = f->PresContext();
-      if (pc->IsRootContentDocument() && pc->PresShell()->GetRootScrollFrame() == f) {
-        return scrollableFrame;
+      if (pc->IsRootContentDocument() && pc->PresShell()->GetRootFrame() == f) {
+        return pc->PresShell()->GetRootScrollFrameAsScrollable();
       }
     }
   }
   return nullptr;
 }
 
 // static
 nsRect
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -581,17 +581,18 @@ public:
     /**
      * If the SCROLLABLE_ONLY_ASYNC_SCROLLABLE flag is set, then we only
      * want to match scrollable frames for which WantAsyncScroll() returns
      * true.
      */
     SCROLLABLE_ONLY_ASYNC_SCROLLABLE = 0x04,
     /**
      * If the SCROLLABLE_ALWAYS_MATCH_ROOT flag is set, then return the
-     * root scrollable frame for the root content document if we hit it.
+     * root scrollable frame for the root content document if we don't hit
+     * anything else.
      */
     SCROLLABLE_ALWAYS_MATCH_ROOT = 0x08,
   };
   /**
    * GetNearestScrollableFrame locates the first ancestor of aFrame
    * (or aFrame itself) that is scrollable with overflow:scroll or
    * overflow:auto in some direction.
    *
--- a/mobile/android/base/resources/layout-v11/new_tablet_tabs_item_cell.xml
+++ b/mobile/android/base/resources/layout-v11/new_tablet_tabs_item_cell.xml
@@ -6,18 +6,17 @@
 <org.mozilla.gecko.tabs.TabsLayoutItemView xmlns:android="http://schemas.android.com/apk/res/android"
                                            xmlns:gecko="http://schemas.android.com/apk/res-auto"
                                            style="@style/TabsItem"
                                            android:focusable="true"
                                            android:id="@+id/info"
                                            android:layout_width="wrap_content"
                                            android:layout_height="wrap_content"
                                            android:gravity="center"
-                                           android:orientation="vertical"
-                                           android:paddingBottom="@dimen/new_tablet_tab_panel_grid_padding">
+                                           android:orientation="vertical">
 
     <LinearLayout android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                   android:orientation="horizontal"
                   android:duplicateParentState="true"
                   android:paddingLeft="@dimen/new_tablet_tab_highlight_stroke_width"
                   android:paddingRight="@dimen/new_tablet_tab_highlight_stroke_width"
                   android:paddingBottom="@dimen/new_tablet_tab_highlight_stroke_width">
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/values-xlarge-land-v11/dimens.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<resources>
+
+    <dimen name="new_tablet_tab_panel_grid_padding">64dp</dimen>
+
+</resources>
--- a/mobile/android/base/resources/values-xlarge-v11/dimens.xml
+++ b/mobile/android/base/resources/values-xlarge-v11/dimens.xml
@@ -7,10 +7,11 @@
 
     <dimen name="browser_toolbar_height">56dp</dimen>
     <dimen name="remote_tab_child_row_height">56dp</dimen>
     <dimen name="remote_tab_group_row_height">34dp</dimen>
     <dimen name="tabs_counter_size">26sp</dimen>
     <dimen name="tabs_panel_indicator_width">60dp</dimen>
     <dimen name="tabs_panel_list_padding">8dip</dimen>
     <dimen name="panel_grid_view_column_width">250dp</dimen>
+    <dimen name="new_tablet_tab_panel_grid_padding">48dp</dimen>
 
 </resources>
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -197,17 +197,17 @@
     <style name="Widget.TabsGridLayout" parent="Widget.GridView">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:paddingTop">0dp</item>
         <item name="android:stretchMode">columnWidth</item>
         <item name="android:numColumns">auto_fit</item>
         <item name="android:columnWidth">@dimen/tabs_grid_view_column_width</item>
         <item name="android:horizontalSpacing">2dp</item>
-        <item name="android:verticalSpacing">2dp</item>
+        <item name="android:verticalSpacing">21dp</item>
         <item name="android:drawSelectorOnTop">true</item>
         <item name="android:clipToPadding">false</item>
     </style>
 
     <style name="Widget.BookmarkItemView" parent="Widget.TwoLinePageRow"/>
 
     <style name="Widget.BookmarksListView" parent="Widget.HomeListView"/>
 
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -7,16 +7,17 @@
 const {Cu, Cc, Ci} = require("chrome");
 const Services = require("Services");
 const protocol = require("devtools/server/protocol");
 const {Arg, Option, method} = protocol;
 const events = require("sdk/event/core");
 const Heritage = require("sdk/core/heritage");
 const {CssLogic} = require("devtools/styleinspector/css-logic");
 const EventEmitter = require("devtools/toolkit/event-emitter");
+const {setIgnoreLayoutChanges} = require("devtools/server/actors/layout");
 
 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 // FIXME: add ":visited" and ":link" after bug 713106 is fixed
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
 const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
 const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"];
@@ -979,36 +980,44 @@ BoxModelHighlighter.prototype = Heritage
   },
 
   /**
    * Update the highlighter on the current highlighted node (the one that was
    * passed as an argument to show(node)).
    * Should be called whenever node size or attributes change
    */
   _update: function() {
+    setIgnoreLayoutChanges(true);
+
     if (this._updateBoxModel()) {
       if (!this.options.hideInfoBar) {
         this._showInfobar();
       } else {
         this._hideInfobar();
       }
       this._showBoxModel();
     } else {
       // Nothing to highlight (0px rectangle like a <script> tag for instance)
       this._hide();
     }
+
+    setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   /**
    * Hide the highlighter, the outline and the infobar.
    */
   _hide: function() {
+    setIgnoreLayoutChanges(true);
+
     this._untrackMutations();
     this._hideBoxModel();
     this._hideInfobar();
+
+    setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   /**
    * Hide the infobar
    */
   _hideInfobar: function() {
     this.markup.setAttributeForElement(
       this.ID_CLASS_PREFIX + "nodeinfobar-container", "hidden", "true");
@@ -1244,17 +1253,17 @@ BoxModelHighlighter.prototype = Heritage
     let pseudos = PSEUDO_CLASSES.filter(pseudo => {
       return DOMUtils.hasPseudoClassLock(node, pseudo);
     }, this).join("");
     if (pseudo) {
       // Display :after as ::after
       pseudos += ":" + pseudo;
     }
 
-    let rect = node.getBoundingClientRect();
+    let rect = this.currentQuads.border.bounds;
     let dim = Math.ceil(rect.width) + " x " + Math.ceil(rect.height);
 
     let elementId = this.ID_CLASS_PREFIX + "nodeinfobar-";
     this.markup.setTextContentForElement(elementId + "tagname", tagName);
     this.markup.setTextContentForElement(elementId + "id", id);
     this.markup.setTextContentForElement(elementId + "classes", classList);
     this.markup.setTextContentForElement(elementId + "pseudo-classes", pseudos);
     this.markup.setTextContentForElement(elementId + "dimensions", dim);
@@ -1486,16 +1495,18 @@ CssTransformHighlighter.prototype = Heri
   },
 
   /**
    * Update the highlighter on the current highlighted node (the one that was
    * passed as an argument to show(node)).
    * Should be called whenever node size or attributes change
    */
   _update: function() {
+    setIgnoreLayoutChanges(true);
+
     // Getting the points for the transformed shape
     let quad = this.currentQuads.border;
     if (!quad || quad.bounds.width <= 0 || quad.bounds.height <= 0) {
       this._hideShapes();
       return null;
     }
 
     // Getting the points for the untransformed shape
@@ -1506,23 +1517,27 @@ CssTransformHighlighter.prototype = Heri
     for (let nb of ["1", "2", "3", "4"]) {
       this._setLinePoints(untransformedQuad["p" + nb], quad["p" + nb], "line" + nb);
     }
 
     // Adapt to the current zoom
     this.markup.scaleRootElement(this.currentNode, this.ID_CLASS_PREFIX + "root");
 
     this._showShapes();
+
+    setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   /**
    * Hide the highlighter, the outline and the infobar.
    */
   _hide: function() {
+    setIgnoreLayoutChanges(true);
     this._hideShapes();
+    setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   _hideShapes: function() {
     this.markup.setAttributeForElement(this.ID_CLASS_PREFIX + "elements",
       "hidden", "true");
   },
 
   _showShapes: function() {
--- a/toolkit/devtools/server/actors/layout.js
+++ b/toolkit/devtools/server/actors/layout.js
@@ -188,16 +188,38 @@ Observable.prototype = {
   destroy: function() {
     this.stop();
     this.callback = null;
     this.tabActor = null;
   }
 };
 
 /**
+ * The LayouChangesObserver will observe reflows as soon as it is started.
+ * Some devtools actors may cause reflows and it may be wanted to "hide" these
+ * reflows from the LayouChangesObserver consumers.
+ * If this is the case, such actors should require this module and use this
+ * global function to turn the ignore mode on and off temporarily.
+ *
+ * Note that if a node is provided, it will be used to force a sync reflow to
+ * make sure all reflows which occurred before switching the mode on or off are
+ * either observed or ignored depending on the current mode.
+ *
+ * @param {Boolean} ignore
+ * @param {DOMNode} syncReflowNode The node to use to force a sync reflow
+ */
+let gIgnoreLayoutChanges = false;
+exports.setIgnoreLayoutChanges = function(ignore, syncReflowNode) {
+  if (syncReflowNode) {
+    let forceSyncReflow = syncReflowNode.offsetWidth;
+  }
+  gIgnoreLayoutChanges = ignore;
+}
+
+/**
  * The LayoutChangesObserver class is instantiated only once per given tab
  * and is used to track reflows and dom and style changes in that tab.
  * The LayoutActor uses this class to send reflow events to its clients.
  *
  * This class isn't exported on the module because it shouldn't be instantiated
  * to avoid creating several instances per tabs.
  * Use `getLayoutChangesObserver(tabActor)`
  * and `releaseLayoutChangesObserver(tabActor)`
@@ -297,16 +319,20 @@ LayoutChangesObserver.prototype = Herita
    * Executed whenever a reflow is observed. Only stacks the reflow in the
    * reflows array.
    * The EVENT_BATCHING_DELAY loop will take care of it later.
    * @param {Number} start When the reflow started
    * @param {Number} end When the reflow ended
    * @param {Boolean} isInterruptible
    */
   _onReflow: function(start, end, isInterruptible) {
+    if (gIgnoreLayoutChanges) {
+      return;
+    }
+
     // XXX: when/if bug 997092 gets fixed, we will be able to know which
     // elements have been reflowed, which would be a nice thing to add here.
     this.reflows.push({
       start: start,
       end: end,
       isInterruptible: isInterruptible
     });
   }