зеркало из https://github.com/mozilla/gecko-dev.git
Merge b2g-inbound to m-c a=merge
This commit is contained in:
Коммит
8715ca584e
|
@ -1013,7 +1013,7 @@ pref("services.sync.fxaccounts.enabled", true);
|
|||
pref("identity.fxaccounts.enabled", true);
|
||||
|
||||
// Mobile Identity API.
|
||||
pref("services.mobileid.server.uri", "https://msisdn-dev.stage.mozaws.net");
|
||||
pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
|
||||
|
||||
// Enable mapped array buffer
|
||||
pref("dom.mapped_arraybuffer.enabled", true);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "8cd6c73ef83257a569d148e246108b2c161127bb",
|
||||
"revision": "6739781fb8d0f3ae8bff65d1093e74d9f21ed6e5",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -4,9 +4,14 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include "base/message_loop.h"
|
||||
#include "BluetoothInterface.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
|
@ -69,6 +74,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 +123,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);
|
||||
}
|
||||
}
|
||||
|
||||
/* |AcceptWatcher| specializes |SocketMessageWatcher| for accept
|
||||
* operations by reading the socket messages from Bluedroid and
|
||||
* forwarding the received client socket to the resource handler.
|
||||
* The first message is received immediately. When there's a new
|
||||
* connection, Bluedroid sends the 2nd message with the socket
|
||||
* info and socket file descriptor.
|
||||
*/
|
||||
class AcceptWatcher MOZ_FINAL : public SocketMessageWatcher
|
||||
{
|
||||
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
|
||||
|
|
|
@ -561,6 +561,43 @@ function waitForAdapterAttributeChanged(aAdapter, aAttrName, aExpectedValue) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for specified number of 'devicefound' events.
|
||||
*
|
||||
* Resolve if specified number of devices has been found. Never reject.
|
||||
*
|
||||
* Fulfill params: an array which contains BluetoothDeviceEvents that we
|
||||
* received from the BluetoothDiscoveryHandle.
|
||||
*
|
||||
* @param aDiscoveryHandle
|
||||
* A BluetoothDiscoveryHandle which is used to notify application of
|
||||
* discovered remote bluetooth devices.
|
||||
* @param aExpectedNumberOfDevices
|
||||
* The number of remote devices we expect to discovery.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForDevicesFound(aDiscoveryHandle, aExpectedNumberOfDevices) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
ok(aDiscoveryHandle instanceof BluetoothDiscoveryHandle,
|
||||
"discoveryHandle should be a BluetoothDiscoveryHandle");
|
||||
|
||||
let devicesArray = [];
|
||||
aDiscoveryHandle.ondevicefound = function onDeviceFound(aEvent) {
|
||||
ok(aEvent instanceof BluetoothDeviceEvent,
|
||||
"aEvent should be a BluetoothDeviceEvent");
|
||||
|
||||
devicesArray.push(aEvent);
|
||||
if (devicesArray.length >= aExpectedNumberOfDevices) {
|
||||
aDiscoveryHandle.ondevicefound = null;
|
||||
deferred.resolve(devicesArray);
|
||||
}
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush permission settings and call |finish()|.
|
||||
*/
|
||||
|
|
|
@ -6,3 +6,4 @@ qemu = false
|
|||
[test_dom_BluetoothManager_API2.js]
|
||||
[test_dom_BluetoothAdapter_enable_API2.js]
|
||||
[test_dom_BluetoothAdapter_setters_API2.js]
|
||||
[test_dom_BluetoothAdapter_discovery_API2.js]
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Test Purpose:
|
||||
// To verify the discovery process of BluetoothAdapter.
|
||||
// Testers have to put the B2G devices in an environment which is surrounded
|
||||
// by N discoverable remote devices. To pass this test, the number N has to be
|
||||
// greater or equals than EXPECTED_NUMBER_OF_REMOTE_DEVICES.
|
||||
//
|
||||
// Test Procedure:
|
||||
// [0] Set Bluetooth permission and enable default adapter.
|
||||
// [1] Start discovery and verify the correctness.
|
||||
// [2] Attach event handler for 'ondevicefound'.
|
||||
// [3] Stop discovery and verify the correctness.
|
||||
// [4] Mark the BluetoothDiscoveryHandle from [1] as expired.
|
||||
// [5] Start discovery and verify the correctness.
|
||||
// [6] Wait for 'devicefound' events.
|
||||
// [7] Stop discovery and verify the correctness.
|
||||
// [8] Call 'startDiscovery' twice continuously.
|
||||
// [9] Call 'stopDiscovery' twice continuously.
|
||||
// [10] Clean up the event handler of [2].
|
||||
//
|
||||
// Test Coverage:
|
||||
// - BluetoothAdapter.discovering
|
||||
// - BluetoothAdapter.startDiscovery()
|
||||
// - BluetoothAdapter.stopDiscovery()
|
||||
// - BluetoothAdapter.onattributechanged()
|
||||
// - BluetoothDiscoveryHandle.ondevicefound()
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const EXPECTED_NUMBER_OF_REMOTE_DEVICES = 2;
|
||||
|
||||
startBluetoothTest(true, function testCaseMain(aAdapter) {
|
||||
log("Checking adapter attributes ...");
|
||||
|
||||
is(aAdapter.state, "enabled", "adapter.state");
|
||||
isnot(aAdapter.address, "", "adapter.address");
|
||||
|
||||
// Since adapter has just been re-enabled, these properties should be 'false'.
|
||||
is(aAdapter.discovering, false, "adapter.discovering");
|
||||
is(aAdapter.discoverable, false, "adapter.discoverable");
|
||||
|
||||
log("adapter.address: " + aAdapter.address);
|
||||
log("adapter.name: " + aAdapter.name);
|
||||
|
||||
let discoveryHandle = null;
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
log("[1] Start discovery and verify the correctness ... ");
|
||||
let promises = [];
|
||||
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", true));
|
||||
promises.push(aAdapter.startDiscovery());
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function(aResults) {
|
||||
log("[2] Attach event handler for 'ondevicefound' ... ");
|
||||
discoveryHandle = aResults[1];
|
||||
isHandleExpired = false;
|
||||
discoveryHandle.ondevicefound = function onDeviceFound(aEvent) {
|
||||
if (isHandleExpired) {
|
||||
ok(false, "Expired BluetoothDiscoveryHandle received an event.");
|
||||
}
|
||||
};
|
||||
})
|
||||
.then(function() {
|
||||
log("[3] Stop discovery and and verify the correctness ... ");
|
||||
let promises = [];
|
||||
if (aAdapter.discovering) {
|
||||
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", false));
|
||||
}
|
||||
promises.push(aAdapter.stopDiscovery());
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function() {
|
||||
log("[4] Mark the BluetoothDiscoveryHandle from [1] as expired ... ");
|
||||
isHandleExpired = true;
|
||||
})
|
||||
.then(function() {
|
||||
log("[5] Start discovery and verify the correctness ... ");
|
||||
let promises = [];
|
||||
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", true));
|
||||
promises.push(aAdapter.startDiscovery());
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function(aResults) {
|
||||
log("[6] Wait for 'devicefound' events ... ");
|
||||
return waitForDevicesFound(aResults[1], EXPECTED_NUMBER_OF_REMOTE_DEVICES);
|
||||
})
|
||||
.then(function() {
|
||||
log("[7] Stop discovery and and verify the correctness ... ");
|
||||
let promises = [];
|
||||
if (aAdapter.discovering) {
|
||||
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", false));
|
||||
}
|
||||
promises.push(aAdapter.stopDiscovery());
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function() {
|
||||
log("[8] Call 'startDiscovery' twice continuously ... ");
|
||||
return aAdapter.startDiscovery()
|
||||
.then(() => aAdapter.startDiscovery())
|
||||
.then(() => ok(false, "Call startDiscovery() when adapter is discovering. - Fail"),
|
||||
() => ok(true, "Call startDiscovery() when adapter is discovering. - Success"));
|
||||
})
|
||||
.then(function() {
|
||||
log("[9] Call 'stopDiscovery' twice continuously ... ");
|
||||
return aAdapter.stopDiscovery()
|
||||
.then(() => aAdapter.stopDiscovery())
|
||||
.then(() => ok(true, "Call stopDiscovery() when adapter isn't discovering. - Success"),
|
||||
() => ok(false, "Call stopDiscovery() when adapter isn't discovering. - Fail"));
|
||||
})
|
||||
.then(function() {
|
||||
log("[10] Clean up the event handler of [2] ... ");
|
||||
if (discoveryHandle) {
|
||||
discoveryHandle.ondevicefound = null;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -25,6 +25,7 @@
|
|||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "MozMtpServer.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsString.h"
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include "VolumeManager.h"
|
||||
|
||||
using namespace mozilla::hal;
|
||||
USING_MTP_NAMESPACE
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
|
@ -69,6 +71,7 @@ using namespace mozilla::hal;
|
|||
|
||||
#define ICS_SYS_USB_FUNCTIONS "/sys/devices/virtual/android_usb/android0/functions"
|
||||
#define ICS_SYS_UMS_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mass_storage"
|
||||
#define ICS_SYS_MTP_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mtp"
|
||||
#define ICS_SYS_USB_STATE "/sys/devices/virtual/android_usb/android0/state"
|
||||
|
||||
#define USE_DEBUG 0
|
||||
|
@ -229,6 +232,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void StartMtpServer();
|
||||
void StopMtpServer();
|
||||
|
||||
void UpdateState();
|
||||
|
||||
const char* ModeStr(int32_t aMode)
|
||||
|
@ -347,6 +353,7 @@ private:
|
|||
};
|
||||
|
||||
static StaticRefPtr<AutoMounter> sAutoMounter;
|
||||
static StaticRefPtr<MozMtpServer> sMozMtpServer;
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
|
@ -398,6 +405,25 @@ AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
AutoMounter::StartMtpServer()
|
||||
{
|
||||
if (sMozMtpServer) {
|
||||
// Mtp Server is already running - nothing to do
|
||||
return;
|
||||
}
|
||||
LOG("Starting MtpServer");
|
||||
sMozMtpServer = new MozMtpServer();
|
||||
sMozMtpServer->Run();
|
||||
}
|
||||
|
||||
void
|
||||
AutoMounter::StopMtpServer()
|
||||
{
|
||||
LOG("Stopping MtpServer");
|
||||
sMozMtpServer = nullptr;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void
|
||||
|
@ -441,22 +467,23 @@ AutoMounter::UpdateState()
|
|||
|
||||
bool umsAvail = false;
|
||||
bool umsEnabled = false;
|
||||
bool mtpAvail = false;
|
||||
bool mtpEnabled = false;
|
||||
|
||||
if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) {
|
||||
char functionsStr[60];
|
||||
if (!ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
|
||||
ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
|
||||
functionsStr[0] = '\0';
|
||||
}
|
||||
umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0);
|
||||
if (umsAvail) {
|
||||
char functionsStr[60];
|
||||
if (ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
|
||||
umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
|
||||
} else {
|
||||
ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
|
||||
umsEnabled = false;
|
||||
}
|
||||
} else {
|
||||
umsEnabled = false;
|
||||
umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
|
||||
}
|
||||
mtpAvail = (access(ICS_SYS_MTP_DIRECTORY, F_OK) == 0);
|
||||
if (mtpAvail) {
|
||||
mtpEnabled = strstr(functionsStr, "mtp") != nullptr;
|
||||
}
|
||||
} else {
|
||||
umsAvail = ReadSysFile(GB_SYS_UMS_ENABLE, &umsEnabled);
|
||||
}
|
||||
|
||||
bool usbCablePluggedIn = IsUsbCablePluggedIn();
|
||||
|
@ -469,9 +496,19 @@ AutoMounter::UpdateState()
|
|||
}
|
||||
}
|
||||
|
||||
bool tryToShare = (umsAvail && umsEnabled && enabled && usbCablePluggedIn);
|
||||
LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
|
||||
umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare);
|
||||
bool tryToShare = (((umsAvail && umsEnabled) || (mtpAvail && mtpEnabled))
|
||||
&& enabled && usbCablePluggedIn);
|
||||
LOG("UpdateState: ums:%d%d mtp:%d%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
|
||||
umsAvail, umsEnabled, mtpAvail, mtpEnabled, mMode, usbCablePluggedIn, tryToShare);
|
||||
|
||||
if (mtpAvail && mtpEnabled) {
|
||||
if (enabled && usbCablePluggedIn) {
|
||||
StartMtpServer();
|
||||
} else {
|
||||
StopMtpServer();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool filesOpen = false;
|
||||
static unsigned filesOpenDelayCount = 0;
|
||||
|
@ -902,6 +939,9 @@ AutoMounterUnmountVolume(const nsCString& aVolumeName)
|
|||
void
|
||||
ShutdownAutoMounter()
|
||||
{
|
||||
if (sAutoMounter) {
|
||||
sAutoMounter->StopMtpServer();
|
||||
}
|
||||
sAutoMounterSetting = nullptr;
|
||||
sUsbCableObserver = nullptr;
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_system_mozmtpcommon_h__
|
||||
#define mozilla_system_mozmtpcommon_h__
|
||||
|
||||
#include "mozilla/Types.h"
|
||||
#include <android/log.h>
|
||||
|
||||
#define MTP_LOG(msg, ...) \
|
||||
__android_log_print(ANDROID_LOG_INFO, "MozMtp", \
|
||||
"%s: " msg, __FUNCTION__, ##__VA_ARGS__) \
|
||||
|
||||
#define MTP_ERR(msg, ...) \
|
||||
__android_log_print(ANDROID_LOG_ERROR, "MozMtp", \
|
||||
"%s: " msg, __FUNCTION__, ##__VA_ARGS__) \
|
||||
|
||||
#define BEGIN_MTP_NAMESPACE \
|
||||
namespace mozilla { namespace system { namespace mtp {
|
||||
#define END_MTP_NAMESPACE \
|
||||
} /* namespace mtp */ } /* namespace system */ } /* namespace mozilla */
|
||||
#define USING_MTP_NAMESPACE \
|
||||
using namespace mozilla::system::mtp;
|
||||
|
||||
namespace android {
|
||||
class MOZ_EXPORT MtpServer;
|
||||
class MOZ_EXPORT MtpStorage;
|
||||
class MOZ_EXPORT MtpDatabase;
|
||||
class MOZ_EXPORT MtpDataPacket;
|
||||
class MOZ_EXPORT MtpProperty;
|
||||
}
|
||||
|
||||
#include <mtp.h>
|
||||
#include <MtpDatabase.h>
|
||||
#include <MtpObjectInfo.h>
|
||||
#include <MtpProperty.h>
|
||||
#include <MtpServer.h>
|
||||
#include <MtpStorage.h>
|
||||
#include <MtpTypes.h>
|
||||
|
||||
#endif // mozilla_system_mtpcommon_h__
|
|
@ -0,0 +1,792 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MozMtpDatabase.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "prio.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <libgen.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla {
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCloseDir, PRDir, PR_CloseDir)
|
||||
}
|
||||
|
||||
BEGIN_MTP_NAMESPACE
|
||||
|
||||
static const char *
|
||||
ObjectPropertyAsStr(MtpObjectProperty aProperty)
|
||||
{
|
||||
switch (aProperty) {
|
||||
case MTP_PROPERTY_STORAGE_ID: return "MTP_PROPERTY_STORAGE_ID";
|
||||
case MTP_PROPERTY_OBJECT_FORMAT: return "MTP_PROPERTY_OBJECT_FORMAT";
|
||||
case MTP_PROPERTY_OBJECT_SIZE: return "MTP_PROPERTY_OBJECT_SIZE";
|
||||
case MTP_PROPERTY_WIDTH: return "MTP_PROPERTY_WIDTH";
|
||||
case MTP_PROPERTY_HEIGHT: return "MTP_PROPERTY_HEIGHT";
|
||||
case MTP_PROPERTY_IMAGE_BIT_DEPTH: return "MTP_PROPERTY_IMAGE_BIT_DEPTH";
|
||||
case MTP_PROPERTY_DISPLAY_NAME: return "MTP_PROPERTY_DISPLAY_NAME";
|
||||
}
|
||||
return "MTP_PROPERTY_???";
|
||||
}
|
||||
|
||||
MozMtpDatabase::MozMtpDatabase(const char *aDir)
|
||||
{
|
||||
MTP_LOG("");
|
||||
|
||||
// We use the index into the array as the handle. Since zero isn't a valid
|
||||
// index, we stick a dummy entry there.
|
||||
|
||||
RefPtr<DbEntry> dummy;
|
||||
|
||||
mDb.AppendElement(dummy);
|
||||
|
||||
ReadVolume("sdcard", aDir);
|
||||
}
|
||||
|
||||
//virtual
|
||||
MozMtpDatabase::~MozMtpDatabase()
|
||||
{
|
||||
MTP_LOG("");
|
||||
}
|
||||
|
||||
void
|
||||
MozMtpDatabase::AddEntry(DbEntry *entry)
|
||||
{
|
||||
entry->mHandle = GetNextHandle();
|
||||
MOZ_ASSERT(mDb.Length() == entry->mHandle);
|
||||
mDb.AppendElement(entry);
|
||||
|
||||
MTP_LOG("AddEntry: Handle: 0x%08x Parent: 0x%08x Path:'%s'",
|
||||
entry->mHandle, entry->mParent, entry->mPath.get());
|
||||
}
|
||||
|
||||
TemporaryRef<MozMtpDatabase::DbEntry>
|
||||
MozMtpDatabase::GetEntry(MtpObjectHandle aHandle)
|
||||
{
|
||||
RefPtr<DbEntry> entry;
|
||||
|
||||
if (aHandle > 0 && aHandle < mDb.Length()) {
|
||||
entry = mDb[aHandle];
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
void
|
||||
MozMtpDatabase::RemoveEntry(MtpObjectHandle aHandle)
|
||||
{
|
||||
if (aHandle > 0 && aHandle < mDb.Length()) {
|
||||
mDb[aHandle] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCString
|
||||
MozMtpDatabase::BaseName(const nsCString& path)
|
||||
{
|
||||
nsCOMPtr<nsIFile> file;
|
||||
NS_NewNativeLocalFile(path, false, getter_AddRefs(file));
|
||||
if (file) {
|
||||
nsCString leafName;
|
||||
file->GetNativeLeafName(leafName);
|
||||
return leafName;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void
|
||||
MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
|
||||
{
|
||||
ScopedCloseDir dir;
|
||||
|
||||
if (!(dir = PR_OpenDir(aDir))) {
|
||||
MTP_ERR("Unable to open directory '%s'", aDir);
|
||||
return;
|
||||
}
|
||||
|
||||
PRDirEntry* dirEntry;
|
||||
while ((dirEntry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
|
||||
nsPrintfCString filename("%s/%s", aDir, dirEntry->name);
|
||||
PRFileInfo64 fileInfo;
|
||||
if (PR_GetFileInfo64(filename.get(), &fileInfo) != PR_SUCCESS) {
|
||||
MTP_ERR("Unable to retrieve file information for '%s'", filename.get());
|
||||
continue;
|
||||
}
|
||||
|
||||
RefPtr<DbEntry> entry = new DbEntry;
|
||||
|
||||
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
|
||||
entry->mParent = aParent;
|
||||
entry->mObjectName = dirEntry->name;
|
||||
entry->mDisplayName = dirEntry->name;
|
||||
entry->mPath = filename;
|
||||
entry->mDateCreated = fileInfo.creationTime;
|
||||
entry->mDateModified = fileInfo.modifyTime;
|
||||
|
||||
if (fileInfo.type == PR_FILE_FILE) {
|
||||
entry->mObjectFormat = MTP_FORMAT_DEFINED;
|
||||
//TODO: Check how 64-bit filesize are dealt with
|
||||
entry->mObjectSize = fileInfo.size;
|
||||
AddEntry(entry);
|
||||
} else if (fileInfo.type == PR_FILE_DIRECTORY) {
|
||||
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
|
||||
entry->mObjectSize = 0;
|
||||
AddEntry(entry);
|
||||
ParseDirectory(filename.get(), entry->mHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MozMtpDatabase::ReadVolume(const char *volumeName, const char *aDir)
|
||||
{
|
||||
//TODO: Add an assert re thread being run on
|
||||
|
||||
PRFileInfo fileInfo;
|
||||
|
||||
if (PR_GetFileInfo(aDir, &fileInfo) != PR_SUCCESS) {
|
||||
MTP_ERR("'%s' doesn't exist", aDir);
|
||||
return;
|
||||
}
|
||||
if (fileInfo.type != PR_FILE_DIRECTORY) {
|
||||
MTP_ERR("'%s' isn't a directory", aDir);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<DbEntry> entry = new DbEntry;
|
||||
|
||||
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
|
||||
entry->mParent = MTP_PARENT_ROOT;
|
||||
entry->mObjectName = volumeName;
|
||||
entry->mDisplayName = volumeName;
|
||||
entry->mPath = aDir;
|
||||
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
|
||||
entry->mObjectSize = 0;
|
||||
|
||||
AddEntry(entry);
|
||||
|
||||
ParseDirectory(aDir, entry->mHandle);
|
||||
}
|
||||
|
||||
// called from SendObjectInfo to reserve a database entry for the incoming file
|
||||
//virtual
|
||||
MtpObjectHandle
|
||||
MozMtpDatabase::beginSendObject(const char* aPath,
|
||||
MtpObjectFormat aFormat,
|
||||
MtpObjectHandle aParent,
|
||||
MtpStorageID aStorageID,
|
||||
uint64_t aSize,
|
||||
time_t aModified)
|
||||
{
|
||||
if (!aParent) {
|
||||
MTP_LOG("aParent is NULL");
|
||||
return kInvalidObjectHandle;
|
||||
}
|
||||
|
||||
RefPtr<DbEntry> entry = new DbEntry;
|
||||
|
||||
entry->mStorageID = aStorageID;
|
||||
entry->mParent = aParent;
|
||||
entry->mPath = aPath;
|
||||
entry->mObjectName = BaseName(entry->mPath);
|
||||
entry->mDisplayName = entry->mObjectName;
|
||||
entry->mObjectFormat = aFormat;
|
||||
entry->mObjectSize = aSize;
|
||||
|
||||
AddEntry(entry);
|
||||
|
||||
MTP_LOG("Handle: 0x%08x Parent: 0x%08x Path: '%s'", entry->mHandle, aParent, aPath);
|
||||
|
||||
return entry->mHandle;
|
||||
}
|
||||
|
||||
// called to report success or failure of the SendObject file transfer
|
||||
// success should signal a notification of the new object's creation,
|
||||
// failure should remove the database entry created in beginSendObject
|
||||
|
||||
//virtual
|
||||
void
|
||||
MozMtpDatabase::endSendObject(const char* aPath,
|
||||
MtpObjectHandle aHandle,
|
||||
MtpObjectFormat aFormat,
|
||||
bool succeeded)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x Path: '%s'", aHandle, aPath);
|
||||
if (!succeeded) {
|
||||
RemoveEntry(aHandle);
|
||||
}
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpObjectHandleList*
|
||||
MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
|
||||
MtpObjectFormat aFormat,
|
||||
MtpObjectHandle aParent)
|
||||
{
|
||||
MTP_LOG("StorageID: 0x%08x Format: 0x%04x Parent: 0x%08x",
|
||||
aStorageID, aFormat, aParent);
|
||||
|
||||
//TODO: Optimize
|
||||
|
||||
ScopedDeletePtr<MtpObjectHandleList> list;
|
||||
|
||||
list = new MtpObjectHandleList();
|
||||
|
||||
DbArray::size_type numEntries = mDb.Length();
|
||||
DbArray::index_type entryIndex;
|
||||
for (entryIndex = 1; entryIndex < numEntries; entryIndex++) {
|
||||
RefPtr<DbEntry> entry = mDb[entryIndex];
|
||||
if (entry->mParent == aParent) {
|
||||
list->push(entry->mHandle);
|
||||
}
|
||||
}
|
||||
return list.forget();
|
||||
}
|
||||
|
||||
//virtual
|
||||
int
|
||||
MozMtpDatabase::getNumObjects(MtpStorageID aStorageID,
|
||||
MtpObjectFormat aFormat,
|
||||
MtpObjectHandle aParent)
|
||||
{
|
||||
MTP_LOG("");
|
||||
|
||||
return mDb.Length() - 1;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpObjectFormatList*
|
||||
MozMtpDatabase::getSupportedPlaybackFormats()
|
||||
{
|
||||
static const uint16_t init_data[] = {MTP_FORMAT_PNG};
|
||||
|
||||
MtpObjectFormatList *list = new MtpObjectFormatList();
|
||||
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
|
||||
|
||||
MTP_LOG("returning MTP_FORMAT_PNG");
|
||||
return list;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpObjectFormatList*
|
||||
MozMtpDatabase::getSupportedCaptureFormats()
|
||||
{
|
||||
static const uint16_t init_data[] = {MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG};
|
||||
|
||||
MtpObjectFormatList *list = new MtpObjectFormatList();
|
||||
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
|
||||
MTP_LOG("returning MTP_FORMAT_PNG");
|
||||
return list;
|
||||
}
|
||||
|
||||
static const MtpObjectProperty sSupportedObjectProperties[] =
|
||||
{
|
||||
MTP_PROPERTY_STORAGE_ID,
|
||||
MTP_PROPERTY_PARENT_OBJECT,
|
||||
MTP_PROPERTY_OBJECT_FORMAT,
|
||||
MTP_PROPERTY_OBJECT_SIZE,
|
||||
MTP_PROPERTY_OBJECT_FILE_NAME, // just the filename - no directory
|
||||
MTP_PROPERTY_PROTECTION_STATUS, // UINT16 - always 0
|
||||
MTP_PROPERTY_DATE_MODIFIED,
|
||||
MTP_PROPERTY_DATE_ADDED,
|
||||
};
|
||||
|
||||
//virtual
|
||||
MtpObjectPropertyList*
|
||||
MozMtpDatabase::getSupportedObjectProperties(MtpObjectFormat aFormat)
|
||||
{
|
||||
MTP_LOG("");
|
||||
MtpObjectPropertyList *list = new MtpObjectPropertyList();
|
||||
list->appendArray(sSupportedObjectProperties,
|
||||
MOZ_ARRAY_LENGTH(sSupportedObjectProperties));
|
||||
return list;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpDevicePropertyList*
|
||||
MozMtpDatabase::getSupportedDeviceProperties()
|
||||
{
|
||||
MTP_LOG("");
|
||||
static const uint16_t init_data[] = { MTP_DEVICE_PROPERTY_UNDEFINED };
|
||||
|
||||
MtpDevicePropertyList *list = new MtpDevicePropertyList();
|
||||
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
|
||||
return list;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::getObjectPropertyValue(MtpObjectHandle aHandle,
|
||||
MtpObjectProperty aProperty,
|
||||
MtpDataPacket& aPacket)
|
||||
{
|
||||
RefPtr<DbEntry> entry = GetEntry(aHandle);
|
||||
if (!entry) {
|
||||
MTP_ERR("Invalid Handle: 0x%08x", aHandle);
|
||||
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
|
||||
}
|
||||
|
||||
MTP_LOG("Handle: 0x%08x '%s' Property: %s 0x%08x",
|
||||
aHandle, entry->mDisplayName.get(), ObjectPropertyAsStr(aProperty), aProperty);
|
||||
|
||||
switch (aProperty)
|
||||
{
|
||||
case MTP_PROPERTY_STORAGE_ID: aPacket.putUInt32(entry->mStorageID); break;
|
||||
case MTP_PROPERTY_PARENT_OBJECT: aPacket.putUInt32(entry->mParent); break;
|
||||
case MTP_PROPERTY_OBJECT_FORMAT: aPacket.putUInt32(entry->mObjectFormat); break;
|
||||
case MTP_PROPERTY_OBJECT_SIZE: aPacket.putUInt32(entry->mObjectSize); break;
|
||||
case MTP_PROPERTY_DISPLAY_NAME: aPacket.putString(entry->mDisplayName.get()); break;
|
||||
|
||||
default:
|
||||
MTP_LOG("Invalid Property: 0x%08x", aProperty);
|
||||
return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
|
||||
}
|
||||
|
||||
return MTP_RESPONSE_OK;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
|
||||
MtpObjectProperty aProperty,
|
||||
MtpDataPacket& aPacket)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
|
||||
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::getDevicePropertyValue(MtpDeviceProperty aProperty,
|
||||
MtpDataPacket& aPacket)
|
||||
{
|
||||
MTP_LOG("(GENERAL ERROR)");
|
||||
return MTP_RESPONSE_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::setDevicePropertyValue(MtpDeviceProperty aProperty,
|
||||
MtpDataPacket& aPacket)
|
||||
{
|
||||
MTP_LOG("(NOT SUPPORTED)");
|
||||
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::resetDeviceProperty(MtpDeviceProperty aProperty)
|
||||
{
|
||||
MTP_LOG("(NOT SUPPORTED)");
|
||||
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void
|
||||
MozMtpDatabase::QueryEntries(MozMtpDatabase::MatchType aMatchType,
|
||||
uint32_t aMatchField1,
|
||||
uint32_t aMatchField2,
|
||||
DbArray &result)
|
||||
{
|
||||
DbArray::size_type numEntries = mDb.Length();
|
||||
DbArray::index_type entryIdx;
|
||||
RefPtr<DbEntry> entry;
|
||||
|
||||
result.Clear();
|
||||
|
||||
switch (aMatchType) {
|
||||
|
||||
case MatchAll:
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
if (mDb[entryIdx]) {
|
||||
result.AppendElement(mDb[entryIdx]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MatchHandle:
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
entry = mDb[entryIdx];
|
||||
if (entry && entry->mHandle == aMatchField1) {
|
||||
result.AppendElement(entry);
|
||||
// Handles are unique - return the one that we found.
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MatchParent:
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
entry = mDb[entryIdx];
|
||||
if (entry && entry->mParent == aMatchField1) {
|
||||
result.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MatchFormat:
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
entry = mDb[entryIdx];
|
||||
if (entry && entry->mObjectFormat == aMatchField1) {
|
||||
result.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MatchHandleFormat:
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
entry = mDb[entryIdx];
|
||||
if (entry && entry->mHandle == aMatchField1) {
|
||||
if (entry->mObjectFormat == aMatchField2) {
|
||||
result.AppendElement(entry);
|
||||
}
|
||||
// Only 1 entry can match my aHandle. So we can return early.
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MatchParentFormat:
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
entry = mDb[entryIdx];
|
||||
if (entry && entry->mParent == aMatchField1 && entry->mObjectFormat == aMatchField2) {
|
||||
result.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(!"Invalid MatchType");
|
||||
}
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
|
||||
uint32_t aFormat,
|
||||
uint32_t aProperty,
|
||||
int aGroupCode,
|
||||
int aDepth,
|
||||
MtpDataPacket& aPacket)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x Format: 0x%08x aProperty: 0x%08x aGroupCode: %d aDepth %d (NOT SUPPORTED)",
|
||||
aHandle, aFormat, aProperty, aGroupCode, aDepth);
|
||||
|
||||
if (aDepth > 1) {
|
||||
return MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED;
|
||||
}
|
||||
if (aGroupCode != 0) {
|
||||
return MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED;
|
||||
}
|
||||
|
||||
MatchType matchType = MatchAll;
|
||||
uint32_t matchField1 = 0;
|
||||
uint32_t matchField2 = 0;
|
||||
|
||||
// aHandle == 0 implies all objects at the root level
|
||||
// further specificed by aFormat and/or aDepth
|
||||
|
||||
if (aFormat == 0) {
|
||||
if (aHandle == 0xffffffff) {
|
||||
// select all objects
|
||||
matchType = MatchAll;
|
||||
} else {
|
||||
if (aDepth == 1) {
|
||||
// select objects whose Parent matches aHandle
|
||||
matchType = MatchParent;
|
||||
matchField1 = aHandle;
|
||||
} else {
|
||||
// select object whose handle matches aHandle
|
||||
matchType = MatchHandle;
|
||||
matchField1 = aHandle;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (aHandle == 0xffffffff) {
|
||||
// select all objects whose format matches aFormat
|
||||
matchType = MatchFormat;
|
||||
matchField1 = aFormat;
|
||||
} else {
|
||||
if (aDepth == 1) {
|
||||
// select objects whose Parent is aHandle and format matches aFormat
|
||||
matchType = MatchParentFormat;
|
||||
matchField1 = aHandle;
|
||||
matchField2 = aFormat;
|
||||
} else {
|
||||
// select objects whose handle is aHandle and format matches aFormat
|
||||
matchType = MatchHandleFormat;
|
||||
matchField1 = aHandle;
|
||||
matchField2 = aFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DbArray result;
|
||||
QueryEntries(matchType, matchField1, matchField2, result);
|
||||
|
||||
const MtpObjectProperty *objectPropertyList;
|
||||
size_t numObjectProperties = 0;
|
||||
MtpObjectProperty objectProperty;
|
||||
|
||||
if (aProperty == 0xffffffff) {
|
||||
// return all supported properties
|
||||
numObjectProperties = MOZ_ARRAY_LENGTH(sSupportedObjectProperties);
|
||||
objectPropertyList = sSupportedObjectProperties;
|
||||
} else {
|
||||
// return property indicated by aProperty
|
||||
numObjectProperties = 1;
|
||||
objectProperty = aProperty;
|
||||
objectPropertyList = &objectProperty;
|
||||
}
|
||||
|
||||
DbArray::size_type numEntries = result.Length();
|
||||
DbArray::index_type entryIdx;
|
||||
|
||||
aPacket.putUInt32(numEntries);
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
RefPtr<DbEntry> entry = result[entryIdx];
|
||||
|
||||
for (size_t propertyIdx = 0; propertyIdx < numObjectProperties; propertyIdx++) {
|
||||
aPacket.putUInt32(entry->mHandle);
|
||||
MtpObjectProperty prop = objectPropertyList[propertyIdx];
|
||||
aPacket.putUInt16(prop);
|
||||
switch (prop) {
|
||||
|
||||
case MTP_PROPERTY_STORAGE_ID:
|
||||
aPacket.putUInt16(MTP_TYPE_UINT32);
|
||||
aPacket.putUInt32(entry->mStorageID);
|
||||
break;
|
||||
|
||||
case MTP_PROPERTY_PARENT_OBJECT:
|
||||
aPacket.putUInt16(MTP_TYPE_UINT32);
|
||||
aPacket.putUInt32(entry->mParent);
|
||||
break;
|
||||
|
||||
case MTP_PROPERTY_OBJECT_FORMAT:
|
||||
aPacket.putUInt16(MTP_TYPE_UINT16);
|
||||
aPacket.putUInt16(entry->mObjectFormat);
|
||||
break;
|
||||
|
||||
case MTP_PROPERTY_OBJECT_SIZE:
|
||||
aPacket.putUInt16(MTP_TYPE_UINT64);
|
||||
aPacket.putUInt64(entry->mObjectSize);
|
||||
break;
|
||||
|
||||
case MTP_PROPERTY_OBJECT_FILE_NAME:
|
||||
aPacket.putUInt16(MTP_TYPE_STR);
|
||||
aPacket.putString(entry->mObjectName.get());
|
||||
break;
|
||||
|
||||
case MTP_PROPERTY_PROTECTION_STATUS:
|
||||
aPacket.putUInt16(MTP_TYPE_UINT16);
|
||||
aPacket.putUInt16(0); // 0 = No Protection
|
||||
break;
|
||||
|
||||
case MTP_PROPERTY_DATE_MODIFIED: {
|
||||
aPacket.putUInt16(MTP_TYPE_STR);
|
||||
PRExplodedTime explodedTime;
|
||||
PR_ExplodeTime(entry->mDateModified, PR_LocalTimeParameters, &explodedTime);
|
||||
char dateStr[20];
|
||||
PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
|
||||
aPacket.putString(dateStr);
|
||||
break;
|
||||
}
|
||||
|
||||
case MTP_PROPERTY_DATE_ADDED: {
|
||||
aPacket.putUInt16(MTP_TYPE_STR);
|
||||
PRExplodedTime explodedTime;
|
||||
PR_ExplodeTime(entry->mDateCreated, PR_LocalTimeParameters, &explodedTime);
|
||||
char dateStr[20];
|
||||
PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
|
||||
aPacket.putString(dateStr);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MTP_ERR("Unrecognized property code: %u", prop);
|
||||
return MTP_RESPONSE_GENERAL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MTP_RESPONSE_OK;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::getObjectInfo(MtpObjectHandle aHandle,
|
||||
MtpObjectInfo& aInfo)
|
||||
{
|
||||
RefPtr<DbEntry> entry = GetEntry(aHandle);
|
||||
if (!entry) {
|
||||
MTP_ERR("Handle 0x%08x is invalid", aHandle);
|
||||
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
|
||||
}
|
||||
|
||||
MTP_LOG("Handle: 0x%08x Display:'%s' Object:'%s'", aHandle, entry->mDisplayName.get(), entry->mObjectName.get());
|
||||
|
||||
aInfo.mHandle = aHandle;
|
||||
aInfo.mStorageID = entry->mStorageID;
|
||||
aInfo.mFormat = entry->mObjectFormat;
|
||||
aInfo.mProtectionStatus = 0x0;
|
||||
aInfo.mCompressedSize = 0;
|
||||
aInfo.mThumbFormat = entry->mObjectFormat;
|
||||
aInfo.mThumbCompressedSize = 20*20*4;
|
||||
aInfo.mThumbPixWidth = 20;
|
||||
aInfo.mThumbPixHeight =20;
|
||||
aInfo.mImagePixWidth = 20;
|
||||
aInfo.mImagePixHeight = 20;
|
||||
aInfo.mImagePixDepth = 4;
|
||||
aInfo.mParent = entry->mParent;
|
||||
aInfo.mAssociationType = 0;
|
||||
aInfo.mAssociationDesc = 0;
|
||||
aInfo.mSequenceNumber = 0;
|
||||
aInfo.mName = ::strdup(entry->mObjectName.get());
|
||||
aInfo.mDateCreated = 0;
|
||||
aInfo.mDateModified = 0;
|
||||
aInfo.mKeywords = ::strdup("fxos,touch");
|
||||
|
||||
return MTP_RESPONSE_OK;
|
||||
}
|
||||
|
||||
//virtual
|
||||
void*
|
||||
MozMtpDatabase::getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
|
||||
|
||||
aOutThumbSize = 0;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::getObjectFilePath(MtpObjectHandle aHandle,
|
||||
MtpString& aOutFilePath,
|
||||
int64_t& aOutFileLength,
|
||||
MtpObjectFormat& aOutFormat)
|
||||
{
|
||||
RefPtr<DbEntry> entry = GetEntry(aHandle);
|
||||
if (!entry) {
|
||||
MTP_ERR("Handle 0x%08x is invalid", aHandle);
|
||||
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
|
||||
}
|
||||
|
||||
MTP_LOG("Handle: 0x%08x FilePath: '%s'", aHandle, entry->mPath.get());
|
||||
|
||||
aOutFilePath = entry->mPath.get();
|
||||
aOutFileLength = entry->mObjectSize;
|
||||
aOutFormat = entry->mObjectFormat;
|
||||
|
||||
return MTP_RESPONSE_OK;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::deleteFile(MtpObjectHandle aHandle)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
|
||||
|
||||
//TODO
|
||||
|
||||
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::moveFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
|
||||
|
||||
// change parent
|
||||
|
||||
return MTP_RESPONSE_OK
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::copyFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
|
||||
|
||||
// duplicate DbEntry
|
||||
// change parent
|
||||
|
||||
return MTP_RESPONSE_OK
|
||||
}
|
||||
#endif
|
||||
|
||||
//virtual
|
||||
MtpObjectHandleList*
|
||||
MozMtpDatabase::getObjectReferences(MtpObjectHandle aHandle)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpResponseCode
|
||||
MozMtpDatabase::setObjectReferences(MtpObjectHandle aHandle,
|
||||
MtpObjectHandleList* aReferences)
|
||||
{
|
||||
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
|
||||
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpProperty*
|
||||
MozMtpDatabase::getObjectPropertyDesc(MtpObjectProperty aProperty,
|
||||
MtpObjectFormat aFormat)
|
||||
{
|
||||
MTP_LOG("Property: %s 0x%08x", ObjectPropertyAsStr(aProperty), aProperty);
|
||||
|
||||
// TODO: Perhaps Filesize should be 64-bit?
|
||||
|
||||
MtpProperty* result = nullptr;
|
||||
switch (aProperty)
|
||||
{
|
||||
case MTP_PROPERTY_STORAGE_ID: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
|
||||
case MTP_PROPERTY_OBJECT_FORMAT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
|
||||
case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
|
||||
case MTP_PROPERTY_WIDTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
|
||||
case MTP_PROPERTY_HEIGHT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
|
||||
case MTP_PROPERTY_IMAGE_BIT_DEPTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
|
||||
case MTP_PROPERTY_DISPLAY_NAME: result = new MtpProperty(aProperty, MTP_TYPE_STR); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//virtual
|
||||
MtpProperty*
|
||||
MozMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty aProperty)
|
||||
{
|
||||
MTP_LOG("(returning MTP_DEVICE_PROPERTY_UNDEFINED)");
|
||||
return new MtpProperty(MTP_DEVICE_PROPERTY_UNDEFINED, MTP_TYPE_UNDEFINED);
|
||||
}
|
||||
|
||||
//virtual
|
||||
void
|
||||
MozMtpDatabase::sessionStarted()
|
||||
{
|
||||
MTP_LOG("");
|
||||
}
|
||||
|
||||
//virtual
|
||||
void
|
||||
MozMtpDatabase::sessionEnded()
|
||||
{
|
||||
MTP_LOG("");
|
||||
}
|
||||
|
||||
END_MTP_NAMESPACE
|
|
@ -0,0 +1,161 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_system_mozmtpdatabase_h__
|
||||
#define mozilla_system_mozmtpdatabase_h__
|
||||
|
||||
#include "MozMtpCommon.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
BEGIN_MTP_NAMESPACE // mozilla::system::mtp
|
||||
|
||||
using namespace android;
|
||||
|
||||
class MozMtpDatabase : public MtpDatabase
|
||||
{
|
||||
public:
|
||||
MozMtpDatabase(const char *aDir);
|
||||
virtual ~MozMtpDatabase();
|
||||
|
||||
// called from SendObjectInfo to reserve a database entry for the incoming file
|
||||
virtual MtpObjectHandle beginSendObject(const char* aPath,
|
||||
MtpObjectFormat aFormat,
|
||||
MtpObjectHandle aParent,
|
||||
MtpStorageID aStorageID,
|
||||
uint64_t aSize,
|
||||
time_t aModified);
|
||||
|
||||
// called to report success or failure of the SendObject file transfer
|
||||
// success should signal a notification of the new object's creation,
|
||||
// failure should remove the database entry created in beginSendObject
|
||||
virtual void endSendObject(const char* aPath,
|
||||
MtpObjectHandle aHandle,
|
||||
MtpObjectFormat aFormat,
|
||||
bool aSucceeded);
|
||||
|
||||
virtual MtpObjectHandleList* getObjectList(MtpStorageID aStorageID,
|
||||
MtpObjectFormat aFormat,
|
||||
MtpObjectHandle aParent);
|
||||
|
||||
virtual int getNumObjects(MtpStorageID aStorageID,
|
||||
MtpObjectFormat aFormat,
|
||||
MtpObjectHandle aParent);
|
||||
|
||||
virtual MtpObjectFormatList* getSupportedPlaybackFormats();
|
||||
|
||||
virtual MtpObjectFormatList* getSupportedCaptureFormats();
|
||||
|
||||
virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat aFormat);
|
||||
|
||||
virtual MtpDevicePropertyList* getSupportedDeviceProperties();
|
||||
|
||||
virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle aHandle,
|
||||
MtpObjectProperty aProperty,
|
||||
MtpDataPacket& aPacket);
|
||||
|
||||
virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle aHandle,
|
||||
MtpObjectProperty aProperty,
|
||||
MtpDataPacket& aPacket);
|
||||
|
||||
virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty aProperty,
|
||||
MtpDataPacket& aPacket);
|
||||
|
||||
virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty aProperty,
|
||||
MtpDataPacket& aPacket);
|
||||
|
||||
virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty aProperty);
|
||||
|
||||
virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle aHandle,
|
||||
uint32_t aFormat,
|
||||
uint32_t aProperty,
|
||||
int aGroupCode,
|
||||
int aDepth,
|
||||
MtpDataPacket& aPacket);
|
||||
|
||||
virtual MtpResponseCode getObjectInfo(MtpObjectHandle aHandle,
|
||||
MtpObjectInfo& aInfo);
|
||||
|
||||
virtual void* getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize);
|
||||
|
||||
virtual MtpResponseCode getObjectFilePath(MtpObjectHandle aHandle,
|
||||
MtpString& aOutFilePath,
|
||||
int64_t& aOutFileLength,
|
||||
MtpObjectFormat& aOutFormat);
|
||||
|
||||
virtual MtpResponseCode deleteFile(MtpObjectHandle aHandle);
|
||||
|
||||
virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle aHandle);
|
||||
|
||||
virtual MtpResponseCode setObjectReferences(MtpObjectHandle aHandle,
|
||||
MtpObjectHandleList* aReferences);
|
||||
|
||||
virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty aProperty,
|
||||
MtpObjectFormat aFormat);
|
||||
|
||||
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty aProperty);
|
||||
|
||||
virtual void sessionStarted();
|
||||
|
||||
virtual void sessionEnded();
|
||||
|
||||
private:
|
||||
|
||||
struct DbEntry
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(DbEntry)
|
||||
|
||||
MtpObjectHandle mHandle; // uint32_t
|
||||
MtpStorageID mStorageID; // uint32_t
|
||||
nsCString mObjectName;
|
||||
MtpObjectFormat mObjectFormat; // uint16_t
|
||||
MtpObjectHandle mParent; // uint32_t
|
||||
uint64_t mObjectSize;
|
||||
nsCString mDisplayName;
|
||||
nsCString mPath;
|
||||
PRTime mDateCreated;
|
||||
PRTime mDateModified;
|
||||
};
|
||||
typedef nsTArray<mozilla::RefPtr<DbEntry> > DbArray;
|
||||
|
||||
DbArray mDb;
|
||||
|
||||
enum MatchType
|
||||
{
|
||||
MatchAll,
|
||||
MatchHandle,
|
||||
MatchParent,
|
||||
MatchFormat,
|
||||
MatchHandleFormat,
|
||||
MatchParentFormat,
|
||||
};
|
||||
|
||||
|
||||
void AddEntry(DbEntry *aEntry);
|
||||
mozilla::TemporaryRef<DbEntry> GetEntry(MtpObjectHandle aHandle);
|
||||
void RemoveEntry(MtpObjectHandle aHandle);
|
||||
void QueryEntries(MatchType aMatchType, uint32_t aMatchField1,
|
||||
uint32_t aMatchField2, DbArray& aResult);
|
||||
|
||||
nsCString BaseName(const nsCString& aPath);
|
||||
|
||||
|
||||
MtpObjectHandle GetNextHandle()
|
||||
{
|
||||
return mDb.Length();
|
||||
}
|
||||
|
||||
void ParseDirectory(const char *aDir, MtpObjectHandle aParent);
|
||||
void ReadVolume(const char *aVolumeName, const char *aDir);
|
||||
};
|
||||
|
||||
END_MTP_NAMESPACE
|
||||
|
||||
#endif // mozilla_system_mozmtpdatabase_h__
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MozMtpServer.h"
|
||||
#include "MozMtpDatabase.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace android;
|
||||
using namespace mozilla;
|
||||
USING_MTP_NAMESPACE
|
||||
|
||||
class MtpServerRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
nsresult Run()
|
||||
{
|
||||
const char *mtpUsbFilename = "/dev/mtp_usb";
|
||||
const char *productName = "FirefoxOS";
|
||||
const char *storageDir = "/storage/sdcard";
|
||||
|
||||
mFd = open(mtpUsbFilename, O_RDWR);
|
||||
if (mFd.get() < 0) {
|
||||
MTP_LOG("open of '%s' failed", mtpUsbFilename);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MTP_LOG("MozMtpServer open done, fd: %d. Start reading.", mFd.get());
|
||||
|
||||
ScopedDeletePtr<MozMtpDatabase> database;
|
||||
ScopedDeletePtr<MtpServer> server;
|
||||
ScopedDeletePtr<MtpStorage> storage;
|
||||
|
||||
database = new MozMtpDatabase(storageDir);
|
||||
server = new MtpServer(mFd.get(), database, false, 1023, 0664, 0775);
|
||||
storage = new MtpStorage(MTP_STORAGE_FIXED_RAM, // id
|
||||
storageDir, // filePath
|
||||
productName, // description
|
||||
100uLL * 1024uLL * 1024uLL, // reserveSpace
|
||||
false, // removable
|
||||
2uLL * 1024uLL * 1024uLL * 1024uLL); // maxFileSize
|
||||
|
||||
server->addStorage(storage);
|
||||
|
||||
MTP_LOG("MozMtpServer started");
|
||||
server->run();
|
||||
MTP_LOG("MozMtpServer finished");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedClose mFd;
|
||||
};
|
||||
|
||||
void
|
||||
MozMtpServer::Run()
|
||||
{
|
||||
nsresult rv = NS_NewNamedThread("MtpServer", getter_AddRefs(mServerThread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mServerThread);
|
||||
mServerThread->Dispatch(new MtpServerRunnable(), 0);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_system_mozmtpserver_h__
|
||||
#define mozilla_system_mozmtpserver_h__
|
||||
|
||||
#include "MozMtpCommon.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
|
||||
BEGIN_MTP_NAMESPACE
|
||||
|
||||
class MozMtpServer
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(MozMtpServer)
|
||||
|
||||
void Run();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIThread> mServerThread;
|
||||
};
|
||||
|
||||
END_MTP_NAMESPACE
|
||||
|
||||
#endif // mozilla_system_mozmtpserver_h__
|
||||
|
||||
|
|
@ -41,6 +41,8 @@ SOURCES += [
|
|||
'AutoMounter.cpp',
|
||||
'AutoMounterSetting.cpp',
|
||||
'GonkGPSGeolocationProvider.cpp',
|
||||
'MozMtpDatabase.cpp',
|
||||
'MozMtpServer.cpp',
|
||||
'NetworkUtils.cpp',
|
||||
'NetworkWorker.cpp',
|
||||
'nsVolume.cpp',
|
||||
|
@ -57,6 +59,11 @@ SOURCES += [
|
|||
'VolumeServiceTest.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ANDROID_VERSION'] >= '17':
|
||||
CXXFLAGS += ['-I%s/frameworks/av/media/mtp' % CONFIG['ANDROID_SOURCE']]
|
||||
else:
|
||||
CXXFLAGS += ['-I%s/frameworks/base/media/mtp' % CONFIG['ANDROID_SOURCE']]
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
|
||||
|
||||
|
|
|
@ -1146,6 +1146,7 @@ var WifiManager = (function() {
|
|||
createWaitForDriverReadyTimer(doStartWifiTethering);
|
||||
});
|
||||
} else {
|
||||
cancelWifiHotspotStatusTimer();
|
||||
gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
|
||||
configuration, function(result) {
|
||||
// Should we fire a dom event if we fail to set wifi tethering ?
|
||||
|
|
|
@ -134,6 +134,7 @@ OS_LIBS += \
|
|||
-lstagefright_omx \
|
||||
-lbinder \
|
||||
-lgui \
|
||||
-lmtp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче