Merge b-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 28 Nov 2014 15:08:30 -0800
changeset 243902 e90536aa55ddeb2452308b0d6b275fb3ead1fe5b
parent 243860 6904bf61707dfc6af314bee2ef20941fd3f7ab41 (current diff)
parent 243901 ddf04d4ae5ff672516efd474141334eee270a5c7 (diff)
child 243903 8daa8f3b0276173a0c4d260a894f72fe8e52cabd
child 243910 8a0c83fe66d18b738c5c822c47eed723201d708d
child 243923 6dededc47e3d31c6707f56303dedc87039356d9e
child 243928 3477ca12357a4b8af68d3958cc497491dedfbb72
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)
reviewersmerge
milestone37.0a1
first release with
nightly linux32
e90536aa55dd / 37.0a1 / 20141129030207 / files
nightly linux64
e90536aa55dd / 37.0a1 / 20141129030207 / files
nightly mac
e90536aa55dd / 37.0a1 / 20141129030207 / files
nightly win32
e90536aa55dd / 37.0a1 / 20141129030207 / files
nightly win64
e90536aa55dd / 37.0a1 / 20141129030207 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge b-i to m-c, a=merge
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"/>
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.
    *