author | Wes Kocher <wkocher@mozilla.com> |
Wed, 16 Jul 2014 17:22:55 -0700 | |
changeset 194489 | a74600665875202a1990416598444deb807d3a5f |
parent 194425 | 8e8f3ba646554c13de1f26140d8a954b79d7549b (current diff) |
parent 194488 | a0873e56568fe86c562fda2ba4d7ad011383c00d (diff) |
child 194490 | c2c93cba158730458663c5816df14f03cc24a72a |
child 194532 | 5dbfea1a872395ff328c5b9df17c1eef931b71a5 |
child 194601 | ec7f2245280cc062085966e138f67ba4342e2b7b |
push id | 27146 |
push user | kwierso@gmail.com |
push date | Thu, 17 Jul 2014 00:23:21 +0000 |
treeherder | mozilla-central@a74600665875 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 33.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
33.0a1
/
20140717030339
/
pushlog to previous
nightly linux64
33.0a1
/
20140717030339
/
pushlog to previous
nightly mac
33.0a1
/
20140717030339
/
pushlog to previous
nightly win32
33.0a1
/
20140717030339
/
pushlog to previous
nightly win64
33.0a1
/
20140717030339
/
pushlog to previous
|
b2g/app/b2g.js | file | annotate | diff | comparison | revisions |
--- 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