mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 12:55:46 +00:00
Merge b2g-inbound to m-c. a=merge
This commit is contained in:
commit
816f9fa0e5
@ -107,7 +107,7 @@ const Observer = {
|
||||
|
||||
};
|
||||
|
||||
const AppFrames = this.AppFrames = {
|
||||
let AppFrames = this.AppFrames = {
|
||||
|
||||
list: () => SystemAppProxy.getAppFrames(),
|
||||
|
||||
|
@ -19,8 +19,8 @@
|
||||
<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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<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"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<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"/>
|
||||
|
@ -19,8 +19,8 @@
|
||||
<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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<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"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
@ -121,7 +121,7 @@
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="a928d2ae29244dfec3753ea695e6b98e38e8849a"/>
|
||||
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="30a441fb7275fc5bc347f84ccb29e977a7eca34e"/>
|
||||
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="30d40a5636ff28a554f1d8e9d975bfd04c2463d3"/>
|
||||
<project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="2b1d8b5b7a760230f4c94c02e733e3929f44253a"/>
|
||||
<project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="e81502511cda303c803e63f049574634bc96f9f2"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="81c4a859d75d413ad688067829d21b7ba9205f81"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "056dbe15b2aac2b252a119c211a85cb14165aa81",
|
||||
"revision": "ccd8b59df6230fc0eb5d47e9c617e58ddb4673dd",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,8 +17,8 @@
|
||||
<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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<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"/>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<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"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
<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="09642e74e250fbc62db860c808ef188628fca55d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<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"/>
|
||||
|
@ -702,6 +702,7 @@ GK_ATOM(oncut, "oncut")
|
||||
GK_ATOM(ondatachange, "ondatachange")
|
||||
GK_ATOM(ondataerror, "ondataerror")
|
||||
GK_ATOM(ondblclick, "ondblclick")
|
||||
GK_ATOM(ondeleted, "ondeleted")
|
||||
GK_ATOM(ondeliverysuccess, "ondeliverysuccess")
|
||||
GK_ATOM(ondeliveryerror, "ondeliveryerror")
|
||||
GK_ATOM(ondevicefound, "ondevicefound")
|
||||
|
@ -4,9 +4,12 @@
|
||||
* 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 <sys/socket.h>
|
||||
#include "base/message_loop.h"
|
||||
#include "BluetoothInterface.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
@ -69,6 +72,40 @@ private:
|
||||
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
|
||||
//
|
||||
@ -84,23 +121,432 @@ struct interface_traits<BluetoothSocketInterface>
|
||||
}
|
||||
};
|
||||
|
||||
bt_status_t
|
||||
BluetoothSocketInterface::Listen(btsock_type_t aType,
|
||||
const char* aServiceName,
|
||||
const uint8_t* aServiceUuid, int aChannel,
|
||||
int& aSockFd, int aFlags)
|
||||
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)
|
||||
{
|
||||
return mInterface->listen(aType, aServiceName, aServiceUuid, aChannel,
|
||||
&aSockFd, aFlags);
|
||||
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;
|
||||
}
|
||||
|
||||
bt_status_t
|
||||
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 aFlags,
|
||||
BluetoothSocketResultHandler* aRes)
|
||||
{
|
||||
int fd;
|
||||
|
||||
bt_status_t status = mInterface->listen(aType, aServiceName, aServiceUuid,
|
||||
aChannel, &fd, aFlags);
|
||||
if (aRes) {
|
||||
DispatchBluetoothSocketResult(aRes, &BluetoothSocketResultHandler::Listen,
|
||||
fd, status);
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
return mInterface->connect(aBdAddr, aType, aUuid, aChannel, &aSockFd,
|
||||
aFlags);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
{
|
||||
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(
|
||||
|
@ -24,6 +24,25 @@ 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:
|
||||
@ -31,13 +50,15 @@ public:
|
||||
|
||||
// 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);
|
||||
|
@ -18,9 +18,6 @@
|
||||
#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
|
||||
|
||||
@ -48,61 +45,55 @@ EnsureBluetoothSocketHalLoad()
|
||||
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,
|
||||
@ -110,12 +101,12 @@ public:
|
||||
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());
|
||||
}
|
||||
@ -159,24 +150,20 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,11 +173,6 @@ public:
|
||||
*/
|
||||
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
|
||||
@ -208,14 +190,10 @@ private:
|
||||
*/
|
||||
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.
|
||||
@ -232,6 +210,7 @@ private:
|
||||
int mChannel;
|
||||
bool mAuth;
|
||||
bool mEncrypt;
|
||||
ConnectionStatus mConnectionStatus;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
@ -253,6 +232,70 @@ 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:
|
||||
@ -392,32 +435,40 @@ private:
|
||||
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
|
||||
@ -434,127 +485,83 @@ public:
|
||||
};
|
||||
|
||||
void
|
||||
DroidSocketImpl::Connect()
|
||||
DroidSocketImpl::Connect(int aFd)
|
||||
{
|
||||
MOZ_ASSERT(sBluetoothSocketInterface);
|
||||
MOZ_ASSERT(aFd >= 0);
|
||||
|
||||
bt_bdaddr_t remoteBdAddress;
|
||||
StringToBdAddressType(mDeviceAddress, &remoteBdAddress);
|
||||
|
||||
// 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);
|
||||
MOZ_ASSERT(aFd >= 0);
|
||||
|
||||
// TODO: uuid and service name as arguments
|
||||
|
||||
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];
|
||||
Close();
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&iv, 0, sizeof(iv));
|
||||
int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
|
||||
NS_ENSURE_TRUE_VOID(flags >= 0);
|
||||
|
||||
iv.iov_base = (unsigned char *)aBuffer;
|
||||
iv.iov_len = aLength;
|
||||
|
||||
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);
|
||||
SetFd(aFd);
|
||||
mConnectionStatus = SOCKET_IS_CONNECTED;
|
||||
|
||||
// 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);
|
||||
nsRefPtr<OnSocketEventRunnable> r =
|
||||
new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
|
||||
NS_DispatchToMainThread(r);
|
||||
|
||||
// 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;
|
||||
}
|
||||
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);
|
||||
@ -563,12 +570,7 @@ DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
|
||||
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) {
|
||||
@ -606,8 +608,113 @@ DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
|
||||
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);
|
||||
@ -649,6 +756,28 @@ DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -657,7 +786,6 @@ BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
|
||||
, mImpl(nullptr)
|
||||
, mAuth(aAuth)
|
||||
, mEncrypt(aEncrypt)
|
||||
, mReceivedSocketInfoLength(0)
|
||||
{
|
||||
MOZ_ASSERT(aObserver);
|
||||
|
||||
@ -681,35 +809,107 @@ BluetoothSocket::CloseDroidSocket()
|
||||
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;
|
||||
}
|
||||
|
||||
@ -725,64 +925,9 @@ BluetoothSocket::SendDroidSocketData(UnixSocketRawData* 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);
|
||||
@ -811,4 +956,3 @@ BluetoothSocket::OnDisconnect()
|
||||
MOZ_ASSERT(mObserver);
|
||||
mObserver->OnSocketDisconnect(this);
|
||||
}
|
||||
|
||||
|
@ -23,30 +23,8 @@ public:
|
||||
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()
|
||||
@ -65,6 +43,11 @@ public:
|
||||
aDeviceAddress = mDeviceAddress;
|
||||
}
|
||||
|
||||
inline void SetAddress(const nsAString& aDeviceAddress)
|
||||
{
|
||||
mDeviceAddress = aDeviceAddress;
|
||||
}
|
||||
|
||||
void CloseDroidSocket();
|
||||
bool SendDroidSocketData(mozilla::ipc::UnixSocketRawData* aData);
|
||||
|
||||
@ -74,10 +57,6 @@ private:
|
||||
nsString mDeviceAddress;
|
||||
bool mAuth;
|
||||
bool mEncrypt;
|
||||
bool mIsServer;
|
||||
int mReceivedSocketInfoLength;
|
||||
|
||||
bool ReceiveSocketInfo(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage);
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
@ -249,6 +249,10 @@ const kEventConstructors = {
|
||||
return new MozEmergencyCbModeEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
MozMessageDeletedEvent: { create: function (aName, aProps) {
|
||||
return new MozMessageDeletedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
MozMmsEvent: { create: function (aName, aProps) {
|
||||
return new MozMmsEvent(aName, aProps);
|
||||
},
|
||||
|
@ -5,6 +5,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDeletedMessageInfo.idl',
|
||||
'nsIDOMMozMmsMessage.idl',
|
||||
'nsIDOMMozMobileMessageThread.idl',
|
||||
'nsIDOMMozSmsMessage.idl',
|
||||
|
17
dom/mobilemessage/interfaces/nsIDeletedMessageInfo.idl
Normal file
17
dom/mobilemessage/interfaces/nsIDeletedMessageInfo.idl
Normal file
@ -0,0 +1,17 @@
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsIVariant;
|
||||
|
||||
[scriptable, builtinclass, uuid(174139d8-00e1-11e4-818a-f38357d90920)]
|
||||
interface nsIDeletedMessageInfo : nsISupports
|
||||
{
|
||||
/* Array of messages ids in long if available. */
|
||||
readonly attribute nsIVariant deletedMessageIds;
|
||||
|
||||
/* Array of thread ids in unsigned long long. if available */
|
||||
readonly attribute nsIVariant deletedThreadIds;
|
||||
};
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDeletedMessageInfo;
|
||||
interface nsIDOMMozSmsMessage;
|
||||
interface nsIDOMMozMmsMessage;
|
||||
interface nsIDOMMozMobileMessageThread;
|
||||
@ -14,7 +15,7 @@ interface nsIDOMMozSmsSegmentInfo;
|
||||
#define MOBILE_MESSAGE_SERVICE_CONTRACTID "@mozilla.org/mobilemessage/mobilemessageservice;1"
|
||||
%}
|
||||
|
||||
[scriptable, builtinclass, uuid(17fce9e4-af56-11e3-83d9-b71055e95493)]
|
||||
[scriptable, builtinclass, uuid(afb83948-003a-11e4-899b-bb8a8d45e7f0)]
|
||||
interface nsIMobileMessageService : nsISupports
|
||||
{
|
||||
[implicit_jscontext]
|
||||
@ -61,4 +62,10 @@ interface nsIMobileMessageService : nsISupports
|
||||
in DOMString body,
|
||||
in unsigned long long unreadCount,
|
||||
in DOMString aLastMessageType);
|
||||
|
||||
nsIDeletedMessageInfo createDeletedMessageInfo(
|
||||
[array, size_is(msgCount)] in long messageIds,
|
||||
in uint32_t msgCount,
|
||||
[array, size_is(threadCount)] in unsigned long long threadIds,
|
||||
in uint32_t threadCount);
|
||||
};
|
||||
|
@ -17,6 +17,7 @@ const char* kSmsDeliveryErrorObserverTopic = "sms-delivery-error";
|
||||
const char* kSilentSmsReceivedObserverTopic = "silent-sms-received";
|
||||
const char* kSmsReadSuccessObserverTopic = "sms-read-success";
|
||||
const char* kSmsReadErrorObserverTopic = "sms-read-error";
|
||||
const char* kSmsDeletedObserverTopic = "sms-deleted";
|
||||
|
||||
} // namespace mobilemessage
|
||||
} // namespace dom
|
||||
|
@ -21,6 +21,7 @@ extern const char* kSmsDeliveryErrorObserverTopic;
|
||||
extern const char* kSilentSmsReceivedObserverTopic;
|
||||
extern const char* kSmsReadSuccessObserverTopic;
|
||||
extern const char* kSmsReadErrorObserverTopic;
|
||||
extern const char* kSmsDeletedObserverTopic;
|
||||
|
||||
#define DELIVERY_RECEIVED NS_LITERAL_STRING("received")
|
||||
#define DELIVERY_SENDING NS_LITERAL_STRING("sending")
|
||||
|
124
dom/mobilemessage/src/DeletedMessageInfo.cpp
Normal file
124
dom/mobilemessage/src/DeletedMessageInfo.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "DeletedMessageInfo.h"
|
||||
#include "nsComponentManagerUtils.h" // for do_CreateInstance
|
||||
#include "nsVariant.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace mobilemessage {
|
||||
|
||||
NS_IMPL_ISUPPORTS(DeletedMessageInfo, nsIDeletedMessageInfo)
|
||||
|
||||
DeletedMessageInfo::DeletedMessageInfo(const DeletedMessageInfoData& aData)
|
||||
: mData(aData)
|
||||
{
|
||||
}
|
||||
|
||||
DeletedMessageInfo::DeletedMessageInfo(int32_t* aMessageIds,
|
||||
uint32_t aMsgCount,
|
||||
uint64_t* aThreadIds,
|
||||
uint32_t aThreadCount)
|
||||
{
|
||||
mData.deletedMessageIds().AppendElements(aMessageIds, aMsgCount);
|
||||
mData.deletedThreadIds().AppendElements(aThreadIds, aThreadCount);
|
||||
}
|
||||
|
||||
DeletedMessageInfo::~DeletedMessageInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
DeletedMessageInfo::Create(int32_t* aMessageIds,
|
||||
uint32_t aMsgCount,
|
||||
uint64_t* aThreadIds,
|
||||
uint32_t aThreadCount,
|
||||
nsIDeletedMessageInfo** aDeletedInfo)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDeletedInfo);
|
||||
NS_ENSURE_TRUE(aMsgCount || aThreadCount, NS_ERROR_INVALID_ARG);
|
||||
|
||||
nsCOMPtr<nsIDeletedMessageInfo> deletedInfo =
|
||||
new DeletedMessageInfo(aMessageIds,
|
||||
aMsgCount,
|
||||
aThreadIds,
|
||||
aThreadCount);
|
||||
deletedInfo.forget(aDeletedInfo);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DeletedMessageInfo::GetDeletedMessageIds(nsIVariant** aDeletedMessageIds)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDeletedMessageIds);
|
||||
|
||||
if (mDeletedMessageIds) {
|
||||
NS_ADDREF(*aDeletedMessageIds = mDeletedMessageIds);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t length = mData.deletedMessageIds().Length();
|
||||
|
||||
if (length == 0) {
|
||||
*aDeletedMessageIds = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
mDeletedMessageIds = do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDeletedMessageIds->SetAsArray(nsIDataType::VTYPE_INT32,
|
||||
nullptr,
|
||||
length,
|
||||
mData.deletedMessageIds().Elements());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mDeletedMessageIds->SetWritable(false);
|
||||
|
||||
NS_ADDREF(*aDeletedMessageIds = mDeletedMessageIds);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DeletedMessageInfo::GetDeletedThreadIds(nsIVariant** aDeletedThreadIds)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDeletedThreadIds);
|
||||
|
||||
if (mDeletedThreadIds) {
|
||||
NS_ADDREF(*aDeletedThreadIds = mDeletedThreadIds);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t length = mData.deletedThreadIds().Length();
|
||||
|
||||
if (length == 0) {
|
||||
*aDeletedThreadIds = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
mDeletedThreadIds = do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDeletedThreadIds->SetAsArray(nsIDataType::VTYPE_UINT64,
|
||||
nullptr,
|
||||
length,
|
||||
mData.deletedThreadIds().Elements());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mDeletedThreadIds->SetWritable(false);
|
||||
|
||||
NS_ADDREF(*aDeletedThreadIds = mDeletedThreadIds);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mobilemessage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
58
dom/mobilemessage/src/DeletedMessageInfo.h
Normal file
58
dom/mobilemessage/src/DeletedMessageInfo.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_mobilemessage_DeletedMessageInfo_h
|
||||
#define mozilla_dom_mobilemessage_DeletedMessageInfo_h
|
||||
|
||||
#include "mozilla/dom/mobilemessage/SmsTypes.h"
|
||||
#include "nsIDeletedMessageInfo.h"
|
||||
|
||||
class nsIWritableVariant;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace mobilemessage {
|
||||
|
||||
class DeletedMessageInfo MOZ_FINAL : public nsIDeletedMessageInfo
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDELETEDMESSAGEINFO
|
||||
|
||||
DeletedMessageInfo(const DeletedMessageInfoData& aData);
|
||||
|
||||
DeletedMessageInfo(int32_t* aMessageIds,
|
||||
uint32_t aMsgCount,
|
||||
uint64_t* aThreadIds,
|
||||
uint32_t aThreadCount);
|
||||
|
||||
static nsresult Create(int32_t* aMessageIds,
|
||||
uint32_t aMsgCount,
|
||||
uint64_t* aThreadIds,
|
||||
uint32_t aThreadCount,
|
||||
nsIDeletedMessageInfo** aDeletedInfo);
|
||||
|
||||
const DeletedMessageInfoData& GetData() const { return mData; }
|
||||
|
||||
private:
|
||||
// Don't try to use the default constructor.
|
||||
DeletedMessageInfo();
|
||||
|
||||
~DeletedMessageInfo();
|
||||
|
||||
DeletedMessageInfoData mData;
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> mDeletedMessageIds;
|
||||
nsCOMPtr<nsIWritableVariant> mDeletedThreadIds;
|
||||
|
||||
protected:
|
||||
/* additional members */
|
||||
};
|
||||
|
||||
} // namespace mobilemessage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_mobilemessage_DeletedMessageInfo_h
|
@ -5,11 +5,13 @@
|
||||
|
||||
#include "MobileMessageManager.h"
|
||||
|
||||
#include "DeletedMessageInfo.h"
|
||||
#include "DOMCursor.h"
|
||||
#include "DOMRequest.h"
|
||||
#include "MobileMessageCallback.h"
|
||||
#include "MobileMessageCursorCallback.h"
|
||||
#include "mozilla/dom/mobilemessage/Constants.h" // For kSms*ObserverTopic
|
||||
#include "mozilla/dom/MozMessageDeletedEvent.h"
|
||||
#include "mozilla/dom/MozMmsEvent.h"
|
||||
#include "mozilla/dom/MozMobileMessageManagerBinding.h"
|
||||
#include "mozilla/dom/MozSmsEvent.h"
|
||||
@ -35,6 +37,7 @@
|
||||
#define DELIVERY_ERROR_EVENT_NAME NS_LITERAL_STRING("deliveryerror")
|
||||
#define READ_SUCCESS_EVENT_NAME NS_LITERAL_STRING("readsuccess")
|
||||
#define READ_ERROR_EVENT_NAME NS_LITERAL_STRING("readerror")
|
||||
#define DELETED_EVENT_NAME NS_LITERAL_STRING("deleted")
|
||||
|
||||
using namespace mozilla::dom::mobilemessage;
|
||||
|
||||
@ -71,6 +74,7 @@ MobileMessageManager::Init()
|
||||
obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false);
|
||||
obs->AddObserver(this, kSmsReadSuccessObserverTopic, false);
|
||||
obs->AddObserver(this, kSmsReadErrorObserverTopic, false);
|
||||
obs->AddObserver(this, kSmsDeletedObserverTopic, false);
|
||||
}
|
||||
|
||||
void
|
||||
@ -91,6 +95,7 @@ MobileMessageManager::Shutdown()
|
||||
obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic);
|
||||
obs->RemoveObserver(this, kSmsReadSuccessObserverTopic);
|
||||
obs->RemoveObserver(this, kSmsReadErrorObserverTopic);
|
||||
obs->RemoveObserver(this, kSmsDeletedObserverTopic);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -518,6 +523,39 @@ MobileMessageManager::DispatchTrustedSmsEventToSelf(const char* aTopic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MobileMessageManager::DispatchTrustedDeletedEventToSelf(nsISupports* aDeletedInfo)
|
||||
{
|
||||
nsCOMPtr<nsIDeletedMessageInfo> deletedInfo = do_QueryInterface(aDeletedInfo);
|
||||
if (deletedInfo) {
|
||||
MozMessageDeletedEventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
DeletedMessageInfo* info =
|
||||
static_cast<DeletedMessageInfo*>(deletedInfo.get());
|
||||
|
||||
uint32_t msgIdLength = info->GetData().deletedMessageIds().Length();
|
||||
if (msgIdLength) {
|
||||
Sequence<int32_t>& deletedMsgIds = init.mDeletedMessageIds.SetValue();
|
||||
deletedMsgIds.AppendElements(info->GetData().deletedMessageIds());
|
||||
}
|
||||
|
||||
uint32_t threadIdLength = info->GetData().deletedThreadIds().Length();
|
||||
if (threadIdLength) {
|
||||
Sequence<uint64_t>& deletedThreadIds = init.mDeletedThreadIds.SetValue();
|
||||
deletedThreadIds.AppendElements(info->GetData().deletedThreadIds());
|
||||
}
|
||||
|
||||
nsRefPtr<MozMessageDeletedEvent> event =
|
||||
MozMessageDeletedEvent::Constructor(this, DELETED_EVENT_NAME, init);
|
||||
return DispatchTrustedEvent(event);
|
||||
}
|
||||
|
||||
NS_ERROR("Got a 'deleted' topic without a valid message!");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileMessageManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
@ -558,6 +596,10 @@ MobileMessageManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
return DispatchTrustedSmsEventToSelf(aTopic, READ_ERROR_EVENT_NAME, aSubject);
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, kSmsDeletedObserverTopic)) {
|
||||
return DispatchTrustedDeletedEventToSelf(aSubject);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ public:
|
||||
IMPL_EVENT_HANDLER(deliveryerror)
|
||||
IMPL_EVENT_HANDLER(readsuccess)
|
||||
IMPL_EVENT_HANDLER(readerror)
|
||||
IMPL_EVENT_HANDLER(deleted)
|
||||
|
||||
private:
|
||||
~MobileMessageManager() {}
|
||||
@ -148,6 +149,9 @@ private:
|
||||
const nsAString& aEventName,
|
||||
nsISupports* aMsg);
|
||||
|
||||
nsresult
|
||||
DispatchTrustedDeletedEventToSelf(nsISupports* aDeletedInfo);
|
||||
|
||||
/**
|
||||
* Helper to get message ID from SMS/MMS Message object
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "MobileMessageThread.h"
|
||||
#include "MobileMessageService.h"
|
||||
#include "SmsSegmentInfo.h"
|
||||
#include "DeletedMessageInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -134,6 +135,20 @@ MobileMessageService::CreateThread(uint64_t aId,
|
||||
aThread);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileMessageService::CreateDeletedMessageInfo(int32_t* aMessageIds,
|
||||
uint32_t aMsgCount,
|
||||
uint64_t* aThreadIds,
|
||||
uint32_t aThreadCount,
|
||||
nsIDeletedMessageInfo** aDeletedInfo)
|
||||
{
|
||||
return DeletedMessageInfo::Create(aMessageIds,
|
||||
aMsgCount,
|
||||
aThreadIds,
|
||||
aThreadCount,
|
||||
aDeletedInfo);
|
||||
}
|
||||
|
||||
} // namespace mobilemessage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -37,10 +37,10 @@ const kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
|
||||
const kSmsDeliveryErrorObserverTopic = "sms-delivery-error";
|
||||
const kSmsReadSuccessObserverTopic = "sms-read-success";
|
||||
const kSmsReadErrorObserverTopic = "sms-read-error";
|
||||
const kSmsDeletedObserverTopic = "sms-deleted";
|
||||
|
||||
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
||||
const kNetworkConnStateChangedTopic = "network-connection-state-changed";
|
||||
const kMobileMessageDeletedObserverTopic = "mobile-message-deleted";
|
||||
|
||||
const kPrefRilRadioDisabled = "ril.radio.disabled";
|
||||
|
||||
@ -910,7 +910,7 @@ CancellableTransaction.prototype = {
|
||||
registerRunCallback: function(callback) {
|
||||
if (!this.isObserversAdded) {
|
||||
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
Services.obs.addObserver(this, kMobileMessageDeletedObserverTopic, false);
|
||||
Services.obs.addObserver(this, kSmsDeletedObserverTopic, false);
|
||||
Services.prefs.addObserver(kPrefRilRadioDisabled, this, false);
|
||||
Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
|
||||
this.isObserversAdded = true;
|
||||
@ -923,7 +923,7 @@ CancellableTransaction.prototype = {
|
||||
removeObservers: function() {
|
||||
if (this.isObserversAdded) {
|
||||
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
Services.obs.removeObserver(this, kMobileMessageDeletedObserverTopic);
|
||||
Services.obs.removeObserver(this, kSmsDeletedObserverTopic);
|
||||
Services.prefs.removeObserver(kPrefRilRadioDisabled, this);
|
||||
Services.prefs.removeObserver(kPrefDefaultServiceId, this);
|
||||
this.isObserversAdded = false;
|
||||
@ -972,13 +972,11 @@ CancellableTransaction.prototype = {
|
||||
this.cancelRunning(_MMS_ERROR_SHUTDOWN);
|
||||
break;
|
||||
}
|
||||
case kMobileMessageDeletedObserverTopic: {
|
||||
data = JSON.parse(data);
|
||||
if (data.id != this.cancellableId) {
|
||||
return;
|
||||
case kSmsDeletedObserverTopic: {
|
||||
if (subject && subject.deletedMessageIds &&
|
||||
subject.deletedMessageIds.indexOf(this.cancellableId) >= 0) {
|
||||
this.cancelRunning(_MMS_ERROR_MESSAGE_DELETED);
|
||||
}
|
||||
|
||||
this.cancelRunning(_MMS_ERROR_MESSAGE_DELETED);
|
||||
break;
|
||||
}
|
||||
case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID: {
|
||||
|
@ -2095,11 +2095,14 @@ MobileMessageDB.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let deletedInfo = { messageIds: [], threadIds: [] };
|
||||
|
||||
txn.oncomplete = function oncomplete(event) {
|
||||
if (aMessageRecord.id > self.lastMessageId) {
|
||||
self.lastMessageId = aMessageRecord.id;
|
||||
}
|
||||
notifyResult(Cr.NS_OK, aMessageRecord);
|
||||
self.notifyDeletedInfo(deletedInfo);
|
||||
};
|
||||
txn.onabort = function onabort(event) {
|
||||
// TODO bug 832140 check event.target.errorCode
|
||||
@ -2111,13 +2114,14 @@ MobileMessageDB.prototype = {
|
||||
let threadStore = stores[2];
|
||||
self.replaceShortMessageOnSave(txn, messageStore, participantStore,
|
||||
threadStore, aMessageRecord,
|
||||
aThreadParticipants);
|
||||
aThreadParticipants, deletedInfo);
|
||||
}, [MESSAGE_STORE_NAME, PARTICIPANT_STORE_NAME, THREAD_STORE_NAME]);
|
||||
},
|
||||
|
||||
replaceShortMessageOnSave: function(aTransaction, aMessageStore,
|
||||
aParticipantStore, aThreadStore,
|
||||
aMessageRecord, aThreadParticipants) {
|
||||
aMessageRecord, aThreadParticipants,
|
||||
aDeletedInfo) {
|
||||
let isReplaceTypePid = (aMessageRecord.pid) &&
|
||||
((aMessageRecord.pid >= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_1 &&
|
||||
aMessageRecord.pid <= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_7) ||
|
||||
@ -2127,7 +2131,8 @@ MobileMessageDB.prototype = {
|
||||
aMessageRecord.delivery != DELIVERY_RECEIVED ||
|
||||
!isReplaceTypePid) {
|
||||
this.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants,
|
||||
aDeletedInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2149,7 +2154,8 @@ MobileMessageDB.prototype = {
|
||||
function(participantRecord) {
|
||||
if (!participantRecord) {
|
||||
self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants,
|
||||
aDeletedInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2160,7 +2166,8 @@ MobileMessageDB.prototype = {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants,
|
||||
aDeletedInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2177,13 +2184,15 @@ MobileMessageDB.prototype = {
|
||||
// Match! Now replace that found message record with current one.
|
||||
aMessageRecord.id = foundMessageRecord.id;
|
||||
self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants,
|
||||
aDeletedInfo);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
realSaveRecord: function(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aThreadParticipants) {
|
||||
aThreadStore, aMessageRecord, aThreadParticipants,
|
||||
aDeletedInfo) {
|
||||
let self = this;
|
||||
this.findThreadRecordByTypedAddresses(aThreadStore, aParticipantStore,
|
||||
aThreadParticipants, true,
|
||||
@ -2229,7 +2238,8 @@ MobileMessageDB.prototype = {
|
||||
aThreadStore,
|
||||
oldMessageRecord.threadId,
|
||||
aMessageRecord.id,
|
||||
oldMessageRecord.read);
|
||||
oldMessageRecord.read,
|
||||
aDeletedInfo);
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -2479,7 +2489,8 @@ MobileMessageDB.prototype = {
|
||||
},
|
||||
|
||||
updateThreadByMessageChange: function(messageStore, threadStore, threadId,
|
||||
messageId, messageRead) {
|
||||
messageId, messageRead, deletedInfo) {
|
||||
let self = this;
|
||||
threadStore.get(threadId).onsuccess = function(event) {
|
||||
// This must exist.
|
||||
let threadRecord = event.target.result;
|
||||
@ -2501,6 +2512,9 @@ MobileMessageDB.prototype = {
|
||||
debug("Deleting mru entry for thread id " + threadId);
|
||||
}
|
||||
threadStore.delete(threadId);
|
||||
if (deletedInfo) {
|
||||
deletedInfo.threadIds.push(threadId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2532,6 +2546,20 @@ MobileMessageDB.prototype = {
|
||||
};
|
||||
},
|
||||
|
||||
notifyDeletedInfo: function(info) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
let deletedInfo =
|
||||
gMobileMessageService
|
||||
.createDeletedMessageInfo(info.messageIds,
|
||||
info.messageIds.length,
|
||||
info.threadIds,
|
||||
info.threadIds.length);
|
||||
Services.obs.notifyObservers(deletedInfo, "sms-deleted", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIRilMobileMessageDatabaseService API
|
||||
*/
|
||||
@ -2993,6 +3021,9 @@ MobileMessageDB.prototype = {
|
||||
self.translateCrErrorToMessageCallbackError(error));
|
||||
return;
|
||||
}
|
||||
|
||||
let deletedInfo = { messageIds: [], threadIds: [] };
|
||||
|
||||
txn.onerror = function onerror(event) {
|
||||
if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
|
||||
//TODO look at event.target.errorCode, pick appropriate error constant
|
||||
@ -3005,6 +3036,7 @@ MobileMessageDB.prototype = {
|
||||
txn.oncomplete = function oncomplete(event) {
|
||||
if (DEBUG) debug("Transaction " + txn + " completed.");
|
||||
aRequest.notifyMessageDeleted(deleted, length);
|
||||
self.notifyDeletedInfo(deletedInfo);
|
||||
};
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
@ -3019,6 +3051,10 @@ MobileMessageDB.prototype = {
|
||||
// First actually delete the message.
|
||||
messageStore.delete(messageId).onsuccess = function(event) {
|
||||
if (DEBUG) debug("Message id " + messageId + " deleted");
|
||||
if (deletedInfo) {
|
||||
deletedInfo.messageIds.push(messageId);
|
||||
}
|
||||
|
||||
deleted[messageIndex] = true;
|
||||
|
||||
// Then update unread count and most recent message.
|
||||
@ -3026,11 +3062,8 @@ MobileMessageDB.prototype = {
|
||||
threadStore,
|
||||
messageRecord.threadId,
|
||||
messageId,
|
||||
messageRecord.read);
|
||||
|
||||
Services.obs.notifyObservers(null,
|
||||
"mobile-message-deleted",
|
||||
JSON.stringify({ id: messageId }));
|
||||
messageRecord.read,
|
||||
deletedInfo);
|
||||
};
|
||||
} else if (DEBUG) {
|
||||
debug("Message id " + messageId + " does not exist");
|
||||
|
@ -122,6 +122,8 @@ child:
|
||||
|
||||
NotifyReadErrorMessage(MobileMessageData aMessageData);
|
||||
|
||||
NotifyDeletedMessageInfo(DeletedMessageInfoData aDeletedInfo);
|
||||
|
||||
parent:
|
||||
/**
|
||||
* Sent when the child no longer needs to use sms.
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "SmsMessage.h"
|
||||
#include "MmsMessage.h"
|
||||
#include "SmsSegmentInfo.h"
|
||||
#include "DeletedMessageInfo.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
@ -132,6 +133,21 @@ SmsChild::RecvNotifyReadErrorMessage(const MobileMessageData& aData)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SmsChild::RecvNotifyDeletedMessageInfo(const DeletedMessageInfoData& aDeletedInfo)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!obs) {
|
||||
NS_ERROR("Failed to get nsIObserverService!");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> info = new DeletedMessageInfo(aDeletedInfo);
|
||||
obs->NotifyObservers(info, kSmsDeletedObserverTopic, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PSmsRequestChild*
|
||||
SmsChild::AllocPSmsRequestChild(const IPCSmsRequest& aRequest)
|
||||
{
|
||||
|
@ -64,6 +64,9 @@ protected:
|
||||
virtual bool
|
||||
RecvNotifyReadErrorMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyDeletedMessageInfo(const DeletedMessageInfoData& aDeletedInfo) MOZ_OVERRIDE;
|
||||
|
||||
virtual PSmsRequestChild*
|
||||
AllocPSmsRequestChild(const IPCSmsRequest& aRequest) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "nsCxPusher.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "DeletedMessageInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -179,6 +180,7 @@ SmsParent::SmsParent()
|
||||
obs->AddObserver(this, kSilentSmsReceivedObserverTopic, false);
|
||||
obs->AddObserver(this, kSmsReadSuccessObserverTopic, false);
|
||||
obs->AddObserver(this, kSmsReadErrorObserverTopic, false);
|
||||
obs->AddObserver(this, kSmsDeletedObserverTopic, false);
|
||||
}
|
||||
|
||||
void
|
||||
@ -199,6 +201,7 @@ SmsParent::ActorDestroy(ActorDestroyReason why)
|
||||
obs->RemoveObserver(this, kSilentSmsReceivedObserverTopic);
|
||||
obs->RemoveObserver(this, kSmsReadSuccessObserverTopic);
|
||||
obs->RemoveObserver(this, kSmsReadErrorObserverTopic);
|
||||
obs->RemoveObserver(this, kSmsDeletedObserverTopic);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -324,6 +327,17 @@ SmsParent::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, kSmsDeletedObserverTopic)) {
|
||||
nsCOMPtr<nsIDeletedMessageInfo> deletedInfo = do_QueryInterface(aSubject);
|
||||
if (!deletedInfo) {
|
||||
NS_ERROR("Got a 'sms-deleted' topic without a valid message!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
unused << SendNotifyDeletedMessageInfo(
|
||||
static_cast<DeletedMessageInfo*>(deletedInfo.get())->GetData());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -110,6 +110,12 @@ union MobileMessageCursorData
|
||||
ThreadData;
|
||||
};
|
||||
|
||||
struct DeletedMessageInfoData
|
||||
{
|
||||
int32_t[] deletedMessageIds;
|
||||
uint64_t[] deletedThreadIds;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ EXPORTS.mozilla.dom += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Constants.cpp',
|
||||
'DeletedMessageInfo.cpp',
|
||||
'DOMMobileMessageError.cpp',
|
||||
'ipc/SmsChild.cpp',
|
||||
'ipc/SmsIPCService.cpp',
|
||||
|
@ -361,9 +361,17 @@ function deleteMessagesById(aMessageIds) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let promises = [];
|
||||
promises.push(waitForManagerEvent("deleted"));
|
||||
|
||||
let request = manager.delete(aMessageIds);
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then((aEvent) => { return aEvent.target.result; });
|
||||
promises.push(wrapDomRequestAsPromise(request));
|
||||
|
||||
return Promise.all(promises)
|
||||
.then((aResults) => {
|
||||
return { deletedInfo: aResults[0],
|
||||
deletedFlags: aResults[1].target.result };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,3 +48,4 @@ qemu = true
|
||||
[test_error_of_mms_manual_retrieval.js]
|
||||
[test_error_of_mms_send.js]
|
||||
[test_error_of_sms_send.js]
|
||||
[test_ondeleted_event.js]
|
||||
|
58
dom/mobilemessage/tests/marionette/test_ondeleted_event.js
Normal file
58
dom/mobilemessage/tests/marionette/test_ondeleted_event.js
Normal file
@ -0,0 +1,58 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const DESTINATING_ADDRESS_1 = "0987654321";
|
||||
const DESTINATING_ADDRESS_2 = "1234567890";
|
||||
const OUT_TEXT = "Outgoing SMS message!";
|
||||
|
||||
function verifyDeletedInfo(aDeletedInfo, aMsgIds, aThreadIds) {
|
||||
is(JSON.stringify(aDeletedInfo.deletedMessageIds),
|
||||
JSON.stringify(aMsgIds), 'Check Msg Id.');
|
||||
is(JSON.stringify(aDeletedInfo.deletedThreadIds),
|
||||
JSON.stringify(aThreadIds), 'Check Thread Id.');
|
||||
}
|
||||
|
||||
function testDeletingMessagesInOneThreadOneByOne() {
|
||||
let sentMessages = [];
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => sendSmsWithSuccess(DESTINATING_ADDRESS_1, OUT_TEXT))
|
||||
.then((aMessage) => { sentMessages.push(aMessage); })
|
||||
.then(() => sendSmsWithSuccess(DESTINATING_ADDRESS_1, OUT_TEXT))
|
||||
.then((aMessage) => { sentMessages.push(aMessage); })
|
||||
.then(() => deleteMessagesById([sentMessages[0].id]))
|
||||
.then((aResult) => {
|
||||
verifyDeletedInfo(aResult.deletedInfo,
|
||||
[sentMessages[0].id],
|
||||
null);
|
||||
})
|
||||
.then(() => deleteMessagesById([sentMessages[1].id]))
|
||||
.then((aResult) => {
|
||||
verifyDeletedInfo(aResult.deletedInfo,
|
||||
[sentMessages[1].id],
|
||||
[sentMessages[1].threadId]);
|
||||
});
|
||||
}
|
||||
|
||||
function testDeletingMessagesInTwoThreadsAtOnce() {
|
||||
let sentMessages = [];
|
||||
return Promise.resolve()
|
||||
.then(() => sendSmsWithSuccess(DESTINATING_ADDRESS_1, OUT_TEXT))
|
||||
.then((aMessage) => { sentMessages.push(aMessage); })
|
||||
.then(() => sendSmsWithSuccess(DESTINATING_ADDRESS_2, OUT_TEXT))
|
||||
.then((aMessage) => { sentMessages.push(aMessage); })
|
||||
.then(() =>
|
||||
deleteMessagesById(sentMessages.map((aMsg) => { return aMsg.id; })))
|
||||
.then((aResult) =>
|
||||
verifyDeletedInfo(aResult.deletedInfo,
|
||||
sentMessages.map((aMsg) => { return aMsg.id; }),
|
||||
sentMessages.map((aMsg) => { return aMsg.threadId; })));
|
||||
}
|
||||
|
||||
startTestCommon(function testCaseMain() {
|
||||
return testDeletingMessagesInOneThreadOneByOne()
|
||||
.then(() => testDeletingMessagesInTwoThreadsAtOnce());
|
||||
});
|
@ -293,7 +293,9 @@ WifiGeoPositionProvider.prototype = {
|
||||
let self = this;
|
||||
let settingsCallback = {
|
||||
handle: function(name, result) {
|
||||
if (name == SETTINGS_DEBUG_ENABLED) {
|
||||
// Stop the B2G UI setting from overriding the js prefs setting, and turning off logging
|
||||
// If gLoggingEnabled is already on during startup, that means it was set in js prefs.
|
||||
if (name == SETTINGS_DEBUG_ENABLED && !gLoggingEnabled) {
|
||||
gLoggingEnabled = result;
|
||||
} else if (name == SETTINGS_WIFI_ENABLED) {
|
||||
gWifiScanningEnabled = result;
|
||||
|
@ -1367,7 +1367,7 @@ DataConnectionHandler.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (defaultDataCallConnected && wifi_active) {
|
||||
if (networkInterface.enabled && wifi_active) {
|
||||
if (DEBUG) {
|
||||
this.debug("Disconnect data call when Wifi is connected.");
|
||||
}
|
||||
@ -1375,7 +1375,7 @@ DataConnectionHandler.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.dataCallSettings.enabled || networkInterface.enabled) {
|
||||
if (!this.dataCallSettings.enabled || defaultDataCallConnected) {
|
||||
if (DEBUG) {
|
||||
this.debug("Data call settings: nothing to do.");
|
||||
}
|
||||
|
@ -1861,6 +1861,15 @@ RilObject.prototype = {
|
||||
options.featureStr = "";
|
||||
this.sendCdmaFlashCommand(options);
|
||||
} else {
|
||||
// Only accept one conference request at a time..
|
||||
if (this._hasConferenceRequest) {
|
||||
options.success = false;
|
||||
options.errorName = "addError";
|
||||
options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
|
||||
this.sendChromeMessage(options);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hasConferenceRequest = true;
|
||||
this.telephonyRequestQueue.push(REQUEST_CONFERENCE,
|
||||
this.sendRilRequestConference, options);
|
||||
|
@ -712,9 +712,11 @@ let emulator = (function() {
|
||||
* @param connectedCallback [optional]
|
||||
* A callback function which is called when conference state becomes
|
||||
* connected.
|
||||
* @param twice [optional]
|
||||
* To send conference request twice. It is only used for special test.
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function addCallsToConference(callsToAdd, connectedCallback) {
|
||||
function addCallsToConference(callsToAdd, connectedCallback, twice) {
|
||||
log("Add " + callsToAdd.length + " calls into conference.");
|
||||
|
||||
let deferred = Promise.defer();
|
||||
@ -755,10 +757,13 @@ let emulator = (function() {
|
||||
});
|
||||
|
||||
// Cannot use apply() through webidl, so just separate the cases to handle.
|
||||
if (callsToAdd.length == 2) {
|
||||
conference.add(callsToAdd[0], callsToAdd[1]);
|
||||
} else {
|
||||
conference.add(callsToAdd[0]);
|
||||
let requestCount = twice ? 2 : 1;
|
||||
for (let i = 0; i < requestCount; ++i) {
|
||||
if (callsToAdd.length == 2) {
|
||||
conference.add(callsToAdd[0], callsToAdd[1]);
|
||||
} else {
|
||||
conference.add(callsToAdd[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
|
@ -56,6 +56,7 @@ disabled = Bug 821958
|
||||
[test_conference_two_remove_one.js]
|
||||
[test_conference_three_hangup_one.js]
|
||||
[test_conference_three_remove_one.js]
|
||||
[test_conference_add_twice_error.js]
|
||||
[test_outgoing_when_two_calls_on_line.js]
|
||||
[test_call_presentation.js]
|
||||
[test_incomingcall_phonestate_speaker.js]
|
||||
|
@ -0,0 +1,48 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
function testConferenceTwoCallsTwice() {
|
||||
log('= testConferenceTwoCallsTwice =');
|
||||
|
||||
let outCall;
|
||||
let inCall;
|
||||
let outNumber = "5555550101";
|
||||
let inNumber = "5555550201";
|
||||
let outInfo = gOutCallStrPool(outNumber);
|
||||
let inInfo = gInCallStrPool(inNumber);
|
||||
|
||||
let sendConferenceTwice = true;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(gCheckInitialState)
|
||||
.then(() => gDial(outNumber))
|
||||
.then(call => { outCall = call; })
|
||||
.then(() => gCheckAll(outCall, [outCall], '', [], [outInfo.ringing]))
|
||||
.then(() => gRemoteAnswer(outCall))
|
||||
.then(() => gCheckAll(outCall, [outCall], '', [], [outInfo.active]))
|
||||
.then(() => gRemoteDial(inNumber))
|
||||
.then(call => { inCall = call; })
|
||||
.then(() => gCheckAll(outCall, [outCall, inCall], '', [],
|
||||
[outInfo.active, inInfo.incoming]))
|
||||
.then(() => gAnswer(inCall))
|
||||
.then(() => gCheckAll(inCall, [outCall, inCall], '', [],
|
||||
[outInfo.held, inInfo.active]))
|
||||
.then(() => gAddCallsToConference([outCall, inCall], function() {
|
||||
gCheckState(conference, [], 'connected', [outCall, inCall]);
|
||||
}, sendConferenceTwice))
|
||||
.then(() => gCheckAll(conference, [], 'connected', [outCall, inCall],
|
||||
[outInfo.active, inInfo.active]))
|
||||
.then(() => gRemoteHangUpCalls([outCall, inCall]));
|
||||
}
|
||||
|
||||
// Start the test
|
||||
startTest(function() {
|
||||
testConferenceTwoCallsTwice()
|
||||
.then(null, error => {
|
||||
ok(false, 'promise rejects during test.');
|
||||
})
|
||||
.then(finish);
|
||||
});
|
@ -657,6 +657,8 @@ var interfaceNamesInGlobalScope =
|
||||
{name: "MozInputContext", b2g: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "MozInputMethodManager", b2g: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "MozMessageDeletedEvent", b2g: true, pref: "dom.sms.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MozMmsEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
21
dom/webidl/MozMessageDeletedEvent.webidl
Normal file
21
dom/webidl/MozMessageDeletedEvent.webidl
Normal file
@ -0,0 +1,21 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[Pref="dom.sms.enabled",
|
||||
Constructor(DOMString type, optional MozMessageDeletedEventInit eventInitDict)]
|
||||
interface MozMessageDeletedEvent : Event
|
||||
{
|
||||
// Array of deleted message ids.
|
||||
[Cached, Constant] readonly attribute sequence<long>? deletedMessageIds;
|
||||
// Array of deleted thread ids.
|
||||
[Cached, Constant] readonly attribute sequence<unsigned long long>? deletedThreadIds;
|
||||
};
|
||||
|
||||
dictionary MozMessageDeletedEventInit : EventInit
|
||||
{
|
||||
sequence<long>? deletedMessageIds = null;
|
||||
sequence<unsigned long long>? deletedThreadIds = null;
|
||||
};
|
@ -120,4 +120,5 @@ interface MozMobileMessageManager : EventTarget
|
||||
attribute EventHandler ondeliveryerror;
|
||||
attribute EventHandler onreadsuccess;
|
||||
attribute EventHandler onreaderror;
|
||||
attribute EventHandler ondeleted;
|
||||
};
|
||||
|
@ -652,6 +652,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
||||
'MozContactChangeEvent.webidl',
|
||||
'MozEmergencyCbModeEvent.webidl',
|
||||
'MozInterAppMessageEvent.webidl',
|
||||
'MozMessageDeletedEvent.webidl',
|
||||
'MozMmsEvent.webidl',
|
||||
'MozOtaStatusEvent.webidl',
|
||||
'MozSettingsEvent.webidl',
|
||||
|
@ -4,11 +4,16 @@
|
||||
let Promise = SpecialPowers.Cu.import('resource://gre/modules/Promise.jsm').Promise;
|
||||
|
||||
const STOCK_HOSTAPD_NAME = 'goldfish-hostapd';
|
||||
const HOSTAPD_CONFIG_PATH = '/data/misc/wifi/hostapd/';
|
||||
const HOSTAPD_CONFIG_PATH = '/data/misc/wifi/remote-hostapd/';
|
||||
|
||||
const SETTINGS_RIL_DATA_ENABLED = 'ril.data.enabled';
|
||||
const SETTINGS_TETHERING_WIFI_ENABLED = 'tethering.wifi.enabled';
|
||||
const SETTINGS_TETHERING_WIFI_IP = 'tethering.wifi.ip';
|
||||
const SETTINGS_TETHERING_WIFI_SECURITY = 'tethering.wifi.security.type';
|
||||
|
||||
const HOSTAPD_COMMON_CONFIG = {
|
||||
driver: 'test',
|
||||
ctrl_interface: '/data/misc/wifi/hostapd',
|
||||
ctrl_interface: '/data/misc/wifi/remote-hostapd',
|
||||
test_socket: 'DIR:/data/misc/wifi/sockets',
|
||||
hw_mode: 'b',
|
||||
channel: '2',
|
||||
@ -38,6 +43,29 @@ let gTestSuite = (function() {
|
||||
let wifiOrigEnabled;
|
||||
let pendingEmulatorShellCount = 0;
|
||||
|
||||
/**
|
||||
* A wrapper function of "is".
|
||||
*
|
||||
* Calls the marionette function "is" as well as throws an exception
|
||||
* if the givens values are not equal.
|
||||
*
|
||||
* @param value1
|
||||
* Any type of value to compare.
|
||||
*
|
||||
* @param value2
|
||||
* Any type of value to compare.
|
||||
*
|
||||
* @param message
|
||||
* Debug message for this check.
|
||||
*
|
||||
*/
|
||||
function isOrThrow(value1, value2, message) {
|
||||
is(value1, value2, message);
|
||||
if (value1 !== value2) {
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send emulator shell command with safe guard.
|
||||
*
|
||||
@ -98,6 +126,34 @@ let gTestSuite = (function() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for one named MozMobileConnection event.
|
||||
*
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
*
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
* @param aEventName
|
||||
* A string event name.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForMobileConnectionEventOnce(aEventName, aServiceId) {
|
||||
aServiceId = aServiceId || 0;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let mobileconnection = navigator.mozMobileConnections[aServiceId];
|
||||
|
||||
mobileconnection.addEventListener(aEventName, function onevent(aEvent) {
|
||||
mobileconnection.removeEventListener(aEventName, onevent);
|
||||
|
||||
ok(true, "Mobile connection event '" + aEventName + "' got.");
|
||||
deferred.resolve(aEvent);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the detail of currently running processes containing the given name.
|
||||
*
|
||||
@ -151,7 +207,9 @@ let gTestSuite = (function() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let permissions = [{ 'type': 'wifi-manage', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'settings-write', 'allow': 1, 'context': window.document }];
|
||||
{ 'type': 'settings-write', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'settings-read', 'allow': 1, 'context': window.document },
|
||||
{ 'type': 'mobileconnection', 'allow': 1, 'context': window.document }];
|
||||
|
||||
SpecialPowers.pushPermissions(permissions, function() {
|
||||
deferred.resolve();
|
||||
@ -226,6 +284,71 @@ let gTestSuite = (function() {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for RIL data being connected.
|
||||
*
|
||||
* This function will check |MozMobileConnection.data.connected| on
|
||||
* every 'datachange' event. Resolve when |MozMobileConnection.data.connected|
|
||||
* becomes the expected state. Never reject.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aConnected
|
||||
* Boolean that indicates the desired data state.
|
||||
*
|
||||
* @param aServiceId [optional]
|
||||
* A numeric DSDS service id. Default: 0.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForRilDataConnected(aConnected, aServiceId) {
|
||||
aServiceId = aServiceId || 0;
|
||||
return waitForMobileConnectionEventOnce('datachange', aServiceId)
|
||||
.then(function () {
|
||||
let mobileconnection = navigator.mozMobileConnections[aServiceId];
|
||||
if (mobileconnection.data.connected !== aConnected) {
|
||||
return waitForRilDataConnected(aConnected, aServiceId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to enable/disable wifi tethering.
|
||||
*
|
||||
* Enable/disable wifi tethering by changing the settings value 'tethering.wifi.enabled'.
|
||||
* Resolve when the routing is verified to set up successfully in 20 seconds. The polling
|
||||
* period is 1 second.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: The error message.
|
||||
*
|
||||
* @param aEnabled
|
||||
* Boolean that indicates to enable or disable wifi tethering.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function requestTetheringEnabled(aEnabled) {
|
||||
let RETRY_INTERVAL_MS = 1000;
|
||||
let retryCnt = 20;
|
||||
|
||||
return setSettings1(SETTINGS_TETHERING_WIFI_ENABLED, aEnabled)
|
||||
.then(function waitForRoutingVerified() {
|
||||
return verifyTetheringRouting(aEnabled)
|
||||
.then(null, function onreject(aReason) {
|
||||
|
||||
log('verifyTetheringRouting rejected due to ' + aReason +
|
||||
' (' + retryCnt + ')');
|
||||
|
||||
if (!retryCnt--) {
|
||||
throw aReason;
|
||||
}
|
||||
|
||||
return waitForTimeout(RETRY_INTERVAL_MS).then(waitForRoutingVerified);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget the given network.
|
||||
*
|
||||
@ -459,6 +582,61 @@ let gTestSuite = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mozSettings value with only one key.
|
||||
*
|
||||
* Resolve if that mozSettings value is set successfully, reject otherwise.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aKey
|
||||
* A string key.
|
||||
* @param aValue
|
||||
* An object value.
|
||||
* @param aAllowError [optional]
|
||||
* A boolean value. If set to true, an error response won't be treated
|
||||
* as test failure. Default: false.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setSettings1(aKey, aValue, aAllowError) {
|
||||
let settings = {};
|
||||
settings[aKey] = aValue;
|
||||
return setSettings(settings, aAllowError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mozSettings value specified by @aKey.
|
||||
*
|
||||
* Resolve if that mozSettings value is retrieved successfully, reject
|
||||
* otherwise.
|
||||
*
|
||||
* Fulfill params:
|
||||
* The corresponding mozSettings value of the key.
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aKey
|
||||
* A string.
|
||||
* @param aAllowError [optional]
|
||||
* A boolean value. If set to true, an error response won't be treated
|
||||
* as test failure. Default: false.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function getSettings(aKey, aAllowError) {
|
||||
let request =
|
||||
navigator.mozSettings.createLock().get(aKey);
|
||||
return wrapDomRequestAsPromise(request)
|
||||
.then(function resolve(aEvent) {
|
||||
ok(true, "getSettings(" + aKey + ") - success");
|
||||
return aEvent.target.result[aKey];
|
||||
}, function reject(aEvent) {
|
||||
ok(aAllowError, "getSettings(" + aKey + ") - error");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start hostapd processes with given configuration list.
|
||||
*
|
||||
@ -759,6 +937,159 @@ let gTestSuite = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify everything about routing when the wifi tethering is either on or off.
|
||||
*
|
||||
* We use two unix commands to verify the routing: 'netcfg' and 'ip route'.
|
||||
* For now the following two things will be checked:
|
||||
* 1) The default route interface should be 'rmnet0'.
|
||||
* 2) wlan0 is up and its ip is set to a private subnet.
|
||||
*
|
||||
* We also verify iptables output as netd's NatController will execute
|
||||
* $ iptables -t nat -A POSTROUTING -o rmnet0 -j MASQUERADE
|
||||
*
|
||||
* Resolve when the verification is successful and reject otherwise.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: String that indicates the reason of rejection.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function verifyTetheringRouting(aEnabled) {
|
||||
let netcfgResult = {};
|
||||
let ipRouteResult = {};
|
||||
|
||||
// Execute 'netcfg' and parse to |netcfgResult|, each key of which is the
|
||||
// interface name and value is { ip(string) }.
|
||||
function exeAndParseNetcfg() {
|
||||
return runEmulatorShellSafe(['netcfg'])
|
||||
.then(function (aLines) {
|
||||
// Sample output:
|
||||
//
|
||||
// lo UP 127.0.0.1/8 0x00000049 00:00:00:00:00:00
|
||||
// eth0 UP 10.0.2.15/24 0x00001043 52:54:00:12:34:56
|
||||
// rmnet1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:58
|
||||
// rmnet2 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:59
|
||||
// rmnet3 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:5a
|
||||
// wlan0 UP 192.168.1.1/24 0x00001043 52:54:00:12:34:5b
|
||||
// sit0 DOWN 0.0.0.0/0 0x00000080 00:00:00:00:00:00
|
||||
// rmnet0 UP 10.0.2.100/24 0x00001043 52:54:00:12:34:57
|
||||
//
|
||||
aLines.forEach(function (aLine) {
|
||||
let tokens = aLine.split(/\s+/);
|
||||
if (tokens.length < 5) {
|
||||
return;
|
||||
}
|
||||
let ifname = tokens[0];
|
||||
let ip = (tokens[2].split('/'))[0];
|
||||
netcfgResult[ifname] = { ip: ip };
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Execute 'ip route' and parse to |ipRouteResult|, each key of which is the
|
||||
// interface name and value is { src(string), default(boolean) }.
|
||||
function exeAndParseIpRoute() {
|
||||
return runEmulatorShellSafe(['ip', 'route'])
|
||||
.then(function (aLines) {
|
||||
// Sample output:
|
||||
//
|
||||
// 10.0.2.4 via 10.0.2.2 dev rmnet0
|
||||
// 10.0.2.3 via 10.0.2.2 dev rmnet0
|
||||
// 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.1
|
||||
// 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
|
||||
// 10.0.2.0/24 dev rmnet0 proto kernel scope link src 10.0.2.100
|
||||
// default via 10.0.2.2 dev rmnet0
|
||||
// default via 10.0.2.2 dev eth0 metric 2
|
||||
//
|
||||
|
||||
// Parse source ip for each interface.
|
||||
aLines.forEach(function (aLine) {
|
||||
let tokens = aLine.trim().split(/\s+/);
|
||||
let srcIndex = tokens.indexOf('src');
|
||||
if (srcIndex < 0 || srcIndex + 1 >= tokens.length) {
|
||||
return;
|
||||
}
|
||||
let ifname = tokens[2];
|
||||
let src = tokens[srcIndex + 1];
|
||||
ipRouteResult[ifname] = { src: src, default: false };
|
||||
});
|
||||
|
||||
// Parse default interfaces.
|
||||
aLines.forEach(function (aLine) {
|
||||
let tokens = aLine.split(/\s+/);
|
||||
if (tokens.length < 2) {
|
||||
return;
|
||||
}
|
||||
if ('default' === tokens[0]) {
|
||||
let ifnameIndex = tokens.indexOf('dev');
|
||||
if (ifnameIndex < 0 || ifnameIndex + 1 >= tokens.length) {
|
||||
return;
|
||||
}
|
||||
let ifname = tokens[ifnameIndex + 1];
|
||||
if (ipRouteResult[ifname]) {
|
||||
ipRouteResult[ifname].default = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Find MASQUERADE in POSTROUTING section. 'MASQUERADE' should be found
|
||||
// when tethering is enabled. 'MASQUERADE' shouldn't be found when tethering
|
||||
// is disabled.
|
||||
function verifyIptables() {
|
||||
return runEmulatorShellSafe(['iptables', '-t', 'nat', '-L', 'POSTROUTING'])
|
||||
.then(function(aLines) {
|
||||
// $ iptables -t nat -L POSTROUTING
|
||||
//
|
||||
// Sample output (tethering on):
|
||||
//
|
||||
// Chain POSTROUTING (policy ACCEPT)
|
||||
// target prot opt source destination
|
||||
// MASQUERADE all -- anywhere anywhere
|
||||
//
|
||||
let found = (function find_MASQUERADE() {
|
||||
// Skip first two lines.
|
||||
for (let i = 2; i < aLines.length; i++) {
|
||||
if (-1 !== aLines[i].indexOf('MASQUERADE')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
if ((aEnabled && !found) || (!aEnabled && found)) {
|
||||
throw 'MASQUERADE' + (found ? '' : ' not') + ' found while tethering is ' +
|
||||
(aEnabled ? 'enabled' : 'disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function verifyDefaultRouteAndIp(aExpectedWifiTetheringIp) {
|
||||
log(JSON.stringify(ipRouteResult));
|
||||
log(JSON.stringify(netcfgResult));
|
||||
|
||||
if (aEnabled) {
|
||||
isOrThrow(ipRouteResult['rmnet0'].src, netcfgResult['rmnet0'].ip, 'rmnet0.ip');
|
||||
isOrThrow(ipRouteResult['rmnet0'].default, true, 'rmnet0.default');
|
||||
|
||||
isOrThrow(ipRouteResult['wlan0'].src, netcfgResult['wlan0'].ip, 'wlan0.ip');
|
||||
isOrThrow(ipRouteResult['wlan0'].src, aExpectedWifiTetheringIp, 'expected ip');
|
||||
isOrThrow(ipRouteResult['wlan0'].default, false, 'wlan0.default');
|
||||
}
|
||||
}
|
||||
|
||||
return verifyIptables()
|
||||
.then(exeAndParseNetcfg)
|
||||
.then(exeAndParseIpRoute)
|
||||
.then(() => getSettings(SETTINGS_TETHERING_WIFI_IP))
|
||||
.then(ip => verifyDefaultRouteAndIp(ip));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all the allocated resources and running services for the test.
|
||||
*
|
||||
@ -829,6 +1160,8 @@ let gTestSuite = (function() {
|
||||
suite.waitForConnected = waitForConnected;
|
||||
suite.forgetNetwork = forgetNetwork;
|
||||
suite.waitForTimeout = waitForTimeout;
|
||||
suite.waitForRilDataConnected = waitForRilDataConnected;
|
||||
suite.requestTetheringEnabled = requestTetheringEnabled;
|
||||
|
||||
/**
|
||||
* Common test routine.
|
||||
@ -878,5 +1211,56 @@ let gTestSuite = (function() {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* The common test routine for wifi tethering.
|
||||
*
|
||||
* Similar as doTest except that it will set 'ril.data.enabled' to true
|
||||
* before testing and restore it afterward. It will also verify 'ril.data.enabled'
|
||||
* and 'tethering.wifi.enabled' to be false in the beginning. Note that this routine
|
||||
* will NOT change the state of 'tethering.wifi.enabled' so the user should enable
|
||||
* than disable on his/her own. This routine will only check if tethering is turned
|
||||
* off after testing.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aTestCaseChain
|
||||
* The test case entry point, which can be a function or a promise.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
suite.doTestTethering = function(aTestCaseChain) {
|
||||
|
||||
function verifyInitialState() {
|
||||
return getSettings(SETTINGS_RIL_DATA_ENABLED)
|
||||
.then(enabled => isOrThrow(enabled, false, SETTINGS_RIL_DATA_ENABLED))
|
||||
.then(() => getSettings(SETTINGS_TETHERING_WIFI_ENABLED))
|
||||
.then(enabled => isOrThrow(enabled, false, SETTINGS_TETHERING_WIFI_ENABLED));
|
||||
}
|
||||
|
||||
function initTetheringTestEnvironment() {
|
||||
// Enable ril data.
|
||||
return Promise.all([waitForRilDataConnected(true),
|
||||
setSettings1(SETTINGS_RIL_DATA_ENABLED, true)])
|
||||
.then(setSettings1(SETTINGS_TETHERING_WIFI_SECURITY, 'open'));
|
||||
}
|
||||
|
||||
function restoreToInitialState() {
|
||||
return setSettings1(SETTINGS_RIL_DATA_ENABLED, false)
|
||||
.then(() => getSettings(SETTINGS_TETHERING_WIFI_ENABLED))
|
||||
.then(enabled => is(enabled, false, 'Tethering should be turned off.'));
|
||||
}
|
||||
|
||||
return suite.doTest(function() {
|
||||
return verifyInitialState()
|
||||
.then(initTetheringTestEnvironment)
|
||||
.then(aTestCaseChain)
|
||||
.then(restoreToInitialState, function onreject(aReason) {
|
||||
return restoreToInitialState()
|
||||
.then(() => { throw aReason; }); // Re-throw the orignal reject reason.
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return suite;
|
||||
})();
|
||||
|
@ -8,3 +8,6 @@ qemu = true
|
||||
[test_wifi_associate.js]
|
||||
[test_wifi_associate_wo_connect.js]
|
||||
[test_wifi_auto_connect.js]
|
||||
[test_wifi_tethering_wifi_disabled.js]
|
||||
[test_wifi_tethering_wifi_inactive.js]
|
||||
[test_wifi_tethering_wifi_active.js]
|
||||
|
59
dom/wifi/test/marionette/test_wifi_tethering_wifi_active.js
Normal file
59
dom/wifi/test/marionette/test_wifi_tethering_wifi_active.js
Normal file
@ -0,0 +1,59 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
function connectToFirstNetwork() {
|
||||
let firstNetwork;
|
||||
return gTestSuite.requestWifiScan()
|
||||
.then(function (networks) {
|
||||
firstNetwork = networks[0];
|
||||
return gTestSuite.testAssociate(firstNetwork);
|
||||
})
|
||||
.then(() => firstNetwork);
|
||||
}
|
||||
|
||||
gTestSuite.doTestTethering(function() {
|
||||
let firstNetwork;
|
||||
|
||||
return gTestSuite.ensureWifiEnabled(true)
|
||||
.then(function () {
|
||||
return Promise.all([
|
||||
// 1) Set up the event listener first:
|
||||
// RIL data should become disconnected after connecting to wifi.
|
||||
gTestSuite.waitForRilDataConnected(false),
|
||||
|
||||
// 2) Connect to the first scanned network.
|
||||
connectToFirstNetwork()
|
||||
.then(aFirstNetwork => firstNetwork = aFirstNetwork)
|
||||
]);
|
||||
})
|
||||
.then(function() {
|
||||
return Promise.all([
|
||||
// 1) Set up the event listeners first:
|
||||
// Wifi should be turned off and RIL data should be connected.
|
||||
gTestSuite.waitForWifiManagerEventOnce('disabled'),
|
||||
gTestSuite.waitForRilDataConnected(true),
|
||||
|
||||
// 2) Start wifi tethering.
|
||||
gTestSuite.requestTetheringEnabled(true)
|
||||
]);
|
||||
})
|
||||
.then(function() {
|
||||
return Promise.all([
|
||||
// 1) Set up the event listeners first:
|
||||
// Wifi should be enabled, RIL data should become disconnected and
|
||||
// we should connect to an wifi AP.
|
||||
gTestSuite.waitForWifiManagerEventOnce('enabled'),
|
||||
gTestSuite.waitForRilDataConnected(false),
|
||||
gTestSuite.waitForConnected(firstNetwork),
|
||||
|
||||
// 2) Stop wifi tethering.
|
||||
gTestSuite.requestTetheringEnabled(false)
|
||||
]);
|
||||
})
|
||||
// Remove wlan0 from default route by disabling wifi. Otherwise,
|
||||
// it will cause the subsequent test cases loading page error.
|
||||
.then(() => gTestSuite.requestWifiEnabled(false));
|
||||
});
|
@ -0,0 +1,11 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
gTestSuite.doTestTethering(function() {
|
||||
return gTestSuite.ensureWifiEnabled(false)
|
||||
.then(() => gTestSuite.requestTetheringEnabled(true))
|
||||
.then(() => gTestSuite.requestTetheringEnabled(false))
|
||||
});
|
@ -0,0 +1,21 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
gTestSuite.doTestTethering(function() {
|
||||
return gTestSuite.ensureWifiEnabled(true)
|
||||
.then(function() {
|
||||
return Promise.all([
|
||||
gTestSuite.waitForWifiManagerEventOnce('disabled'),
|
||||
gTestSuite.requestTetheringEnabled(true)
|
||||
]);
|
||||
})
|
||||
.then(function() {
|
||||
return Promise.all([
|
||||
gTestSuite.waitForWifiManagerEventOnce('enabled'),
|
||||
gTestSuite.requestTetheringEnabled(false)
|
||||
]);
|
||||
});
|
||||
});
|
@ -12,5 +12,4 @@ b2g = true
|
||||
skip = false
|
||||
|
||||
[test_touchcaret.py]
|
||||
disabled = "Bug 1035172"
|
||||
[test_selectioncarets.py]
|
||||
|
Loading…
Reference in New Issue
Block a user