Merge b2g-inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 16 Jul 2014 17:22:55 -0700
changeset 215285 a74600665875202a1990416598444deb807d3a5f
parent 215221 8e8f3ba646554c13de1f26140d8a954b79d7549b (current diff)
parent 215284 a0873e56568fe86c562fda2ba4d7ad011383c00d (diff)
child 215286 c2c93cba158730458663c5816df14f03cc24a72a
child 215328 5dbfea1a872395ff328c5b9df17c1eef931b71a5
child 215397 ec7f2245280cc062085966e138f67ba4342e2b7b
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
a74600665875 / 33.0a1 / 20140717030339 / files
nightly linux64
a74600665875 / 33.0a1 / 20140717030339 / files
nightly mac
a74600665875 / 33.0a1 / 20140717030339 / files
nightly win32
a74600665875 / 33.0a1 / 20140717030339 / files
nightly win64
a74600665875 / 33.0a1 / 20140717030339 / 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 b2g-inbound to m-c a=merge
b2g/app/b2g.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1008,12 +1008,12 @@ pref("touchcaret.enabled", false);
 // Disable selection caret by default
 pref("selectioncaret.enabled", false);
 
 // Enable sync and mozId with Firefox Accounts.
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 
 // Mobile Identity API.
-pref("services.mobileid.server.uri", "https://msisdn-dev.stage.mozaws.net");
+pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
 
 // Enable mapped array buffer
 pref("dom.mapped_arraybuffer.enabled", true);
--- 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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
   <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="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <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="777194d772c831b5dab40cf919523d5665f2a46c"/>
--- 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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- 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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
   <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": "8cd6c73ef83257a569d148e246108b2c161127bb", 
+    "revision": "6739781fb8d0f3ae8bff65d1093e74d9f21ed6e5", 
     "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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <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="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- 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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <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="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
   <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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <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="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/dom/bluetooth2/bluedroid/BluetoothInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothInterface.cpp
@@ -1,17 +1,22 @@
 /* -*- 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 <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "base/message_loop.h"
 #include "BluetoothInterface.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 template<class T>
 struct interface_traits
 { };
 
 //
@@ -64,48 +69,491 @@ public:
   }
 
 private:
   nsRefPtr<Obj> mObj;
   void (Obj::*mMethod)(Arg1);
   Arg1 mArg1;
 };
 
+template <typename Obj, typename Res,
+          typename Arg1, typename Arg2, typename Arg3>
+class BluetoothInterfaceRunnable3 : public nsRunnable
+{
+public:
+  BluetoothInterfaceRunnable3(Obj* aObj,
+                              Res (Obj::*aMethod)(Arg1, Arg2, Arg3),
+                              const Arg1& aArg1, const Arg2& aArg2,
+                              const Arg3& aArg3)
+  : mObj(aObj)
+  , mMethod(aMethod)
+  , mArg1(aArg1)
+  , mArg2(aArg2)
+  , mArg3(aArg3)
+  {
+    MOZ_ASSERT(mObj);
+    MOZ_ASSERT(mMethod);
+  }
+
+  NS_METHOD
+  Run() MOZ_OVERRIDE
+  {
+    ((*mObj).*mMethod)(mArg1, mArg2, mArg3);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<Obj> mObj;
+  void (Obj::*mMethod)(Arg1, Arg2, Arg3);
+  Arg1 mArg1;
+  Arg2 mArg2;
+  Arg3 mArg3;
+};
+
 //
 // Socket Interface
 //
 
 template<>
 struct interface_traits<BluetoothSocketInterface>
 {
   typedef const btsock_interface_t const_interface_type;
 
   static const char* profile_id()
   {
     return BT_PROFILE_SOCKETS_ID;
   }
 };
 
-bt_status_t
+typedef
+  BluetoothInterfaceRunnable1<BluetoothSocketResultHandler, void, int>
+  BluetoothSocketIntResultRunnable;
+
+typedef
+  BluetoothInterfaceRunnable3<BluetoothSocketResultHandler,
+                              void, int, const nsAString_internal&, int>
+  BluetoothSocketIntStringIntResultRunnable;
+
+typedef
+  BluetoothInterfaceRunnable1<BluetoothSocketResultHandler, void, bt_status_t>
+  BluetoothSocketErrorRunnable;
+
+static nsresult
+DispatchBluetoothSocketResult(BluetoothSocketResultHandler* aRes,
+                              void (BluetoothSocketResultHandler::*aMethod)(int),
+                              int aArg, bt_status_t aStatus)
+{
+  MOZ_ASSERT(aRes);
+
+  nsRunnable* runnable;
+
+  if (aStatus == BT_STATUS_SUCCESS) {
+    runnable = new BluetoothSocketIntResultRunnable(aRes, aMethod, aArg);
+  } else {
+    runnable = new BluetoothSocketErrorRunnable(aRes,
+      &BluetoothSocketResultHandler::OnError, aStatus);
+  }
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  if (NS_FAILED(rv)) {
+    BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
+  }
+  return rv;
+}
+
+static nsresult
+DispatchBluetoothSocketResult(
+  BluetoothSocketResultHandler* aRes,
+  void (BluetoothSocketResultHandler::*aMethod)(int, const nsAString&, int),
+  int aArg1, const nsAString& aArg2, int aArg3, bt_status_t aStatus)
+{
+  MOZ_ASSERT(aRes);
+
+  nsRunnable* runnable;
+
+  if (aStatus == BT_STATUS_SUCCESS) {
+    runnable = new BluetoothSocketIntStringIntResultRunnable(aRes, aMethod,
+                                                             aArg1, aArg2,
+                                                             aArg3);
+  } else {
+    runnable = new BluetoothSocketErrorRunnable(aRes,
+      &BluetoothSocketResultHandler::OnError, aStatus);
+  }
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  if (NS_FAILED(rv)) {
+    BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
+  }
+  return rv;
+}
+
+void
 BluetoothSocketInterface::Listen(btsock_type_t aType,
                                  const char* aServiceName,
-                                 const uint8_t* aServiceUuid, int aChannel,
-                                 int& aSockFd, int aFlags)
+                                 const uint8_t* aServiceUuid,
+                                 int aChannel, int aFlags,
+                                 BluetoothSocketResultHandler* aRes)
 {
-  return mInterface->listen(aType, aServiceName, aServiceUuid, aChannel,
-                           &aSockFd, aFlags);
+  int fd;
+
+  bt_status_t status = mInterface->listen(aType, aServiceName, aServiceUuid,
+                                          aChannel, &fd, aFlags);
+  if (aRes) {
+    DispatchBluetoothSocketResult(aRes, &BluetoothSocketResultHandler::Listen,
+                                  fd, status);
+  }
 }
 
-bt_status_t
+#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
+    ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
+      ((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
+
+/* |SocketMessageWatcher| receives Bluedroid's socket setup
+ * messages on the I/O thread. You need to inherit from this
+ * class to make use of it.
+ *
+ * Bluedroid sends two socket info messages (20 bytes) at
+ * the beginning of a connection to both peers.
+ *
+ *   - 1st message: [channel:4]
+ *   - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
+ *
+ * On the server side, the second message will contain a
+ * socket file descriptor for the connection. The client
+ * uses the original file descriptor.
+ */
+class SocketMessageWatcher : public MessageLoopForIO::Watcher
+{
+public:
+  static const unsigned char MSG1_SIZE = 4;
+  static const unsigned char MSG2_SIZE = 16;
+
+  static const unsigned char OFF_CHANNEL1 = 0;
+  static const unsigned char OFF_SIZE = 4;
+  static const unsigned char OFF_BDADDRESS = 6;
+  static const unsigned char OFF_CHANNEL2 = 12;
+  static const unsigned char OFF_STATUS = 16;
+
+  SocketMessageWatcher(int aFd)
+  : mFd(aFd)
+  , mClientFd(-1)
+  , mLen(0)
+  { }
+
+  virtual ~SocketMessageWatcher()
+  { }
+
+  virtual void Proceed(bt_status_t aStatus) = 0;
+
+  void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE
+  {
+    bt_status_t status;
+
+    switch (mLen) {
+      case 0:
+        status = RecvMsg1();
+        break;
+      case MSG1_SIZE:
+        status = RecvMsg2();
+        break;
+      default:
+        /* message-size error */
+        status = BT_STATUS_FAIL;
+        break;
+    }
+
+    if (IsComplete() || status != BT_STATUS_SUCCESS) {
+      mWatcher.StopWatchingFileDescriptor();
+      Proceed(status);
+    }
+  }
+
+  void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE
+  { }
+
+  void Watch()
+  {
+    MessageLoopForIO::current()->WatchFileDescriptor(
+      mFd,
+      true,
+      MessageLoopForIO::WATCH_READ,
+      &mWatcher,
+      this);
+  }
+
+  bool IsComplete() const
+  {
+    return mLen == (MSG1_SIZE + MSG2_SIZE);
+  }
+
+  int GetFd() const
+  {
+    return mFd;
+  }
+
+  int32_t GetChannel1() const
+  {
+    return ReadInt32(OFF_CHANNEL1);
+  }
+
+  int32_t GetSize() const
+  {
+    return ReadInt16(OFF_SIZE);
+  }
+
+  nsString GetBdAddress() const
+  {
+    nsString bdAddress;
+    ReadBdAddress(OFF_BDADDRESS, bdAddress);
+    return bdAddress;
+  }
+
+  int32_t GetChannel2() const
+  {
+    return ReadInt32(OFF_CHANNEL2);
+  }
+
+  int32_t GetConnectionStatus() const
+  {
+    return ReadInt32(OFF_STATUS);
+  }
+
+  int GetClientFd() const
+  {
+    return mClientFd;
+  }
+
+private:
+  bt_status_t RecvMsg1()
+  {
+    struct iovec iv;
+    memset(&iv, 0, sizeof(iv));
+    iv.iov_base = mBuf;
+    iv.iov_len = MSG1_SIZE;
+
+    struct msghdr msg;
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_iov = &iv;
+    msg.msg_iovlen = 1;
+
+    ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
+    if (res < 0) {
+      return BT_STATUS_FAIL;
+    }
+
+    mLen += res;
+
+    return BT_STATUS_SUCCESS;
+  }
+
+  bt_status_t RecvMsg2()
+  {
+    struct iovec iv;
+    memset(&iv, 0, sizeof(iv));
+    iv.iov_base = mBuf + MSG1_SIZE;
+    iv.iov_len = MSG2_SIZE;
+
+    struct msghdr msg;
+    struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_iov = &iv;
+    msg.msg_iovlen = 1;
+    msg.msg_control = cmsgbuf;
+    msg.msg_controllen = sizeof(cmsgbuf);
+
+    ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
+    if (res < 0) {
+      return BT_STATUS_FAIL;
+    }
+
+    mLen += res;
+
+    if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
+      return BT_STATUS_FAIL;
+    }
+
+    struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
+
+    // Extract client fd from message header
+    for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+      if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
+        // if multiple file descriptors have been sent, we close
+        // all but the final one.
+        if (mClientFd != -1) {
+          TEMP_FAILURE_RETRY(close(mClientFd));
+        }
+        // retrieve sent client fd
+        mClientFd = *(static_cast<int*>(CMSG_DATA(cmsgptr)));
+      }
+    }
+
+    return BT_STATUS_SUCCESS;
+  }
+
+  int16_t ReadInt16(unsigned long aOffset) const
+  {
+    /* little-endian buffer */
+    return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
+            static_cast<int16_t>(mBuf[aOffset]);
+  }
+
+  int32_t ReadInt32(unsigned long aOffset) const
+  {
+    /* little-endian buffer */
+    return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
+           (static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
+           (static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
+            static_cast<int32_t>(mBuf[aOffset]);
+  }
+
+  void ReadBdAddress(unsigned long aOffset, nsAString& aBdAddress) const
+  {
+    char str[18];
+    sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+            mBuf[aOffset + 0], mBuf[aOffset + 1], mBuf[aOffset + 2],
+            mBuf[aOffset + 3], mBuf[aOffset + 4], mBuf[aOffset + 5]);
+    aBdAddress.AssignLiteral(str);
+  }
+
+  MessageLoopForIO::FileDescriptorWatcher mWatcher;
+  int mFd;
+  int mClientFd;
+  unsigned char mLen;
+  uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
+};
+
+/* |SocketMessageWatcherTask| starts a SocketMessageWatcher
+ * on the I/O task
+ */
+class SocketMessageWatcherTask MOZ_FINAL : public Task
+{
+public:
+  SocketMessageWatcherTask(SocketMessageWatcher* aWatcher)
+  : mWatcher(aWatcher)
+  {
+    MOZ_ASSERT(mWatcher);
+  }
+
+  void Run() MOZ_OVERRIDE
+  {
+    mWatcher->Watch();
+  }
+
+private:
+  SocketMessageWatcher* mWatcher;
+};
+
+/* |DeleteTask| deletes a class instance on the I/O thread
+ */
+template <typename T>
+class DeleteTask MOZ_FINAL : public Task
+{
+public:
+  DeleteTask(T* aPtr)
+  : mPtr(aPtr)
+  { }
+
+  void Run() MOZ_OVERRIDE
+  {
+    mPtr = nullptr;
+  }
+
+private:
+  nsAutoPtr<T> mPtr;
+};
+
+/* |ConnectWatcher| specializes SocketMessageWatcher for
+ * connect operations by reading the socket messages from
+ * Bluedroid and forwarding the connected socket to the
+ * resource handler.
+ */
+class ConnectWatcher MOZ_FINAL : public SocketMessageWatcher
+{
+public:
+  ConnectWatcher(int aFd, BluetoothSocketResultHandler* aRes)
+  : SocketMessageWatcher(aFd)
+  , mRes(aRes)
+  { }
+
+  void Proceed(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    if (mRes) {
+      DispatchBluetoothSocketResult(mRes,
+                                   &BluetoothSocketResultHandler::Connect,
+                                    GetFd(), GetBdAddress(),
+                                    GetConnectionStatus(), aStatus);
+    }
+    MessageLoopForIO::current()->PostTask(
+      FROM_HERE, new DeleteTask<ConnectWatcher>(this));
+  }
+
+private:
+  nsRefPtr<BluetoothSocketResultHandler> mRes;
+};
+
+void
 BluetoothSocketInterface::Connect(const bt_bdaddr_t* aBdAddr,
                                   btsock_type_t aType, const uint8_t* aUuid,
-                                  int aChannel, int& aSockFd, int aFlags)
+                                  int aChannel, int aFlags,
+                                  BluetoothSocketResultHandler* aRes)
+{
+  int fd;
+
+  bt_status_t status = mInterface->connect(aBdAddr, aType, aUuid, aChannel,
+                                           &fd, aFlags);
+  if (status == BT_STATUS_SUCCESS) {
+    /* receive Bluedroid's socket-setup messages */
+    Task* t = new SocketMessageWatcherTask(new ConnectWatcher(fd, aRes));
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
+  } else if (aRes) {
+      DispatchBluetoothSocketResult(aRes,
+                                    &BluetoothSocketResultHandler::Connect,
+                                    -1, EmptyString(), 0, status);
+  }
+}
+
+/* |AcceptWatcher| specializes |SocketMessageWatcher| for accept
+ * operations by reading the socket messages from Bluedroid and
+ * forwarding the received client socket to the resource handler.
+ * The first message is received immediately. When there's a new
+ * connection, Bluedroid sends the 2nd message with the socket
+ * info and socket file descriptor.
+ */
+class AcceptWatcher MOZ_FINAL : public SocketMessageWatcher
 {
-  return mInterface->connect(aBdAddr, aType, aUuid, aChannel, &aSockFd,
-                             aFlags);
+public:
+  AcceptWatcher(int aFd, BluetoothSocketResultHandler* aRes)
+  : SocketMessageWatcher(aFd)
+  , mRes(aRes)
+  {
+    /* not supplying a result handler leaks received file descriptor */
+    MOZ_ASSERT(mRes);
+  }
+
+  void Proceed(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    if (mRes) {
+      DispatchBluetoothSocketResult(mRes,
+                                    &BluetoothSocketResultHandler::Accept,
+                                    GetClientFd(), GetBdAddress(),
+                                    GetConnectionStatus(),
+                                    aStatus);
+    }
+    MessageLoopForIO::current()->PostTask(
+      FROM_HERE, new DeleteTask<AcceptWatcher>(this));
+  }
+
+private:
+  nsRefPtr<BluetoothSocketResultHandler> mRes;
+};
+
+void
+BluetoothSocketInterface::Accept(int aFd, BluetoothSocketResultHandler* aRes)
+{
+  /* receive Bluedroid's socket-setup messages and client fd */
+  Task* t = new SocketMessageWatcherTask(new AcceptWatcher(aFd, aRes));
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
 }
 
 BluetoothSocketInterface::BluetoothSocketInterface(
   const btsock_interface_t* aInterface)
 : mInterface(aInterface)
 {
   MOZ_ASSERT(mInterface);
 }
--- a/dom/bluetooth2/bluedroid/BluetoothInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothInterface.h
@@ -19,30 +19,51 @@
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothInterface;
 
 //
 // Socket Interface
 //
 
+class BluetoothSocketResultHandler
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothSocketResultHandler)
+
+  virtual ~BluetoothSocketResultHandler() { }
+
+  virtual void OnError(bt_status_t aStatus)
+  {
+    BT_WARNING("Received error code %d", (int)aStatus);
+  }
+
+  virtual void Listen(int aSockFd) { }
+  virtual void Connect(int aSockFd, const nsAString& aBdAddress,
+                       int aConnectionState) { }
+  virtual void Accept(int aSockFd, const nsAString& aBdAddress,
+                      int aConnectionState) { }
+};
+
 class BluetoothSocketInterface
 {
 public:
   friend BluetoothInterface;
 
   // Init and Cleanup is handled by BluetoothInterface
 
-  bt_status_t Listen(btsock_type_t aType,
-                     const char* aServiceName, const uint8_t* aServiceUuid,
-                     int aChannel, int& aSockFd, int aFlags);
+  void Listen(btsock_type_t aType,
+              const char* aServiceName, const uint8_t* aServiceUuid,
+              int aChannel, int aFlags, BluetoothSocketResultHandler* aRes);
 
-  bt_status_t Connect(const bt_bdaddr_t* aBdAddr, btsock_type_t aType,
-                      const uint8_t* aUuid, int aChannel, int& aSockFd,
-                      int aFlags);
+  void Connect(const bt_bdaddr_t* aBdAddr, btsock_type_t aType,
+               const uint8_t* aUuid, int aChannel, int aFlags,
+               BluetoothSocketResultHandler* aRes);
+
+  void Accept(int aFd, BluetoothSocketResultHandler* aRes);
 
 protected:
   BluetoothSocketInterface(const btsock_interface_t* aInterface);
   ~BluetoothSocketInterface();
 
 private:
   const btsock_interface_t* mInterface;
 };
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -13,19 +13,16 @@
 #include "BluetoothInterface.h"
 #include "BluetoothSocketObserver.h"
 #include "BluetoothUtils.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
-#define FIRST_SOCKET_INFO_MSG_LENGTH 4
-#define TOTAL_SOCKET_INFO_LENGTH 20
-
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
 static const size_t MAX_READ_SIZE = 1 << 16;
 static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {
   0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
   0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
 };
@@ -43,84 +40,78 @@ EnsureBluetoothSocketHalLoad()
   NS_ENSURE_TRUE(btInf, false);
 
   sBluetoothSocketInterface = btInf->GetBluetoothSocketInterface();
   NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
 
   return true;
 }
 
-static int16_t
-ReadInt16(const uint8_t* aData, size_t* aOffset)
-{
-  int16_t value = (aData[*aOffset + 1] << 8) | aData[*aOffset];
-
-  *aOffset += 2;
-  return value;
-}
-
-static int32_t
-ReadInt32(const uint8_t* aData, size_t* aOffset)
-{
-  int32_t value = (aData[*aOffset + 3] << 24) |
-                  (aData[*aOffset + 2] << 16) |
-                  (aData[*aOffset + 1] << 8) |
-                  aData[*aOffset];
-  *aOffset += 4;
-  return value;
-}
-
-static void
-ReadBdAddress(const uint8_t* aData, size_t* aOffset, nsAString& aDeviceAddress)
-{
-  char bdstr[18];
-  sprintf(bdstr, "%02x:%02x:%02x:%02x:%02x:%02x",
-          aData[*aOffset], aData[*aOffset + 1], aData[*aOffset + 2],
-          aData[*aOffset + 3], aData[*aOffset + 4], aData[*aOffset + 5]);
-
-  aDeviceAddress.AssignLiteral(bdstr);
-  *aOffset += 6;
-}
-
 class mozilla::dom::bluetooth::DroidSocketImpl : public ipc::UnixFdWatcher
 {
 public:
+  /* The connection status in DroidSocketImpl indicates the current
+   * phase of the socket connection. The initial settign should always
+   * be DISCONNECTED, when no connection is present.
+   *
+   * To establish a connection on the server, DroidSocketImpl moves
+   * to LISTENING. It now waits for incoming connection attempts by
+   * installing a read watcher on the I/O thread. When its socket file
+   * descriptor becomes readable, DroidSocketImpl accepts the connection
+   * and finally moves DroidSocketImpl to CONNECTED. DroidSocketImpl now
+   * uses read and write watchers during data transfers. Any socket setup
+   * is handled internally by the accept method.
+   *
+   * On the client side, DroidSocketImpl moves to CONNECTING and installs
+   * a write watcher on the I/O thread to wait until the connection is
+   * ready. The socket setup is handled internally by the connect method.
+   * Installing the write handler makes the code compatible with POSIX
+   * semantics for non-blocking connects and gives a clear signal when the
+   * conncetion is ready. DroidSocketImpl then moves to CONNECTED and uses
+   * read and write watchers during data transfers.
+   */
+  enum ConnectionStatus {
+    SOCKET_IS_DISCONNECTED = 0,
+    SOCKET_IS_LISTENING,
+    SOCKET_IS_CONNECTING,
+    SOCKET_IS_CONNECTED
+  };
+
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer, int aFd)
     : ipc::UnixFdWatcher(aIOLoop, aFd)
     , mConsumer(aConsumer)
-    , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mChannel(0)
     , mAuth(false)
     , mEncrypt(false)
-  {
-  }
+    , mConnectionStatus(SOCKET_IS_DISCONNECTED)
+  { }
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
                   int aChannel, bool aAuth, bool aEncrypt)
     : ipc::UnixFdWatcher(aIOLoop)
     , mConsumer(aConsumer)
-    , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mChannel(aChannel)
     , mAuth(aAuth)
     , mEncrypt(aEncrypt)
+    , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   { }
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
                   const nsAString& aDeviceAddress,
                   int aChannel, bool aAuth, bool aEncrypt)
     : ipc::UnixFdWatcher(aIOLoop)
     , mConsumer(aConsumer)
-    , mReadMsgForClientFd(false)
     , mShuttingDownOnIOThread(false)
     , mDeviceAddress(aDeviceAddress)
     , mChannel(aChannel)
     , mAuth(aAuth)
     , mEncrypt(aEncrypt)
+    , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   {
     MOZ_ASSERT(!mDeviceAddress.IsEmpty());
   }
 
   ~DroidSocketImpl()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
@@ -154,48 +145,39 @@ public:
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(!mShuttingDownOnIOThread);
 
     RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
 
     mShuttingDownOnIOThread = true;
   }
 
-  void Connect();
-  void Listen();
-
-  void SetUpIO(bool aWrite)
-  {
-    AddWatchers(READ_WATCHER, true);
-    if (aWrite) {
-        AddWatchers(WRITE_WATCHER, false);
-    }
-  }
+  void Connect(int aFd);
+  void Listen(int aFd);
+  void Accept(int aFd);
 
   void ConnectClientFd()
   {
     // Stop current read watch
     RemoveWatchers(READ_WATCHER);
 
+    mConnectionStatus = SOCKET_IS_CONNECTED;
+
     // Restart read & write watch on client fd
-    SetUpIO(true);
+    AddWatchers(READ_WATCHER, true);
+    AddWatchers(WRITE_WATCHER, false);
   }
 
   /**
    * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
    * directly from main thread. All non-main-thread accesses should happen with
    * mImpl as container.
    */
   RefPtr<BluetoothSocket> mConsumer;
 
-  /**
-   * If true, read message header to get client fd.
-   */
-  bool mReadMsgForClientFd;
-
 private:
   /**
    * libevent triggered functions that reads data from socket when available and
    * guarenteed non-blocking. Only to be called on IO thread.
    *
    * @param aFd [in] File descriptor to read from
    */
   virtual void OnFileCanReadWithoutBlocking(int aFd);
@@ -203,40 +185,37 @@ private:
   /**
    * libevent or developer triggered functions that writes data to socket when
    * available and guarenteed non-blocking. Only to be called on IO thread.
    *
    * @param aFd [in] File descriptor to read from
    */
   virtual void OnFileCanWriteWithoutBlocking(int aFd);
 
-  /**
-   * Read message to get data and client fd wrapped in message header
-   *
-   * @param aFd     [in]  File descriptor to read message from
-   * @param aBuffer [out] Data buffer read
-   * @param aLength [out] Number of bytes read
-   */
-  ssize_t ReadMsg(int aFd, void *aBuffer, size_t aLength);
+  void OnSocketCanReceiveWithoutBlocking(int aFd);
+  void OnSocketCanAcceptWithoutBlocking(int aFd);
+  void OnSocketCanSendWithoutBlocking(int aFd);
+  void OnSocketCanConnectWithoutBlocking(int aFd);
 
   /**
    * Raw data queue. Must be pushed/popped from IO thread only.
    */
   typedef nsTArray<UnixSocketRawData* > UnixSocketRawDataQueue;
   UnixSocketRawDataQueue mOutgoingQ;
 
   /**
    * If true, do not requeue whatever task we're running
    */
   bool mShuttingDownOnIOThread;
 
   nsString mDeviceAddress;
   int mChannel;
   bool mAuth;
   bool mEncrypt;
+  ConnectionStatus mConnectionStatus;
 };
 
 template<class T>
 class DeleteInstanceRunnable : public nsRunnable
 {
 public:
   DeleteInstanceRunnable(T* aInstance)
   : mInstance(aInstance)
@@ -248,16 +227,80 @@ public:
 
     return NS_OK;
   }
 
 private:
   T* mInstance;
 };
 
+class DroidSocketImplRunnable : public nsRunnable
+{
+public:
+  DroidSocketImpl* GetImpl() const
+  {
+    return mImpl;
+  }
+
+protected:
+  DroidSocketImplRunnable(DroidSocketImpl* aImpl)
+  : mImpl(aImpl)
+  {
+    MOZ_ASSERT(aImpl);
+  }
+
+  virtual ~DroidSocketImplRunnable()
+  { }
+
+private:
+  DroidSocketImpl* mImpl;
+};
+
+class OnSocketEventRunnable : public DroidSocketImplRunnable
+{
+public:
+  enum SocketEvent {
+    CONNECT_SUCCESS,
+    CONNECT_ERROR,
+    DISCONNECT
+  };
+
+  OnSocketEventRunnable(DroidSocketImpl* aImpl, SocketEvent e)
+  : DroidSocketImplRunnable(aImpl)
+  , mEvent(e)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    DroidSocketImpl* impl = GetImpl();
+
+    if (impl->IsShutdownOnMainThread()) {
+      NS_WARNING("CloseSocket has already been called!");
+      // Since we've already explicitly closed and the close happened before
+      // this, this isn't really an error. Since we've warned, return OK.
+      return NS_OK;
+    }
+    if (mEvent == CONNECT_SUCCESS) {
+      impl->mConsumer->NotifySuccess();
+    } else if (mEvent == CONNECT_ERROR) {
+      impl->mConsumer->NotifyError();
+    } else if (mEvent == DISCONNECT) {
+      impl->mConsumer->NotifyDisconnect();
+    }
+    return NS_OK;
+  }
+
+private:
+  SocketEvent mEvent;
+};
+
 class RequestClosingSocketTask : public nsRunnable
 {
 public:
   RequestClosingSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl)
   {
     MOZ_ASSERT(aImpl);
   }
 
@@ -387,193 +430,152 @@ protected:
   }
 private:
   DroidSocketImpl* mDroidSocketImpl;
 };
 
 class SocketConnectTask : public DroidSocketImplTask
 {
 public:
-  SocketConnectTask(DroidSocketImpl* aDroidSocketImpl)
+  SocketConnectTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
   : DroidSocketImplTask(aDroidSocketImpl)
+  , mFd(aFd)
   { }
 
   void Run() MOZ_OVERRIDE
   {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(!IsCanceled());
-    GetDroidSocketImpl()->Connect();
+    GetDroidSocketImpl()->Connect(mFd);
   }
+
+private:
+  int mFd;
 };
 
 class SocketListenTask : public DroidSocketImplTask
 {
 public:
-  SocketListenTask(DroidSocketImpl* aDroidSocketImpl)
+  SocketListenTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
   : DroidSocketImplTask(aDroidSocketImpl)
+  , mFd(aFd)
   { }
 
   void Run() MOZ_OVERRIDE
   {
     MOZ_ASSERT(!NS_IsMainThread());
     if (!IsCanceled()) {
-      GetDroidSocketImpl()->Listen();
+      GetDroidSocketImpl()->Listen(mFd);
     }
   }
+
+private:
+  int mFd;
 };
 
 class SocketConnectClientFdTask : public Task
 {
   virtual void Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     mImpl->ConnectClientFd();
   }
 
   DroidSocketImpl* mImpl;
 public:
   SocketConnectClientFdTask(DroidSocketImpl* aImpl) : mImpl(aImpl) { }
 };
 
 void
-DroidSocketImpl::Connect()
+DroidSocketImpl::Connect(int aFd)
 {
-  MOZ_ASSERT(sBluetoothSocketInterface);
-
-  bt_bdaddr_t remoteBdAddress;
-  StringToBdAddressType(mDeviceAddress, &remoteBdAddress);
+  MOZ_ASSERT(aFd >= 0);
 
-  // TODO: uuid as argument
-  int fd = -1;
-  bt_status_t status =
-    sBluetoothSocketInterface->Connect(&remoteBdAddress,
-                                       BTSOCK_RFCOMM,
-                                       UUID_OBEX_OBJECT_PUSH,
-                                       mChannel,
-                                       fd,
-                                       (BTSOCK_FLAG_ENCRYPT * mEncrypt) |
-                                       (BTSOCK_FLAG_AUTH * mAuth));
-  NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
-  NS_ENSURE_TRUE_VOID(fd >= 0);
-
-  int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+  int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
   NS_ENSURE_TRUE_VOID(flags >= 0);
 
   if (!(flags & O_NONBLOCK)) {
-    int res = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
+    int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
     NS_ENSURE_TRUE_VOID(!res);
   }
 
-  SetFd(fd);
+  SetFd(aFd);
+  mConnectionStatus = SOCKET_IS_CONNECTING;
 
-  AddWatchers(READ_WATCHER, true);
   AddWatchers(WRITE_WATCHER, false);
 }
 
 void
-DroidSocketImpl::Listen()
+DroidSocketImpl::Listen(int aFd)
 {
-  MOZ_ASSERT(sBluetoothSocketInterface);
-
-  // TODO: uuid and service name as arguments
+  MOZ_ASSERT(aFd >= 0);
 
-  int fd = -1;
-  bt_status_t status =
-    sBluetoothSocketInterface->Listen(BTSOCK_RFCOMM,
-                                      "OBEX Object Push",
-                                      UUID_OBEX_OBJECT_PUSH,
-                                      mChannel,
-                                      fd,
-                                      (BTSOCK_FLAG_ENCRYPT * mEncrypt) |
-                                      (BTSOCK_FLAG_AUTH * mAuth));
-  NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
-  NS_ENSURE_TRUE_VOID(fd >= 0);
-
-  int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+  int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
   NS_ENSURE_TRUE_VOID(flags >= 0);
 
   if (!(flags & O_NONBLOCK)) {
-    int res = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
+    int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
     NS_ENSURE_TRUE_VOID(!res);
   }
 
-  SetFd(fd);
+  SetFd(aFd);
+  mConnectionStatus = SOCKET_IS_LISTENING;
+
   AddWatchers(READ_WATCHER, true);
 }
 
-ssize_t
-DroidSocketImpl::ReadMsg(int aFd, void *aBuffer, size_t aLength)
+void
+DroidSocketImpl::Accept(int aFd)
 {
-  ssize_t ret;
-  struct msghdr msg;
-  struct iovec iv;
-  struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
-
-  memset(&msg, 0, sizeof(msg));
-  memset(&iv, 0, sizeof(iv));
+  Close();
 
-  iv.iov_base = (unsigned char *)aBuffer;
-  iv.iov_len = aLength;
+  int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
+  NS_ENSURE_TRUE_VOID(flags >= 0);
 
-  msg.msg_iov = &iv;
-  msg.msg_iovlen = 1;
-  msg.msg_control = cmsgbuf;
-  msg.msg_controllen = sizeof(cmsgbuf);
-
-  ret = recvmsg(GetFd(), &msg, MSG_NOSIGNAL);
-  if (ret < 0 && errno == EPIPE) {
-    // Treat this as an end of stream
-    return 0;
+  if (!(flags & O_NONBLOCK)) {
+    int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
+    NS_ENSURE_TRUE_VOID(!res);
   }
 
-  NS_ENSURE_FALSE(ret < 0, -1);
-  NS_ENSURE_FALSE(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE), -1);
-
-  // Extract client fd from message header
-  for (struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
-       cmsgptr != nullptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
-    if (cmsgptr->cmsg_level != SOL_SOCKET) {
-      continue;
-    }
-    if (cmsgptr->cmsg_type == SCM_RIGHTS) {
-      int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
+  SetFd(aFd);
+  mConnectionStatus = SOCKET_IS_CONNECTED;
 
-      // Overwrite fd with client fd
-      int fd = pDescriptors[0];
-      int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
-      NS_ENSURE_TRUE(flags >= 0, 0);
-      if (!(flags & O_NONBLOCK)) {
-        int res = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
-        NS_ENSURE_TRUE(!res, 0);
-      }
-      Close();
-      SetFd(fd);
-      break;
-    }
+  nsRefPtr<OnSocketEventRunnable> r =
+    new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
+  NS_DispatchToMainThread(r);
+
+  AddWatchers(READ_WATCHER, true);
+  if (!mOutgoingQ.IsEmpty()) {
+    AddWatchers(WRITE_WATCHER, false);
   }
-
-  return ret;
 }
 
 void
 DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
 {
+  if (mConnectionStatus == SOCKET_IS_CONNECTED) {
+    OnSocketCanReceiveWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_LISTENING) {
+    OnSocketCanAcceptWithoutBlocking(aFd);
+  } else {
+    NS_NOTREACHED("invalid connection state for reading");
+  }
+}
+
+void
+DroidSocketImpl::OnSocketCanReceiveWithoutBlocking(int aFd)
+{
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDownOnIOThread);
 
   // Read all of the incoming data.
   while (true) {
     nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
 
-    ssize_t ret;
-    if (!mReadMsgForClientFd) {
-      ret = read(aFd, incoming->mData, incoming->mSize);
-    } else {
-      ret = ReadMsg(aFd, incoming->mData, incoming->mSize);
-    }
+    ssize_t ret = read(aFd, incoming->mData, incoming->mSize);
 
     if (ret <= 0) {
       if (ret == -1) {
         if (errno == EINTR) {
           continue; // retry system call when interrupted
         }
         if (errno == EAGAIN || errno == EWOULDBLOCK) {
           return; // no data available: return and re-poll
@@ -601,19 +603,124 @@ DroidSocketImpl::OnFileCanReadWithoutBlo
     if (ret < ssize_t(MAX_READ_SIZE)) {
       return;
     }
   }
 
   MOZ_CRASH("We returned early");
 }
 
+class AcceptTask MOZ_FINAL : public DroidSocketImplTask
+{
+public:
+  AcceptTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
+  : DroidSocketImplTask(aDroidSocketImpl)
+  , mFd(aFd)
+  { }
+
+  void Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!IsCanceled());
+
+    GetDroidSocketImpl()->Accept(mFd);
+  }
+
+private:
+  int mFd;
+};
+
+class AcceptResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
+{
+public:
+  AcceptResultHandler(DroidSocketImpl* aImpl)
+  : mImpl(aImpl)
+  {
+    MOZ_ASSERT(mImpl);
+  }
+
+  void Accept(int aFd, const nsAString& aBdAddress,
+              int aConnectionStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mImpl->IsShutdownOnMainThread()) {
+      BT_LOGD("mConsumer is null, aborting receive!");
+      return;
+    }
+
+    mImpl->mConsumer->SetAddress(aBdAddress);
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new AcceptTask(mImpl, aFd));
+  }
+
+  void OnError(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    BT_LOGR("BluetoothSocketInterface::Accept failed: %d", (int)aStatus);
+  }
+
+private:
+  DroidSocketImpl* mImpl;
+};
+
+class AcceptRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  AcceptRunnable(int aFd, DroidSocketImpl* aImpl)
+  : mFd(aFd)
+  , mImpl(aImpl)
+  {
+    MOZ_ASSERT(mImpl);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(sBluetoothSocketInterface);
+
+    sBluetoothSocketInterface->Accept(mFd, new AcceptResultHandler(mImpl));
+
+    return NS_OK;
+  }
+
+private:
+  int mFd;
+  DroidSocketImpl* mImpl;
+};
+
+void
+DroidSocketImpl::OnSocketCanAcceptWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  /* When a listening socket is ready for receiving data,
+   * we can call |Accept| on it.
+   */
+
+  RemoveWatchers(READ_WATCHER);
+  nsRefPtr<AcceptRunnable> t = new AcceptRunnable(aFd, this);
+  NS_DispatchToMainThread(t);
+}
+
 void
 DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
 {
+  if (mConnectionStatus == SOCKET_IS_CONNECTED) {
+    OnSocketCanSendWithoutBlocking(aFd);
+  } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
+    OnSocketCanConnectWithoutBlocking(aFd);
+  } else {
+    NS_NOTREACHED("invalid connection state for writing");
+  }
+}
+
+void
+DroidSocketImpl::OnSocketCanSendWithoutBlocking(int aFd)
+{
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDownOnIOThread);
   MOZ_ASSERT(aFd >= 0);
 
   // Try to write the bytes of mCurrentRilRawData.  If all were written, continue.
   //
   // Otherwise, save the byte position of the next byte to write
   // within mCurrentWriteOffset, and request another write when the
@@ -644,25 +751,46 @@ DroidSocketImpl::OnFileCanWriteWithoutBl
     if (data->mCurrentWriteOffset != data->mSize) {
       AddWatchers(WRITE_WATCHER, false);
     }
     mOutgoingQ.RemoveElementAt(0);
     delete data;
   }
 }
 
+void
+DroidSocketImpl::OnSocketCanConnectWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  /* We follow Posix behaviour here: Connect operations are
+   * complete once we can write to the connecting socket.
+   */
+
+  mConnectionStatus = SOCKET_IS_CONNECTED;
+
+  nsRefPtr<OnSocketEventRunnable> r =
+    new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
+  NS_DispatchToMainThread(r);
+
+  AddWatchers(READ_WATCHER, true);
+  if (!mOutgoingQ.IsEmpty()) {
+    AddWatchers(WRITE_WATCHER, false);
+  }
+}
+
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
   , mImpl(nullptr)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
-  , mReceivedSocketInfoLength(0)
 {
   MOZ_ASSERT(aObserver);
 
   EnsureBluetoothSocketHalLoad();
   mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
 }
 
 void
@@ -676,118 +804,135 @@ BluetoothSocket::CloseDroidSocket()
   // From this point on, we consider mImpl as being deleted.
   // We sever the relationship here so any future calls to listen or connect
   // will create a new implementation.
   mImpl->ShutdownOnMainThread();
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                    new ShutdownSocketTask(mImpl));
   mImpl = nullptr;
 
-  OnDisconnect();
+  NotifyDisconnect();
 }
 
+class ConnectResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
+{
+public:
+  ConnectResultHandler(DroidSocketImpl* aImpl)
+  : mImpl(aImpl)
+  {
+    MOZ_ASSERT(mImpl);
+  }
+
+  void Connect(int aFd, const nsAString& aBdAddress,
+               int aConnectionStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (!mImpl->IsShutdownOnMainThread()) {
+      mImpl->mConsumer->SetAddress(aBdAddress);
+    }
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                     new SocketConnectTask(mImpl, aFd));
+  }
+
+  void OnError(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    BT_WARNING("Connect failed: %d", (int)aStatus);
+  }
+
+private:
+  DroidSocketImpl* mImpl;
+};
+
 bool
 BluetoothSocket::Connect(const nsAString& aDeviceAddress, int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
-  mIsServer = false;
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this, aDeviceAddress,
                               aChannel, mAuth, mEncrypt);
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                   new SocketConnectTask(mImpl));
+
+  bt_bdaddr_t remoteBdAddress;
+  StringToBdAddressType(aDeviceAddress, &remoteBdAddress);
 
+  // TODO: uuid as argument
+  sBluetoothSocketInterface->Connect(&remoteBdAddress,
+                                     BTSOCK_RFCOMM,
+                                     UUID_OBEX_OBJECT_PUSH,
+                                     aChannel,
+                                     (BTSOCK_FLAG_ENCRYPT * mEncrypt) |
+                                     (BTSOCK_FLAG_AUTH * mAuth),
+                                     new ConnectResultHandler(mImpl));
   return true;
 }
 
+class ListenResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
+{
+public:
+  ListenResultHandler(DroidSocketImpl* aImpl)
+  : mImpl(aImpl)
+  {
+    MOZ_ASSERT(mImpl);
+  }
+
+  void Listen(int aFd) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                     new SocketListenTask(mImpl, aFd));
+  }
+
+  void OnError(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    BT_WARNING("Listen failed: %d", (int)aStatus);
+  }
+
+private:
+  DroidSocketImpl* mImpl;
+};
+
 bool
 BluetoothSocket::Listen(int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_FALSE(mImpl, false);
 
-  mIsServer = true;
   mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this, aChannel, mAuth,
                               mEncrypt);
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                   new SocketListenTask(mImpl));
+
+  sBluetoothSocketInterface->Listen(BTSOCK_RFCOMM,
+                                    "OBEX Object Push",
+                                    UUID_OBEX_OBJECT_PUSH,
+                                    aChannel,
+                                    (BTSOCK_FLAG_ENCRYPT * mEncrypt) |
+                                    (BTSOCK_FLAG_AUTH * mAuth),
+                                    new ListenResultHandler(mImpl));
   return true;
 }
 
 bool
 BluetoothSocket::SendDroidSocketData(UnixSocketRawData* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE(mImpl, false);
 
   MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                    new SocketSendTask(this, mImpl, aData));
   return true;
 }
 
-bool
-BluetoothSocket::ReceiveSocketInfo(nsAutoPtr<UnixSocketRawData>& aMessage)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  /**
-   * 2 socket info messages (20 bytes) to receive at the beginning:
-   * - 1st message: [channel:4]
-   * - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
-   */
-  if (mReceivedSocketInfoLength >= TOTAL_SOCKET_INFO_LENGTH) {
-    // We've got both socket info messages
-    return false;
-  }
-  mReceivedSocketInfoLength += aMessage->mSize;
-
-  size_t offset = 0;
-  if (mReceivedSocketInfoLength == FIRST_SOCKET_INFO_MSG_LENGTH) {
-    // 1st message: [channel:4]
-    int32_t channel = ReadInt32(aMessage->mData, &offset);
-    BT_LOGR("channel %d", channel);
-
-    // If this is server socket, read header of next message for client fd
-    mImpl->mReadMsgForClientFd = mIsServer;
-  } else if (mReceivedSocketInfoLength == TOTAL_SOCKET_INFO_LENGTH) {
-    // 2nd message: [size:2][bd address:6][channel:4][connection status:4]
-    int16_t size = ReadInt16(aMessage->mData, &offset);
-    ReadBdAddress(aMessage->mData, &offset, mDeviceAddress);
-    int32_t channel = ReadInt32(aMessage->mData, &offset);
-    int32_t connectionStatus = ReadInt32(aMessage->mData, &offset);
-
-    BT_LOGR("size %d channel %d remote addr %s status %d",
-      size, channel, NS_ConvertUTF16toUTF8(mDeviceAddress).get(), connectionStatus);
-
-    if (connectionStatus != 0) {
-      OnConnectError();
-      return true;
-    }
-
-    if (mIsServer) {
-      mImpl->mReadMsgForClientFd = false;
-      // Connect client fd on IO thread
-      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-                                       new SocketConnectClientFdTask(mImpl));
-    }
-    OnConnectSuccess();
-  }
-
-  return true;
-}
-
 void
 BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
 {
-  if (ReceiveSocketInfo(aMessage)) {
-    return;
-  }
-
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
   mObserver->ReceiveSocketData(this, aMessage);
 }
 
 void
 BluetoothSocket::OnConnectSuccess()
 {
@@ -806,9 +951,8 @@ BluetoothSocket::OnConnectError()
 
 void
 BluetoothSocket::OnDisconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
   mObserver->OnSocketDisconnect(this);
 }
-
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.h
@@ -18,40 +18,18 @@ class DroidSocketImpl;
 class BluetoothSocket : public mozilla::ipc::UnixSocketConsumer
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
                   bool aEncrypt);
 
-  /**
-   * Connect to remote server as a client.
-   *
-   * The steps are as following:
-   * 1) BluetoothSocket acquires fd from bluedroid, and creates
-   *    a DroidSocketImpl to watch read/write of the fd.
-   * 2) DroidSocketImpl receives first 2 messages to get socket info.
-   * 3) Obex client session starts.
-   */
   bool Connect(const nsAString& aDeviceAddress, int aChannel);
 
-  /**
-   * Listen to incoming connection as a server.
-   *
-   * The steps are as following:
-   * 1) BluetoothSocket acquires fd from bluedroid, and creates
-   *    a DroidSocketImpl to watch read of the fd. DroidSocketImpl
-   *    receives the 1st message immediately.
-   * 2) When there's incoming connection, DroidSocketImpl receives
-   *    2nd message to get socket info and client fd.
-   * 3) DroidSocketImpl stops watching read of original fd and
-   *    starts to watch read/write of client fd.
-   * 4) Obex server session starts.
-   */
   bool Listen(int aChannel);
 
   inline void Disconnect()
   {
     CloseDroidSocket();
   }
 
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
@@ -60,26 +38,27 @@ public:
   virtual void ReceiveSocketData(
     nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) MOZ_OVERRIDE;
 
   inline void GetAddress(nsAString& aDeviceAddress)
   {
     aDeviceAddress = mDeviceAddress;
   }
 
+  inline void SetAddress(const nsAString& aDeviceAddress)
+  {
+    mDeviceAddress = aDeviceAddress;
+  }
+
   void CloseDroidSocket();
   bool SendDroidSocketData(mozilla::ipc::UnixSocketRawData* aData);
 
 private:
   BluetoothSocketObserver* mObserver;
   DroidSocketImpl* mImpl;
   nsString mDeviceAddress;
   bool mAuth;
   bool mEncrypt;
-  bool mIsServer;
-  int mReceivedSocketInfoLength;
-
-  bool ReceiveSocketInfo(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage);
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth2/tests/marionette/head.js
+++ b/dom/bluetooth2/tests/marionette/head.js
@@ -557,16 +557,53 @@ function waitForAdapterAttributeChanged(
       deferred.resolve(aEvent);
     }
   };
 
   return deferred.promise;
 }
 
 /**
+ * Wait for specified number of 'devicefound' events.
+ *
+ * Resolve if specified number of devices has been found. Never reject.
+ *
+ * Fulfill params: an array which contains BluetoothDeviceEvents that we
+ *                 received from the BluetoothDiscoveryHandle.
+ *
+ * @param aDiscoveryHandle
+ *        A BluetoothDiscoveryHandle which is used to notify application of
+ *        discovered remote bluetooth devices.
+ * @param aExpectedNumberOfDevices
+ *        The number of remote devices we expect to discovery.
+ *
+ * @return A deferred promise.
+ */
+function waitForDevicesFound(aDiscoveryHandle, aExpectedNumberOfDevices) {
+  let deferred = Promise.defer();
+
+  ok(aDiscoveryHandle instanceof BluetoothDiscoveryHandle,
+    "discoveryHandle should be a BluetoothDiscoveryHandle");
+
+  let devicesArray = [];
+  aDiscoveryHandle.ondevicefound = function onDeviceFound(aEvent) {
+    ok(aEvent instanceof BluetoothDeviceEvent,
+      "aEvent should be a BluetoothDeviceEvent");
+
+    devicesArray.push(aEvent);
+    if (devicesArray.length >= aExpectedNumberOfDevices) {
+      aDiscoveryHandle.ondevicefound = null;
+      deferred.resolve(devicesArray);
+    }
+  };
+
+  return deferred.promise;
+}
+
+/**
  * Flush permission settings and call |finish()|.
  */
 function cleanUp() {
   waitFor(function() {
     SpecialPowers.flushPermissions(function() {
       // Use ok here so that we have at least one test run.
       ok(true, "permissions flushed");
 
--- a/dom/bluetooth2/tests/marionette/manifest.ini
+++ b/dom/bluetooth2/tests/marionette/manifest.ini
@@ -1,8 +1,9 @@
 [DEFAULT]
 b2g = true
 browser = false
 qemu = false
 
 [test_dom_BluetoothManager_API2.js]
 [test_dom_BluetoothAdapter_enable_API2.js]
 [test_dom_BluetoothAdapter_setters_API2.js]
+[test_dom_BluetoothAdapter_discovery_API2.js]
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/tests/marionette/test_dom_BluetoothAdapter_discovery_API2.js
@@ -0,0 +1,124 @@
+/* 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/. */
+
+///////////////////////////////////////////////////////////////////////////////
+// Test Purpose:
+//   To verify the discovery process of BluetoothAdapter.
+//   Testers have to put the B2G devices in an environment which is surrounded
+//   by N discoverable remote devices. To pass this test, the number N has to be
+//   greater or equals than EXPECTED_NUMBER_OF_REMOTE_DEVICES.
+//
+// Test Procedure:
+//   [0] Set Bluetooth permission and enable default adapter.
+//   [1] Start discovery and verify the correctness.
+//   [2] Attach event handler for 'ondevicefound'.
+//   [3] Stop discovery and verify the correctness.
+//   [4] Mark the BluetoothDiscoveryHandle from [1] as expired.
+//   [5] Start discovery and verify the correctness.
+//   [6] Wait for 'devicefound' events.
+//   [7] Stop discovery and verify the correctness.
+//   [8] Call 'startDiscovery' twice continuously.
+//   [9] Call 'stopDiscovery' twice continuously.
+//   [10] Clean up the event handler of [2].
+//
+// Test Coverage:
+//   - BluetoothAdapter.discovering
+//   - BluetoothAdapter.startDiscovery()
+//   - BluetoothAdapter.stopDiscovery()
+//   - BluetoothAdapter.onattributechanged()
+//   - BluetoothDiscoveryHandle.ondevicefound()
+//
+///////////////////////////////////////////////////////////////////////////////
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const EXPECTED_NUMBER_OF_REMOTE_DEVICES = 2;
+
+startBluetoothTest(true, function testCaseMain(aAdapter) {
+  log("Checking adapter attributes ...");
+
+  is(aAdapter.state, "enabled", "adapter.state");
+  isnot(aAdapter.address, "", "adapter.address");
+
+  // Since adapter has just been re-enabled, these properties should be 'false'.
+  is(aAdapter.discovering, false, "adapter.discovering");
+  is(aAdapter.discoverable, false, "adapter.discoverable");
+
+  log("adapter.address: " + aAdapter.address);
+  log("adapter.name: " + aAdapter.name);
+
+  let discoveryHandle = null;
+  return Promise.resolve()
+    .then(function() {
+      log("[1] Start discovery and verify the correctness ... ");
+      let promises = [];
+      promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", true));
+      promises.push(aAdapter.startDiscovery());
+      return Promise.all(promises);
+    })
+    .then(function(aResults) {
+      log("[2] Attach event handler for 'ondevicefound' ... ");
+      discoveryHandle = aResults[1];
+      isHandleExpired = false;
+      discoveryHandle.ondevicefound = function onDeviceFound(aEvent) {
+        if (isHandleExpired) {
+          ok(false, "Expired BluetoothDiscoveryHandle received an event.");
+        }
+      };
+    })
+    .then(function() {
+      log("[3] Stop discovery and and verify the correctness ... ");
+      let promises = [];
+      if (aAdapter.discovering) {
+        promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", false));
+      }
+      promises.push(aAdapter.stopDiscovery());
+      return Promise.all(promises);
+    })
+    .then(function() {
+      log("[4] Mark the BluetoothDiscoveryHandle from [1] as expired ... ");
+      isHandleExpired = true;
+    })
+    .then(function() {
+      log("[5] Start discovery and verify the correctness ... ");
+      let promises = [];
+      promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", true));
+      promises.push(aAdapter.startDiscovery());
+      return Promise.all(promises);
+    })
+    .then(function(aResults) {
+      log("[6] Wait for 'devicefound' events ... ");
+      return waitForDevicesFound(aResults[1], EXPECTED_NUMBER_OF_REMOTE_DEVICES);
+    })
+    .then(function() {
+      log("[7] Stop discovery and and verify the correctness ... ");
+      let promises = [];
+      if (aAdapter.discovering) {
+        promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", false));
+      }
+      promises.push(aAdapter.stopDiscovery());
+      return Promise.all(promises);
+    })
+    .then(function() {
+      log("[8] Call 'startDiscovery' twice continuously ... ");
+      return aAdapter.startDiscovery()
+        .then(() => aAdapter.startDiscovery())
+        .then(() => ok(false, "Call startDiscovery() when adapter is discovering. - Fail"),
+              () => ok(true, "Call startDiscovery() when adapter is discovering. - Success"));
+    })
+    .then(function() {
+      log("[9] Call 'stopDiscovery' twice continuously ... ");
+      return aAdapter.stopDiscovery()
+        .then(() => aAdapter.stopDiscovery())
+        .then(() => ok(true, "Call stopDiscovery() when adapter isn't discovering. - Success"),
+              () => ok(false, "Call stopDiscovery() when adapter isn't discovering. - Fail"));
+    })
+    .then(function() {
+      log("[10] Clean up the event handler of [2] ... ");
+      if (discoveryHandle) {
+        discoveryHandle.ondevicefound = null;
+      }
+    });
+});
--- a/dom/system/gonk/AutoMounter.cpp
+++ b/dom/system/gonk/AutoMounter.cpp
@@ -20,26 +20,28 @@
 #include "AutoMounter.h"
 #include "nsVolumeService.h"
 #include "AutoMounterSetting.h"
 #include "base/message_loop.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Hal.h"
 #include "mozilla/StaticPtr.h"
+#include "MozMtpServer.h"
 #include "nsAutoPtr.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "OpenFileFinder.h"
 #include "Volume.h"
 #include "VolumeManager.h"
 
 using namespace mozilla::hal;
+USING_MTP_NAMESPACE
 
 /**************************************************************************
 *
 * The following "switch" files are available for monitoring usb
 * connections:
 *
 *   /sys/devices/virtual/switch/usb_connected/state
 *   /sys/devices/virtual/switch/usb_configuration/state
@@ -64,16 +66,17 @@ using namespace mozilla::hal;
 
 #define USB_CONFIGURATION_SWITCH_NAME   NS_LITERAL_STRING("usb_configuration")
 
 #define GB_SYS_UMS_ENABLE     "/sys/devices/virtual/usb_composite/usb_mass_storage/enable"
 #define GB_SYS_USB_CONFIGURED "/sys/devices/virtual/switch/usb_configuration/state"
 
 #define ICS_SYS_USB_FUNCTIONS "/sys/devices/virtual/android_usb/android0/functions"
 #define ICS_SYS_UMS_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mass_storage"
+#define ICS_SYS_MTP_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mtp"
 #define ICS_SYS_USB_STATE     "/sys/devices/virtual/android_usb/android0/state"
 
 #define USE_DEBUG 0
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO,  "AutoMounter", ## args)
 #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN,  "AutoMounter", ## args)
 #define ERR(args...)  __android_log_print(ANDROID_LOG_ERROR, "AutoMounter", ## args)
@@ -224,16 +227,19 @@ public:
 
         // Note: eventually CheckVolumeSettings will call
         //       AutoMounter::SetSharingMode, which will in turn call
         //       UpdateState if needed.
       }
     }
   }
 
+  void StartMtpServer();
+  void StopMtpServer();
+
   void UpdateState();
 
   const char* ModeStr(int32_t aMode)
   {
     switch (aMode) {
       case AUTOMOUNTER_DISABLE:                 return "Disable";
       case AUTOMOUNTER_ENABLE:                  return "Enable";
       case AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED:  return "DisableWhenUnplugged";
@@ -342,16 +348,17 @@ private:
 
   AutoVolumeEventObserver         mVolumeEventObserver;
   AutoVolumeManagerStateObserver  mVolumeManagerStateObserver;
   RefPtr<VolumeResponseCallback>  mResponseCallback;
   int32_t                         mMode;
 };
 
 static StaticRefPtr<AutoMounter> sAutoMounter;
+static StaticRefPtr<MozMtpServer> sMozMtpServer;
 
 /***************************************************************************/
 
 void
 AutoVolumeManagerStateObserver::Notify(const VolumeManager::StateChangedEvent &)
 {
   LOG("VolumeManager state changed event: %s", VolumeManager::StateStr());
 
@@ -393,16 +400,35 @@ AutoMounterResponseCallback::ResponseRec
       aCommand->CmdStr(), ResponseCode(), ResponseStr().get());
 
   if (++mErrorCount < kMaxErrorCount) {
     DBG("Calling UpdateState due to VolumeResponseCallback::OnError");
     sAutoMounter->UpdateState();
   }
 }
 
+void
+AutoMounter::StartMtpServer()
+{
+  if (sMozMtpServer) {
+    // Mtp Server is already running - nothing to do
+    return;
+  }
+  LOG("Starting MtpServer");
+  sMozMtpServer = new MozMtpServer();
+  sMozMtpServer->Run();
+}
+
+void
+AutoMounter::StopMtpServer()
+{
+  LOG("Stopping MtpServer");
+  sMozMtpServer = nullptr;
+}
+
 /***************************************************************************/
 
 void
 AutoMounter::UpdateState()
 {
   static bool inUpdateState = false;
   if (inUpdateState) {
     // When UpdateState calls SetISharing, this causes a volume state
@@ -436,47 +462,58 @@ AutoMounter::UpdateState()
   if (mResponseCallback->IsPending()) {
     // We only deal with one outstanding volume command at a time,
     // so we need to wait for it to finish.
     return;
   }
 
   bool  umsAvail = false;
   bool  umsEnabled = false;
+  bool  mtpAvail = false;
+  bool  mtpEnabled = false;
 
   if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) {
+    char functionsStr[60];
+    if (!ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
+      ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
+      functionsStr[0] = '\0';
+    }
     umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0);
     if (umsAvail) {
-      char functionsStr[60];
-      if (ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
-        umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
-      } else {
-        ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
-        umsEnabled = false;
-      }
-    } else {
-      umsEnabled = false;
+      umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
     }
-  } else {
-    umsAvail = ReadSysFile(GB_SYS_UMS_ENABLE, &umsEnabled);
+    mtpAvail = (access(ICS_SYS_MTP_DIRECTORY, F_OK) == 0);
+    if (mtpAvail) {
+      mtpEnabled = strstr(functionsStr, "mtp") != nullptr;
+    }
   }
 
   bool usbCablePluggedIn = IsUsbCablePluggedIn();
   bool enabled = (mMode == AUTOMOUNTER_ENABLE);
 
   if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) {
     enabled = usbCablePluggedIn;
     if (!usbCablePluggedIn) {
       mMode = AUTOMOUNTER_DISABLE;
     }
   }
 
-  bool tryToShare = (umsAvail && umsEnabled && enabled && usbCablePluggedIn);
-  LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
-      umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare);
+  bool tryToShare = (((umsAvail && umsEnabled) || (mtpAvail && mtpEnabled))
+                  && enabled && usbCablePluggedIn);
+  LOG("UpdateState: ums:%d%d mtp:%d%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
+      umsAvail, umsEnabled, mtpAvail, mtpEnabled, mMode, usbCablePluggedIn, tryToShare);
+
+  if (mtpAvail && mtpEnabled) {
+    if (enabled && usbCablePluggedIn) {
+      StartMtpServer();
+    } else {
+      StopMtpServer();
+    }
+    return;
+  }
 
   bool filesOpen = false;
   static unsigned filesOpenDelayCount = 0;
   VolumeArray::index_type volIndex;
   VolumeArray::size_type  numVolumes = VolumeManager::NumVolumes();
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     RefPtr<Volume>  vol = VolumeManager::GetVolume(volIndex);
     Volume::STATE   volState = vol->State();
@@ -897,16 +934,19 @@ AutoMounterUnmountVolume(const nsCString
       FROM_HERE,
       NewRunnableFunction(AutoMounterUnmountVolumeIOThread,
                           aVolumeName));
 }
 
 void
 ShutdownAutoMounter()
 {
+  if (sAutoMounter) {
+    sAutoMounter->StopMtpServer();
+  }
   sAutoMounterSetting = nullptr;
   sUsbCableObserver = nullptr;
 
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(ShutdownAutoMounterIOThread));
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/MozMtpCommon.h
@@ -0,0 +1,44 @@
+/* -*- 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_system_mozmtpcommon_h__
+#define mozilla_system_mozmtpcommon_h__
+
+#include "mozilla/Types.h"
+#include <android/log.h>
+
+#define MTP_LOG(msg, ...)                                            \
+  __android_log_print(ANDROID_LOG_INFO, "MozMtp",                    \
+                      "%s: " msg, __FUNCTION__, ##__VA_ARGS__)       \
+
+#define MTP_ERR(msg, ...)                                            \
+  __android_log_print(ANDROID_LOG_ERROR, "MozMtp",                   \
+                      "%s: " msg, __FUNCTION__, ##__VA_ARGS__)       \
+
+#define BEGIN_MTP_NAMESPACE \
+  namespace mozilla { namespace system { namespace mtp {
+#define END_MTP_NAMESPACE \
+  } /* namespace mtp */ } /* namespace system */ } /* namespace mozilla */
+#define USING_MTP_NAMESPACE \
+  using namespace mozilla::system::mtp;
+
+namespace android {
+  class MOZ_EXPORT MtpServer;
+  class MOZ_EXPORT MtpStorage;
+  class MOZ_EXPORT MtpDatabase;
+  class MOZ_EXPORT MtpDataPacket;
+  class MOZ_EXPORT MtpProperty;
+}
+
+#include <mtp.h>
+#include <MtpDatabase.h>
+#include <MtpObjectInfo.h>
+#include <MtpProperty.h>
+#include <MtpServer.h>
+#include <MtpStorage.h>
+#include <MtpTypes.h>
+
+#endif // mozilla_system_mtpcommon_h__
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/MozMtpDatabase.cpp
@@ -0,0 +1,792 @@
+/* -*- 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 "MozMtpDatabase.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Scoped.h"
+#include "nsIFile.h"
+#include "nsPrintfCString.h"
+#include "prio.h"
+
+#include <dirent.h>
+#include <libgen.h>
+
+using namespace android;
+using namespace mozilla;
+
+namespace mozilla {
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCloseDir, PRDir, PR_CloseDir)
+}
+
+BEGIN_MTP_NAMESPACE
+
+static const char *
+ObjectPropertyAsStr(MtpObjectProperty aProperty)
+{
+  switch (aProperty) {
+    case MTP_PROPERTY_STORAGE_ID:       return "MTP_PROPERTY_STORAGE_ID";
+    case MTP_PROPERTY_OBJECT_FORMAT:    return "MTP_PROPERTY_OBJECT_FORMAT";
+    case MTP_PROPERTY_OBJECT_SIZE:      return "MTP_PROPERTY_OBJECT_SIZE";
+    case MTP_PROPERTY_WIDTH:            return "MTP_PROPERTY_WIDTH";
+    case MTP_PROPERTY_HEIGHT:           return "MTP_PROPERTY_HEIGHT";
+    case MTP_PROPERTY_IMAGE_BIT_DEPTH:  return "MTP_PROPERTY_IMAGE_BIT_DEPTH";
+    case MTP_PROPERTY_DISPLAY_NAME:     return "MTP_PROPERTY_DISPLAY_NAME";
+  }
+  return "MTP_PROPERTY_???";
+}
+
+MozMtpDatabase::MozMtpDatabase(const char *aDir)
+{
+  MTP_LOG("");
+
+  // We use the index into the array as the handle. Since zero isn't a valid
+  // index, we stick a dummy entry there.
+
+  RefPtr<DbEntry> dummy;
+
+  mDb.AppendElement(dummy);
+
+  ReadVolume("sdcard", aDir);
+}
+
+//virtual
+MozMtpDatabase::~MozMtpDatabase()
+{
+  MTP_LOG("");
+}
+
+void
+MozMtpDatabase::AddEntry(DbEntry *entry)
+{
+  entry->mHandle = GetNextHandle();
+  MOZ_ASSERT(mDb.Length() == entry->mHandle);
+  mDb.AppendElement(entry);
+
+  MTP_LOG("AddEntry: Handle: 0x%08x Parent: 0x%08x Path:'%s'",
+          entry->mHandle, entry->mParent, entry->mPath.get());
+}
+
+TemporaryRef<MozMtpDatabase::DbEntry>
+MozMtpDatabase::GetEntry(MtpObjectHandle aHandle)
+{
+  RefPtr<DbEntry> entry;
+
+  if (aHandle > 0 && aHandle < mDb.Length()) {
+    entry = mDb[aHandle];
+  }
+  return entry;
+}
+
+void
+MozMtpDatabase::RemoveEntry(MtpObjectHandle aHandle)
+{
+  if (aHandle > 0 && aHandle < mDb.Length()) {
+    mDb[aHandle] = nullptr;
+  }
+}
+
+nsCString
+MozMtpDatabase::BaseName(const nsCString& path)
+{
+  nsCOMPtr<nsIFile> file;
+  NS_NewNativeLocalFile(path, false, getter_AddRefs(file));
+  if (file) {
+    nsCString leafName;
+    file->GetNativeLeafName(leafName);
+    return leafName;
+  }
+  return path;
+}
+
+void
+MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
+{
+  ScopedCloseDir dir;
+
+  if (!(dir = PR_OpenDir(aDir))) {
+    MTP_ERR("Unable to open directory '%s'", aDir);
+    return;
+  }
+
+  PRDirEntry* dirEntry;
+  while ((dirEntry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
+    nsPrintfCString filename("%s/%s", aDir, dirEntry->name);
+    PRFileInfo64 fileInfo;
+    if (PR_GetFileInfo64(filename.get(), &fileInfo) != PR_SUCCESS) {
+      MTP_ERR("Unable to retrieve file information for '%s'", filename.get());
+      continue;
+    }
+
+    RefPtr<DbEntry> entry = new DbEntry;
+
+    entry->mStorageID = MTP_STORAGE_FIXED_RAM;
+    entry->mParent = aParent;
+    entry->mObjectName = dirEntry->name;
+    entry->mDisplayName = dirEntry->name;
+    entry->mPath = filename;
+    entry->mDateCreated = fileInfo.creationTime;
+    entry->mDateModified = fileInfo.modifyTime;
+
+    if (fileInfo.type == PR_FILE_FILE) {
+      entry->mObjectFormat = MTP_FORMAT_DEFINED;
+      //TODO: Check how 64-bit filesize are dealt with
+      entry->mObjectSize = fileInfo.size;
+      AddEntry(entry);
+    } else if (fileInfo.type == PR_FILE_DIRECTORY) {
+      entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
+      entry->mObjectSize = 0;
+      AddEntry(entry);
+      ParseDirectory(filename.get(), entry->mHandle);
+    }
+  }
+}
+
+void
+MozMtpDatabase::ReadVolume(const char *volumeName, const char *aDir)
+{
+  //TODO: Add an assert re thread being run on
+
+  PRFileInfo  fileInfo;
+
+  if (PR_GetFileInfo(aDir, &fileInfo) != PR_SUCCESS) {
+    MTP_ERR("'%s' doesn't exist", aDir);
+    return;
+  }
+  if (fileInfo.type != PR_FILE_DIRECTORY) {
+    MTP_ERR("'%s' isn't a directory", aDir);
+    return;
+  }
+
+  RefPtr<DbEntry> entry = new DbEntry;
+
+  entry->mStorageID = MTP_STORAGE_FIXED_RAM;
+  entry->mParent = MTP_PARENT_ROOT;
+  entry->mObjectName = volumeName;
+  entry->mDisplayName = volumeName;
+  entry->mPath = aDir;
+  entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
+  entry->mObjectSize = 0;
+
+  AddEntry(entry);
+
+  ParseDirectory(aDir, entry->mHandle);
+}
+
+// called from SendObjectInfo to reserve a database entry for the incoming file
+//virtual
+MtpObjectHandle
+MozMtpDatabase::beginSendObject(const char* aPath,
+                              MtpObjectFormat aFormat,
+                              MtpObjectHandle aParent,
+                              MtpStorageID aStorageID,
+                              uint64_t aSize,
+                              time_t aModified)
+{
+  if (!aParent) {
+    MTP_LOG("aParent is NULL");
+    return kInvalidObjectHandle;
+  }
+
+  RefPtr<DbEntry> entry = new DbEntry;
+
+  entry->mStorageID = aStorageID;
+  entry->mParent = aParent;
+  entry->mPath = aPath;
+  entry->mObjectName = BaseName(entry->mPath);
+  entry->mDisplayName = entry->mObjectName;
+  entry->mObjectFormat = aFormat;
+  entry->mObjectSize = aSize;
+
+  AddEntry(entry);
+
+  MTP_LOG("Handle: 0x%08x Parent: 0x%08x Path: '%s'", entry->mHandle, aParent, aPath);
+
+  return entry->mHandle;
+}
+
+// called to report success or failure of the SendObject file transfer
+// success should signal a notification of the new object's creation,
+// failure should remove the database entry created in beginSendObject
+
+//virtual
+void
+MozMtpDatabase::endSendObject(const char* aPath,
+                            MtpObjectHandle aHandle,
+                            MtpObjectFormat aFormat,
+                            bool succeeded)
+{
+  MTP_LOG("Handle: 0x%08x Path: '%s'", aHandle, aPath);
+  if (!succeeded) {
+    RemoveEntry(aHandle);
+  }
+}
+
+//virtual
+MtpObjectHandleList*
+MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
+                            MtpObjectFormat aFormat,
+                            MtpObjectHandle aParent)
+{
+  MTP_LOG("StorageID: 0x%08x Format: 0x%04x Parent: 0x%08x",
+          aStorageID, aFormat, aParent);
+
+  //TODO: Optimize
+
+  ScopedDeletePtr<MtpObjectHandleList> list;
+
+  list = new MtpObjectHandleList();
+
+  DbArray::size_type numEntries = mDb.Length();
+  DbArray::index_type entryIndex;
+  for (entryIndex = 1; entryIndex < numEntries; entryIndex++) {
+    RefPtr<DbEntry> entry = mDb[entryIndex];
+    if (entry->mParent == aParent) {
+      list->push(entry->mHandle);
+    }
+  }
+  return list.forget();
+}
+
+//virtual
+int
+MozMtpDatabase::getNumObjects(MtpStorageID aStorageID,
+                            MtpObjectFormat aFormat,
+                            MtpObjectHandle aParent)
+{
+  MTP_LOG("");
+
+  return mDb.Length() - 1;
+}
+
+//virtual
+MtpObjectFormatList*
+MozMtpDatabase::getSupportedPlaybackFormats()
+{
+  static const uint16_t init_data[] = {MTP_FORMAT_PNG};
+
+  MtpObjectFormatList *list = new MtpObjectFormatList();
+  list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
+
+  MTP_LOG("returning MTP_FORMAT_PNG");
+  return list;
+}
+
+//virtual
+MtpObjectFormatList*
+MozMtpDatabase::getSupportedCaptureFormats()
+{
+  static const uint16_t init_data[] = {MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG};
+
+  MtpObjectFormatList *list = new MtpObjectFormatList();
+  list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
+  MTP_LOG("returning MTP_FORMAT_PNG");
+  return list;
+}
+
+static const MtpObjectProperty sSupportedObjectProperties[] =
+{
+  MTP_PROPERTY_STORAGE_ID,
+  MTP_PROPERTY_PARENT_OBJECT,
+  MTP_PROPERTY_OBJECT_FORMAT,
+  MTP_PROPERTY_OBJECT_SIZE,
+  MTP_PROPERTY_OBJECT_FILE_NAME,    // just the filename - no directory
+  MTP_PROPERTY_PROTECTION_STATUS,   // UINT16 - always 0
+  MTP_PROPERTY_DATE_MODIFIED,
+  MTP_PROPERTY_DATE_ADDED,
+};
+
+//virtual
+MtpObjectPropertyList*
+MozMtpDatabase::getSupportedObjectProperties(MtpObjectFormat aFormat)
+{
+  MTP_LOG("");
+  MtpObjectPropertyList *list = new MtpObjectPropertyList();
+  list->appendArray(sSupportedObjectProperties,
+                    MOZ_ARRAY_LENGTH(sSupportedObjectProperties));
+  return list;
+}
+
+//virtual
+MtpDevicePropertyList*
+MozMtpDatabase::getSupportedDeviceProperties()
+{
+  MTP_LOG("");
+  static const uint16_t init_data[] = { MTP_DEVICE_PROPERTY_UNDEFINED };
+
+  MtpDevicePropertyList *list = new MtpDevicePropertyList();
+  list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
+  return list;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::getObjectPropertyValue(MtpObjectHandle aHandle,
+                                     MtpObjectProperty aProperty,
+                                     MtpDataPacket& aPacket)
+{
+  RefPtr<DbEntry> entry = GetEntry(aHandle);
+  if (!entry) {
+    MTP_ERR("Invalid Handle: 0x%08x", aHandle);
+    return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+  }
+
+  MTP_LOG("Handle: 0x%08x '%s' Property: %s 0x%08x",
+          aHandle, entry->mDisplayName.get(), ObjectPropertyAsStr(aProperty), aProperty);
+
+  switch (aProperty)
+  {
+    case MTP_PROPERTY_STORAGE_ID:     aPacket.putUInt32(entry->mStorageID); break;
+    case MTP_PROPERTY_PARENT_OBJECT:  aPacket.putUInt32(entry->mParent); break;
+    case MTP_PROPERTY_OBJECT_FORMAT:  aPacket.putUInt32(entry->mObjectFormat); break;
+    case MTP_PROPERTY_OBJECT_SIZE:    aPacket.putUInt32(entry->mObjectSize); break;
+    case MTP_PROPERTY_DISPLAY_NAME:   aPacket.putString(entry->mDisplayName.get()); break;
+
+    default:
+      MTP_LOG("Invalid Property: 0x%08x", aProperty);
+      return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
+  }
+
+  return MTP_RESPONSE_OK;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
+                                     MtpObjectProperty aProperty,
+                                     MtpDataPacket& aPacket)
+{
+  MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
+  return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::getDevicePropertyValue(MtpDeviceProperty aProperty,
+                                     MtpDataPacket& aPacket)
+{
+  MTP_LOG("(GENERAL ERROR)");
+  return MTP_RESPONSE_GENERAL_ERROR;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::setDevicePropertyValue(MtpDeviceProperty aProperty,
+                                     MtpDataPacket& aPacket)
+{
+  MTP_LOG("(NOT SUPPORTED)");
+  return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::resetDeviceProperty(MtpDeviceProperty aProperty)
+{
+  MTP_LOG("(NOT SUPPORTED)");
+  return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+}
+
+void
+MozMtpDatabase::QueryEntries(MozMtpDatabase::MatchType aMatchType,
+                             uint32_t aMatchField1,
+                             uint32_t aMatchField2,
+                             DbArray &result)
+{
+  DbArray::size_type numEntries = mDb.Length();
+  DbArray::index_type entryIdx;
+  RefPtr<DbEntry> entry;
+
+  result.Clear();
+
+  switch (aMatchType) {
+
+    case MatchAll:
+      for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
+        if (mDb[entryIdx]) {
+          result.AppendElement(mDb[entryIdx]);
+        }
+      }
+      break;
+
+    case MatchHandle:
+      for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
+        entry = mDb[entryIdx];
+        if (entry && entry->mHandle == aMatchField1) {
+          result.AppendElement(entry);
+          // Handles are unique - return the one that we found.
+          return;
+        }
+      }
+      break;
+
+    case MatchParent:
+      for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
+        entry = mDb[entryIdx];
+        if (entry && entry->mParent == aMatchField1) {
+          result.AppendElement(entry);
+        }
+      }
+      break;
+
+    case MatchFormat:
+      for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
+        entry = mDb[entryIdx];
+        if (entry && entry->mObjectFormat == aMatchField1) {
+          result.AppendElement(entry);
+        }
+      }
+      break;
+
+    case MatchHandleFormat:
+      for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
+        entry = mDb[entryIdx];
+        if (entry && entry->mHandle == aMatchField1) {
+          if (entry->mObjectFormat == aMatchField2) {
+            result.AppendElement(entry);
+          }
+          // Only 1 entry can match my aHandle. So we can return early.
+          return;
+        }
+      }
+      break;
+
+    case MatchParentFormat:
+      for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
+        entry = mDb[entryIdx];
+        if (entry && entry->mParent == aMatchField1 && entry->mObjectFormat == aMatchField2) {
+          result.AppendElement(entry);
+        }
+      }
+      break;
+
+    default:
+      MOZ_ASSERT(!"Invalid MatchType");
+  }
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
+                                    uint32_t aFormat,
+                                    uint32_t aProperty,
+                                    int aGroupCode,
+                                    int aDepth,
+                                    MtpDataPacket& aPacket)
+{
+  MTP_LOG("Handle: 0x%08x Format: 0x%08x aProperty: 0x%08x aGroupCode: %d aDepth %d (NOT SUPPORTED)",
+          aHandle, aFormat, aProperty, aGroupCode, aDepth);
+
+  if (aDepth > 1) {
+    return MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED;
+  }
+  if (aGroupCode != 0) {
+    return MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED;
+  }
+
+  MatchType matchType = MatchAll;
+  uint32_t matchField1 = 0;
+  uint32_t matchField2 = 0;
+
+  // aHandle == 0 implies all objects at the root level
+  // further specificed by aFormat and/or aDepth
+
+  if (aFormat == 0) {
+    if (aHandle == 0xffffffff) {
+      // select all objects
+      matchType = MatchAll;
+    } else {
+      if (aDepth == 1) {
+        // select objects whose Parent matches aHandle
+        matchType = MatchParent;
+        matchField1 = aHandle;
+      } else {
+        // select object whose handle matches aHandle
+        matchType = MatchHandle;
+        matchField1 = aHandle;
+      }
+    }
+  } else {
+    if (aHandle == 0xffffffff) {
+      // select all objects whose format matches aFormat
+      matchType = MatchFormat;
+      matchField1 = aFormat;
+    } else {
+      if (aDepth == 1) {
+        // select objects whose Parent is aHandle and format matches aFormat
+        matchType = MatchParentFormat;
+        matchField1 = aHandle;
+        matchField2 = aFormat;
+      } else {
+        // select objects whose handle is aHandle and format matches aFormat
+        matchType = MatchHandleFormat;
+        matchField1 = aHandle;
+        matchField2 = aFormat;
+      }
+    }
+  }
+
+  DbArray result;
+  QueryEntries(matchType, matchField1, matchField2, result);
+
+  const MtpObjectProperty *objectPropertyList;
+  size_t numObjectProperties = 0;
+  MtpObjectProperty objectProperty;
+
+  if (aProperty == 0xffffffff) {
+    // return all supported properties
+    numObjectProperties = MOZ_ARRAY_LENGTH(sSupportedObjectProperties);
+    objectPropertyList = sSupportedObjectProperties;
+  } else {
+    // return property indicated by aProperty
+    numObjectProperties = 1;
+    objectProperty = aProperty;
+    objectPropertyList = &objectProperty;
+  }
+
+  DbArray::size_type numEntries = result.Length();
+  DbArray::index_type entryIdx;
+
+  aPacket.putUInt32(numEntries);
+  for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
+    RefPtr<DbEntry> entry = result[entryIdx];
+
+    for (size_t propertyIdx = 0; propertyIdx < numObjectProperties; propertyIdx++) {
+      aPacket.putUInt32(entry->mHandle);
+      MtpObjectProperty prop = objectPropertyList[propertyIdx];
+      aPacket.putUInt16(prop);
+      switch (prop) {
+
+        case MTP_PROPERTY_STORAGE_ID:
+          aPacket.putUInt16(MTP_TYPE_UINT32);
+          aPacket.putUInt32(entry->mStorageID);
+          break;
+
+        case MTP_PROPERTY_PARENT_OBJECT:
+          aPacket.putUInt16(MTP_TYPE_UINT32);
+          aPacket.putUInt32(entry->mParent);
+          break;
+
+        case MTP_PROPERTY_OBJECT_FORMAT:
+          aPacket.putUInt16(MTP_TYPE_UINT16);
+          aPacket.putUInt16(entry->mObjectFormat);
+          break;
+
+        case MTP_PROPERTY_OBJECT_SIZE:
+          aPacket.putUInt16(MTP_TYPE_UINT64);
+          aPacket.putUInt64(entry->mObjectSize);
+          break;
+
+        case MTP_PROPERTY_OBJECT_FILE_NAME:
+          aPacket.putUInt16(MTP_TYPE_STR);
+          aPacket.putString(entry->mObjectName.get());
+          break;
+
+        case MTP_PROPERTY_PROTECTION_STATUS:
+          aPacket.putUInt16(MTP_TYPE_UINT16);
+          aPacket.putUInt16(0); // 0 = No Protection
+          break;
+
+        case MTP_PROPERTY_DATE_MODIFIED: {
+          aPacket.putUInt16(MTP_TYPE_STR);
+          PRExplodedTime explodedTime;
+          PR_ExplodeTime(entry->mDateModified, PR_LocalTimeParameters, &explodedTime);
+          char dateStr[20];
+          PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
+          aPacket.putString(dateStr);
+          break;
+        }
+
+        case MTP_PROPERTY_DATE_ADDED: {
+          aPacket.putUInt16(MTP_TYPE_STR);
+          PRExplodedTime explodedTime;
+          PR_ExplodeTime(entry->mDateCreated, PR_LocalTimeParameters, &explodedTime);
+          char dateStr[20];
+          PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
+          aPacket.putString(dateStr);
+          break;
+        }
+
+        default:
+          MTP_ERR("Unrecognized property code: %u", prop);
+          return MTP_RESPONSE_GENERAL_ERROR;
+      }
+    }
+  }
+  return MTP_RESPONSE_OK;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::getObjectInfo(MtpObjectHandle aHandle,
+                            MtpObjectInfo& aInfo)
+{
+  RefPtr<DbEntry> entry = GetEntry(aHandle);
+  if (!entry) {
+    MTP_ERR("Handle 0x%08x is invalid", aHandle);
+    return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+  }
+
+  MTP_LOG("Handle: 0x%08x Display:'%s' Object:'%s'", aHandle, entry->mDisplayName.get(), entry->mObjectName.get());
+
+  aInfo.mHandle = aHandle;
+  aInfo.mStorageID = entry->mStorageID;
+  aInfo.mFormat = entry->mObjectFormat;
+  aInfo.mProtectionStatus = 0x0;
+  aInfo.mCompressedSize = 0;
+  aInfo.mThumbFormat = entry->mObjectFormat;
+  aInfo.mThumbCompressedSize = 20*20*4;
+  aInfo.mThumbPixWidth = 20;
+  aInfo.mThumbPixHeight  =20;
+  aInfo.mImagePixWidth = 20;
+  aInfo.mImagePixHeight = 20;
+  aInfo.mImagePixDepth = 4;
+  aInfo.mParent = entry->mParent;
+  aInfo.mAssociationType = 0;
+  aInfo.mAssociationDesc = 0;
+  aInfo.mSequenceNumber = 0;
+  aInfo.mName = ::strdup(entry->mObjectName.get());
+  aInfo.mDateCreated = 0;
+  aInfo.mDateModified = 0;
+  aInfo.mKeywords = ::strdup("fxos,touch");
+
+  return MTP_RESPONSE_OK;
+}
+
+//virtual
+void*
+MozMtpDatabase::getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize)
+{
+  MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
+
+  aOutThumbSize = 0;
+
+  return nullptr;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::getObjectFilePath(MtpObjectHandle aHandle,
+                                MtpString& aOutFilePath,
+                                int64_t& aOutFileLength,
+                                MtpObjectFormat& aOutFormat)
+{
+  RefPtr<DbEntry> entry = GetEntry(aHandle);
+  if (!entry) {
+    MTP_ERR("Handle 0x%08x is invalid", aHandle);
+    return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+  }
+
+  MTP_LOG("Handle: 0x%08x FilePath: '%s'", aHandle, entry->mPath.get());
+
+  aOutFilePath = entry->mPath.get();
+  aOutFileLength = entry->mObjectSize;
+  aOutFormat = entry->mObjectFormat;
+
+  return MTP_RESPONSE_OK;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::deleteFile(MtpObjectHandle aHandle)
+{
+  MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
+
+  //TODO
+
+  return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+}
+
+#if 0
+//virtual
+MtpResponseCode
+MozMtpDatabase::moveFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
+{
+  MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
+
+  // change parent
+
+  return MTP_RESPONSE_OK
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::copyFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
+{
+  MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
+
+  // duplicate DbEntry
+  // change parent
+
+  return MTP_RESPONSE_OK
+}
+#endif
+
+//virtual
+MtpObjectHandleList*
+MozMtpDatabase::getObjectReferences(MtpObjectHandle aHandle)
+{
+  MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
+  return nullptr;
+}
+
+//virtual
+MtpResponseCode
+MozMtpDatabase::setObjectReferences(MtpObjectHandle aHandle,
+                                  MtpObjectHandleList* aReferences)
+{
+  MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
+  return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+}
+
+//virtual
+MtpProperty*
+MozMtpDatabase::getObjectPropertyDesc(MtpObjectProperty aProperty,
+                                      MtpObjectFormat aFormat)
+{
+  MTP_LOG("Property: %s 0x%08x", ObjectPropertyAsStr(aProperty), aProperty);
+
+  // TODO: Perhaps Filesize should be 64-bit?
+
+  MtpProperty* result = nullptr;
+  switch (aProperty)
+  {
+    case MTP_PROPERTY_STORAGE_ID:       result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
+    case MTP_PROPERTY_OBJECT_FORMAT:    result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
+    case MTP_PROPERTY_OBJECT_SIZE:      result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
+    case MTP_PROPERTY_WIDTH:            result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
+    case MTP_PROPERTY_HEIGHT:           result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
+    case MTP_PROPERTY_IMAGE_BIT_DEPTH:  result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
+    case MTP_PROPERTY_DISPLAY_NAME:     result = new MtpProperty(aProperty, MTP_TYPE_STR); break;
+    default:
+      break;
+  }
+
+  return result;
+}
+
+//virtual
+MtpProperty*
+MozMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty aProperty)
+{
+  MTP_LOG("(returning MTP_DEVICE_PROPERTY_UNDEFINED)");
+  return new MtpProperty(MTP_DEVICE_PROPERTY_UNDEFINED, MTP_TYPE_UNDEFINED);
+}
+
+//virtual
+void
+MozMtpDatabase::sessionStarted()
+{
+  MTP_LOG("");
+}
+
+//virtual
+void
+MozMtpDatabase::sessionEnded()
+{
+  MTP_LOG("");
+}
+
+END_MTP_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/MozMtpDatabase.h
@@ -0,0 +1,161 @@
+/* -*- 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_system_mozmtpdatabase_h__
+#define mozilla_system_mozmtpdatabase_h__
+
+#include "MozMtpCommon.h"
+
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+BEGIN_MTP_NAMESPACE // mozilla::system::mtp
+
+using namespace android;
+
+class MozMtpDatabase : public MtpDatabase
+{
+public:
+  MozMtpDatabase(const char *aDir);
+  virtual ~MozMtpDatabase();
+
+  // called from SendObjectInfo to reserve a database entry for the incoming file
+  virtual MtpObjectHandle beginSendObject(const char* aPath,
+                                          MtpObjectFormat aFormat,
+                                          MtpObjectHandle aParent,
+                                          MtpStorageID aStorageID,
+                                          uint64_t aSize,
+                                          time_t aModified);
+
+  // called to report success or failure of the SendObject file transfer
+  // success should signal a notification of the new object's creation,
+  // failure should remove the database entry created in beginSendObject
+  virtual void endSendObject(const char* aPath,
+                             MtpObjectHandle aHandle,
+                             MtpObjectFormat aFormat,
+                             bool aSucceeded);
+
+  virtual MtpObjectHandleList* getObjectList(MtpStorageID aStorageID,
+                                             MtpObjectFormat aFormat,
+                                             MtpObjectHandle aParent);
+
+  virtual int getNumObjects(MtpStorageID aStorageID,
+                            MtpObjectFormat aFormat,
+                            MtpObjectHandle aParent);
+
+  virtual MtpObjectFormatList* getSupportedPlaybackFormats();
+
+  virtual MtpObjectFormatList* getSupportedCaptureFormats();
+
+  virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat aFormat);
+
+  virtual MtpDevicePropertyList* getSupportedDeviceProperties();
+
+  virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle aHandle,
+                                                 MtpObjectProperty aProperty,
+                                                 MtpDataPacket& aPacket);
+
+  virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle aHandle,
+                                                 MtpObjectProperty aProperty,
+                                                 MtpDataPacket& aPacket);
+
+  virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty aProperty,
+                                                 MtpDataPacket& aPacket);
+
+  virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty aProperty,
+                                                 MtpDataPacket& aPacket);
+
+  virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty aProperty);
+
+  virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle aHandle,
+                                                uint32_t aFormat,
+                                                uint32_t aProperty,
+                                                int aGroupCode,
+                                                int aDepth,
+                                                MtpDataPacket& aPacket);
+
+  virtual MtpResponseCode getObjectInfo(MtpObjectHandle aHandle,
+                                        MtpObjectInfo& aInfo);
+
+  virtual void* getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize);
+
+  virtual MtpResponseCode getObjectFilePath(MtpObjectHandle aHandle,
+                                            MtpString& aOutFilePath,
+                                            int64_t& aOutFileLength,
+                                            MtpObjectFormat& aOutFormat);
+
+  virtual MtpResponseCode deleteFile(MtpObjectHandle aHandle);
+
+  virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle aHandle);
+
+  virtual MtpResponseCode setObjectReferences(MtpObjectHandle aHandle,
+                                              MtpObjectHandleList* aReferences);
+
+  virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty aProperty,
+                                             MtpObjectFormat aFormat);
+
+  virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty aProperty);
+
+  virtual void sessionStarted();
+
+  virtual void sessionEnded();
+
+private:
+
+  struct DbEntry
+  {
+    NS_INLINE_DECL_REFCOUNTING(DbEntry)
+
+    MtpObjectHandle mHandle;        // uint32_t
+    MtpStorageID    mStorageID;     // uint32_t
+    nsCString       mObjectName;
+    MtpObjectFormat mObjectFormat;  // uint16_t
+    MtpObjectHandle mParent;        // uint32_t
+    uint64_t        mObjectSize;
+    nsCString       mDisplayName;
+    nsCString       mPath;
+    PRTime          mDateCreated;
+    PRTime          mDateModified;
+  };
+  typedef nsTArray<mozilla::RefPtr<DbEntry> > DbArray;
+
+  DbArray mDb;
+
+  enum MatchType
+  {
+    MatchAll,
+    MatchHandle,
+    MatchParent,
+    MatchFormat,
+    MatchHandleFormat,
+    MatchParentFormat,
+  };
+
+
+  void AddEntry(DbEntry *aEntry);
+  mozilla::TemporaryRef<DbEntry> GetEntry(MtpObjectHandle aHandle);
+  void RemoveEntry(MtpObjectHandle aHandle);
+  void QueryEntries(MatchType aMatchType, uint32_t aMatchField1,
+                    uint32_t aMatchField2, DbArray& aResult);
+
+  nsCString BaseName(const nsCString& aPath);
+
+
+  MtpObjectHandle GetNextHandle()
+  {
+    return mDb.Length();
+  }
+
+  void ParseDirectory(const char *aDir, MtpObjectHandle aParent);
+  void ReadVolume(const char *aVolumeName, const char *aDir);
+};
+
+END_MTP_NAMESPACE
+
+#endif // mozilla_system_mozmtpdatabase_h__
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/MozMtpServer.cpp
@@ -0,0 +1,80 @@
+/* -*- 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 "MozMtpServer.h"
+#include "MozMtpDatabase.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+
+#include "mozilla/FileUtils.h"
+#include "mozilla/Scoped.h"
+#include "nsThreadUtils.h"
+
+using namespace android;
+using namespace mozilla;
+USING_MTP_NAMESPACE
+
+class MtpServerRunnable : public nsRunnable
+{
+public:
+  nsresult Run()
+  {
+    const char *mtpUsbFilename = "/dev/mtp_usb";
+    const char *productName = "FirefoxOS";
+    const char *storageDir = "/storage/sdcard";
+
+    mFd = open(mtpUsbFilename, O_RDWR);
+    if (mFd.get() < 0) {
+      MTP_LOG("open of '%s' failed", mtpUsbFilename);
+      return NS_OK;
+    }
+
+    MTP_LOG("MozMtpServer open done, fd: %d. Start reading.", mFd.get());
+
+    ScopedDeletePtr<MozMtpDatabase> database;
+    ScopedDeletePtr<MtpServer> server;
+    ScopedDeletePtr<MtpStorage> storage;
+
+    database = new MozMtpDatabase(storageDir);
+    server = new MtpServer(mFd.get(), database, false, 1023, 0664, 0775);
+    storage = new MtpStorage(MTP_STORAGE_FIXED_RAM,       // id
+                             storageDir,                  // filePath
+                             productName,                 // description
+                             100uLL * 1024uLL * 1024uLL,  // reserveSpace
+                             false,                       // removable
+                             2uLL * 1024uLL * 1024uLL * 1024uLL); // maxFileSize
+
+    server->addStorage(storage);
+
+    MTP_LOG("MozMtpServer started");
+    server->run();
+    MTP_LOG("MozMtpServer finished");
+
+    return NS_OK;
+  }
+
+private:
+  ScopedClose mFd;
+};
+
+void
+MozMtpServer::Run()
+{
+  nsresult rv = NS_NewNamedThread("MtpServer", getter_AddRefs(mServerThread));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+  MOZ_ASSERT(mServerThread);
+  mServerThread->Dispatch(new MtpServerRunnable(), 0);
+}
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/MozMtpServer.h
@@ -0,0 +1,32 @@
+/* -*- 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_system_mozmtpserver_h__
+#define mozilla_system_mozmtpserver_h__
+
+#include "MozMtpCommon.h"
+
+#include "nsCOMPtr.h"
+#include "nsIThread.h"
+
+BEGIN_MTP_NAMESPACE
+
+class MozMtpServer
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(MozMtpServer)
+
+  void Run();
+
+private:
+  nsCOMPtr<nsIThread> mServerThread;
+};
+
+END_MTP_NAMESPACE
+
+#endif  // mozilla_system_mozmtpserver_h__
+
+
--- a/dom/system/gonk/moz.build
+++ b/dom/system/gonk/moz.build
@@ -36,32 +36,39 @@ EXPORTS += [
     'nsVolumeService.h',
 ]
 SOURCES += [
     'AudioChannelManager.cpp',
     'AudioManager.cpp',
     'AutoMounter.cpp',
     'AutoMounterSetting.cpp',
     'GonkGPSGeolocationProvider.cpp',
+    'MozMtpDatabase.cpp',
+    'MozMtpServer.cpp',
     'NetworkUtils.cpp',
     'NetworkWorker.cpp',
     'nsVolume.cpp',
     'nsVolumeMountLock.cpp',
     'nsVolumeService.cpp',
     'nsVolumeStat.cpp',
     'OpenFileFinder.cpp',
     'SystemWorkerManager.cpp',
     'TimeZoneSettingObserver.cpp',
     'Volume.cpp',
     'VolumeCommand.cpp',
     'VolumeManager.cpp',
     'VolumeServiceIOThread.cpp',
     'VolumeServiceTest.cpp',
 ]
 
+if CONFIG['ANDROID_VERSION'] >= '17':
+    CXXFLAGS += ['-I%s/frameworks/av/media/mtp' % CONFIG['ANDROID_SOURCE']]
+else:
+    CXXFLAGS += ['-I%s/frameworks/base/media/mtp' % CONFIG['ANDROID_SOURCE']]
+
 if CONFIG['ENABLE_TESTS']:
     XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
 
 EXTRA_COMPONENTS += [
     'NetworkInterfaceListService.js',
     'NetworkInterfaceListService.manifest',
     'NetworkManager.manifest',
     'NetworkService.js',
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -1141,16 +1141,17 @@ var WifiManager = (function() {
         }
 
         // Driver startup on certain platforms takes longer than it takes
         // for us to return from loadDriver, so wait 2 seconds before
         // turning on Wifi tethering.
         createWaitForDriverReadyTimer(doStartWifiTethering);
       });
     } else {
+      cancelWifiHotspotStatusTimer();
       gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
                                        configuration, function(result) {
         // Should we fire a dom event if we fail to set wifi tethering  ?
         debug("Disable Wifi tethering result: " + (result ? result : "successfully"));
         // Unload wifi driver even if we fail to control wifi tethering.
         unloadDriver(WIFI_FIRMWARE_AP, function(status) {
           if (status < 0) {
             debug("Fail to unload wifi driver");
--- a/toolkit/library/libxul.mk
+++ b/toolkit/library/libxul.mk
@@ -129,16 +129,17 @@ OS_LIBS += \
   -lsysutils \
   -lcamera_client \
   -lsensorservice \
   -lstagefright \
   -lstagefright_foundation \
   -lstagefright_omx \
   -lbinder \
   -lgui \
+  -lmtp \
   $(NULL)
 endif
 
 ifneq (,$(filter rtsp,$(NECKO_PROTOCOLS)))
 OS_LIBS += -lstagefright_foundation
 endif
 
 ifdef MOZ_WMF