Bug 933588 - Part 1: NFC service. r=khuey

This commit is contained in:
Yoshi Huang 2014-02-19 15:14:22 +08:00
parent 1305f5ccb1
commit c3e7baef48
14 changed files with 704 additions and 215 deletions

View File

@ -111,6 +111,7 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
},
_shutdown: function _shutdown() {
this.nfc.shutdown();
this.nfc = null;
Services.obs.removeObserver(this, NFC.TOPIC_XPCOM_SHUTDOWN);
@ -419,18 +420,22 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
});
function Nfc() {
debug("Starting Worker");
this.worker = new ChromeWorker("resource://gre/modules/nfc_worker.js");
this.worker.onerror = this.onerror.bind(this);
this.worker.onmessage = this.onmessage.bind(this);
debug("Starting Nfc Service");
let nfcService = Cc["@mozilla.org/nfc/service;1"].getService(Ci.nsINfcService);
if (!nfcService) {
debug("No nfc service component available!");
return;
}
nfcService.start(this);
this.nfcService = nfcService;
gMessageManager.init(this);
// Maps sessionId (that are generated from nfcd) with a unique guid : 'SessionToken'
this.sessionTokenMap = {};
this.targetsByRequestId = {};
gSystemWorkerManager.registerNfcWorker(this.worker);
}
Nfc.prototype = {
@ -438,32 +443,26 @@ Nfc.prototype = {
classID: NFC_CID,
classInfo: XPCOMUtils.generateCI({classID: NFC_CID,
classDescription: "Nfc",
interfaces: [Ci.nsIWorkerHolder]}),
interfaces: [Ci.nsINfcService]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerHolder, Ci.nsIObserver]),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsINfcEventListener]),
_currentSessionId: null,
powerLevel: NFC.NFC_POWER_LEVEL_UNKNOWN,
onerror: function onerror(event) {
debug("Got an error: " + event.filename + ":" +
event.lineno + ": " + event.message + "\n");
event.preventDefault();
},
/**
* Send arbitrary message to worker.
* Send arbitrary message to Nfc service.
*
* @param nfcMessageType
* A text message type.
* @param message [optional]
* An optional message object to send.
*/
sendToWorker: function sendToWorker(nfcMessageType, message) {
sendToNfcService: function sendToNfcService(nfcMessageType, message) {
message = message || {};
message.type = nfcMessageType;
this.worker.postMessage(message);
this.nfcService.sendCommand(message);
},
/**
@ -490,11 +489,11 @@ Nfc.prototype = {
},
/**
* Process the incoming message from the NFC worker
* Process the incoming message from the NFC Service.
*/
onmessage: function onmessage(event) {
let message = event.data;
debug("Received message from NFC worker: " + JSON.stringify(message));
onEvent: function onEvent(event) {
let message = Cu.cloneInto(event, this);
debug("Received message from NFC Service: " + JSON.stringify(message));
// mapping error code to error message
if (message.status !== undefined && message.status !== NFC.NFC_SUCCESS) {
@ -562,8 +561,7 @@ Nfc.prototype = {
}
},
// nsINfcWorker
worker: null,
nfcService: null,
sessionTokenMap: null,
@ -612,22 +610,22 @@ Nfc.prototype = {
switch (message.name) {
case "NFC:GetDetailsNDEF":
this.sendToWorker("getDetailsNDEF", message.json);
this.sendToNfcService("getDetailsNDEF", message.json);
break;
case "NFC:ReadNDEF":
this.sendToWorker("readNDEF", message.json);
this.sendToNfcService("readNDEF", message.json);
break;
case "NFC:WriteNDEF":
this.sendToWorker("writeNDEF", message.json);
this.sendToNfcService("writeNDEF", message.json);
break;
case "NFC:MakeReadOnlyNDEF":
this.sendToWorker("makeReadOnlyNDEF", message.json);
this.sendToNfcService("makeReadOnlyNDEF", message.json);
break;
case "NFC:Connect":
this.sendToWorker("connect", message.json);
this.sendToNfcService("connect", message.json);
break;
case "NFC:Close":
this.sendToWorker("close", message.json);
this.sendToNfcService("close", message.json);
break;
case "NFC:SendFile":
// Chrome process is the arbitrator / mediator between
@ -658,7 +656,12 @@ Nfc.prototype = {
},
setConfig: function setConfig(prop) {
this.sendToWorker("config", prop);
this.sendToNfcService("config", prop);
},
shutdown: function shutdown() {
this.nfcService.shutdown();
this.nfcService = null;
}
};

View File

@ -0,0 +1,27 @@
/* 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 "NfcMessageHandler.h"
#include <binder/Parcel.h>
using namespace android;
using namespace mozilla;
bool
NfcMessageHandler::Marshall(Parcel& aParcel, const CommandOptions& aOptions)
{
bool result;
// TODO: Implementation will be Bug 933588 - Part 2.
return result;
}
bool
NfcMessageHandler::Unmarshall(const Parcel& aParcel, EventOptions& aOptions)
{
bool result;
// TODO: Implementation will be Bug 933588 - Part 2.
return result;
}

View File

@ -0,0 +1,26 @@
/* 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 NfcMessageHandler_h
#define NfcMessageHandler_h
namespace android {
class MOZ_EXPORT Parcel;
} // namespace android
namespace mozilla {
class CommandOptions;
class EventOptions;
class NfcMessageHandler
{
public:
bool Marshall(android::Parcel& aParcel, const CommandOptions& aOptions);
bool Unmarshall(const android::Parcel& aParcel, EventOptions& aOptions);
};
} // namespace mozilla
#endif // NfcMessageHandler_h

View File

@ -0,0 +1,105 @@
/* 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 NfcOptions_h
#define NfcOptions_h
#include "mozilla/dom/NfcOptionsBinding.h"
namespace mozilla {
struct NDEFRecordStruct
{
uint8_t mTnf;
nsTArray<uint8_t> mType;
nsTArray<uint8_t> mId;
nsTArray<uint8_t> mPayload;
};
struct CommandOptions
{
CommandOptions(const mozilla::dom::NfcCommandOptions& aOther) {
#define COPY_FIELD(prop) prop = aOther.prop;
#define COPY_OPT_FIELD(prop, defaultValue) \
if (aOther.prop.WasPassed()) { \
prop = aOther.prop.Value(); \
} else { \
prop = defaultValue; \
}
COPY_FIELD(mType)
COPY_FIELD(mRequestId)
COPY_OPT_FIELD(mSessionId, 0)
COPY_OPT_FIELD(mPowerLevel, 0)
COPY_OPT_FIELD(mTechType, 0)
if (!aOther.mRecords.WasPassed()) {
return;
}
mozilla::dom::Sequence<mozilla::dom::NDEFRecord> const & currentValue = aOther.mRecords.InternalValue();
int count = currentValue.Length();
for (uint32_t i = 0; i < count; i++) {
NDEFRecordStruct record;
record.mTnf = currentValue[i].mTnf.Value();
if (currentValue[i].mType.WasPassed()) {
currentValue[i].mType.Value().ComputeLengthAndData();
record.mType.AppendElements(currentValue[i].mType.Value().Data(),
currentValue[i].mType.Value().Length());
}
if (currentValue[i].mId.WasPassed()) {
currentValue[i].mId.Value().ComputeLengthAndData();
record.mId.AppendElements(currentValue[i].mId.Value().Data(),
currentValue[i].mId.Value().Length());
}
if (currentValue[i].mPayload.WasPassed()) {
currentValue[i].mPayload.Value().ComputeLengthAndData();
record.mPayload.AppendElements(currentValue[i].mPayload.Value().Data(),
currentValue[i].mPayload.Value().Length());
}
mRecords.AppendElement(record);
}
#undef COPY_FIELD
#undef COPY_OPT_FIELD
}
nsString mType;
int32_t mSessionId;
nsString mRequestId;
int32_t mPowerLevel;
int32_t mTechType;
nsTArray<NDEFRecordStruct> mRecords;
};
struct EventOptions
{
EventOptions()
: mType(EmptyString()), mStatus(-1), mSessionId(-1), mRequestId(EmptyString()), mMajorVersion(-1), mMinorVersion(-1),
mIsReadOnly(-1), mCanBeMadeReadOnly(-1), mMaxSupportedLength(-1), mPowerLevel(-1)
{}
nsString mType;
int32_t mStatus;
int32_t mSessionId;
nsString mRequestId;
int32_t mMajorVersion;
int32_t mMinorVersion;
nsTArray<uint8_t> mTechList;
nsTArray<NDEFRecordStruct> mRecords;
int32_t mIsReadOnly;
int32_t mCanBeMadeReadOnly;
int32_t mMaxSupportedLength;
int32_t mPowerLevel;
};
} // namespace mozilla
#endif

View File

@ -0,0 +1,352 @@
/* 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 "NfcService.h"
#include <binder/Parcel.h>
#include "mozilla/ModuleUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/RootedDictionary.h"
#include "nsAutoPtr.h"
#include "nsCxPusher.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "NfcOptions.h"
#define NS_NFCSERVICE_CID \
{ 0x584c9a21, 0x4e17, 0x43b7, {0xb1, 0x6a, 0x87, 0xa0, 0x42, 0xef, 0xd4, 0x64} }
#define NS_NFCSERVICE_CONTRACTID "@mozilla.org/nfc/service;1"
using namespace android;
using namespace mozilla::dom;
using namespace mozilla::ipc;
nsLiteralString NfcTechString[] = {
NS_LITERAL_STRING("NDEF"),
NS_LITERAL_STRING("NDEF_WRITEABLE"),
NS_LITERAL_STRING("NDEF_FORMATABLE"),
NS_LITERAL_STRING("P2P"),
NS_LITERAL_STRING("NFC_A"),
NS_LITERAL_STRING("NFC_B"),
NS_LITERAL_STRING("NFC_F"),
NS_LITERAL_STRING("NFC_V"),
NS_LITERAL_STRING("NFC_ISO_DEP"),
NS_LITERAL_STRING("MIFARE_CLASSIC"),
NS_LITERAL_STRING("MIFARE_ULTRALIGHT"),
NS_LITERAL_STRING("BARCODE")
};
namespace mozilla {
static NfcService* gNfcService;
NS_IMPL_ISUPPORTS(NfcService, nsINfcService)
void
assertIsNfcServiceThread()
{
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
MOZ_ASSERT(thread == gNfcService->GetThread());
}
// Runnable used to call Marshall on the NFC thread.
class NfcCommandRunnable : public nsRunnable
{
public:
NfcCommandRunnable(NfcMessageHandler* aHandler, NfcConsumer* aConsumer, CommandOptions aOptions)
: mHandler(aHandler), mConsumer(aConsumer), mOptions(aOptions)
{
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHOD Run()
{
assertIsNfcServiceThread();
Parcel parcel;
parcel.writeInt32(0); // Parcel Size.
mHandler->Marshall(parcel, mOptions);
parcel.setDataPosition(0);
uint32_t sizeBE = htonl(parcel.dataSize() - sizeof(int));
parcel.writeInt32(sizeBE);
mConsumer->PostToNfcDaemon(parcel.data(), parcel.dataSize());
return NS_OK;
}
private:
NfcMessageHandler* mHandler;
NfcConsumer* mConsumer;
CommandOptions mOptions;
};
// Runnable used dispatch the NfcEventOptions on the main thread.
class NfcEventDispatcher : public nsRunnable
{
public:
NfcEventDispatcher(EventOptions& aEvent)
: mEvent(aEvent)
{
assertIsNfcServiceThread();
}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
mozilla::AutoSafeJSContext cx;
RootedDictionary<NfcEventOptions> event(cx);
// the copy constructor is private.
#define COPY_FIELD(prop) event.prop = mEvent.prop;
#define COPY_OPT_FIELD(prop, defaultValue) \
if (mEvent.prop != defaultValue) { \
event.prop.Construct(); \
event.prop.Value() = mEvent.prop; \
}
COPY_FIELD(mType)
COPY_OPT_FIELD(mRequestId, EmptyString())
COPY_OPT_FIELD(mStatus, -1)
COPY_OPT_FIELD(mSessionId, -1)
COPY_OPT_FIELD(mMajorVersion, -1)
COPY_OPT_FIELD(mMinorVersion, -1)
COPY_OPT_FIELD(mPowerLevel, -1)
if (mEvent.mTechList.Length() > 0) {
int length = mEvent.mTechList.Length();
event.mTechList.Construct();
if (!event.mTechList.Value().SetCapacity(length)) {
return NS_ERROR_FAILURE;
}
for (int i = 0; i < length; i++) {
nsString& elem = *event.mTechList.Value().AppendElement();
elem = NfcTechString[mEvent.mTechList[i]];
}
}
if (mEvent.mRecords.Length() > 0) {
int length = mEvent.mRecords.Length();
event.mRecords.Construct();
if (!event.mRecords.Value().SetCapacity(length)) {
return NS_ERROR_FAILURE;
}
for (int i = 0; i < length; i++) {
NDEFRecordStruct& recordStruct = mEvent.mRecords[i];
NDEFRecord& record = *event.mRecords.Value().AppendElement();
record.mTnf.Construct();
record.mTnf.Value() = recordStruct.mTnf;
if (recordStruct.mType.Length() > 0) {
record.mType.Construct();
record.mType.Value().Init(Uint8Array::Create(cx, recordStruct.mType.Length(), recordStruct.mType.Elements()));
}
if (recordStruct.mId.Length() > 0) {
record.mId.Construct();
record.mId.Value().Init(Uint8Array::Create(cx, recordStruct.mId.Length(), recordStruct.mId.Elements()));
}
if (recordStruct.mPayload.Length() > 0) {
record.mPayload.Construct();
record.mPayload.Value().Init(Uint8Array::Create(cx, recordStruct.mPayload.Length(), recordStruct.mPayload.Elements()));
}
}
}
COPY_OPT_FIELD(mIsReadOnly, -1)
COPY_OPT_FIELD(mCanBeMadeReadOnly, -1)
COPY_OPT_FIELD(mMaxSupportedLength, -1)
#undef COPY_FIELD
#undef COPY_OPT_FIELD
gNfcService->DispatchNfcEvent(event);
return NS_OK;
}
private:
EventOptions mEvent;
};
// Runnable used to call Unmarshall on the NFC thread.
class NfcEventRunnable : public nsRunnable
{
public:
NfcEventRunnable(NfcMessageHandler* aHandler, UnixSocketRawData* aData)
: mHandler(aHandler), mData(aData)
{
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHOD Run()
{
assertIsNfcServiceThread();
size_t size = mData->mSize;
size_t offset = 0;
while (size > 0) {
EventOptions event;
const uint8_t* data = mData->mData.get();
uint32_t parcelSize = ((data[offset + 0] & 0xff) << 24) |
((data[offset + 1] & 0xff) << 16) |
((data[offset + 2] & 0xff) << 8) |
(data[offset + 3] & 0xff);
MOZ_ASSERT(parcelSize <= (mData->mSize - offset));
Parcel parcel;
parcel.setData(&data[offset], parcelSize + sizeof(int));
mHandler->Unmarshall(parcel, event);
nsCOMPtr<nsIRunnable> runnable = new NfcEventDispatcher(event);
NS_DispatchToMainThread(runnable);
size -= parcel.dataSize();
offset += parcel.dataSize();
}
return NS_OK;
}
private:
NfcMessageHandler* mHandler;
nsAutoPtr<UnixSocketRawData> mData;
};
NfcService::NfcService()
: mConsumer(new NfcConsumer(this))
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!gNfcService);
}
NfcService::~NfcService()
{
MOZ_ASSERT(!gNfcService);
}
already_AddRefed<NfcService>
NfcService::FactoryCreate()
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return nullptr;
}
MOZ_ASSERT(NS_IsMainThread());
if (!gNfcService) {
gNfcService = new NfcService();
ClearOnShutdown(&gNfcService);
}
nsRefPtr<NfcService> service = gNfcService;
return service.forget();
}
NS_IMETHODIMP
NfcService::Start(nsINfcEventListener* aListener)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aListener);
MOZ_ASSERT(!mThread);
nsresult rv = NS_NewNamedThread("NfcThread", getter_AddRefs(mThread));
if (NS_FAILED(rv)) {
NS_WARNING("Can't create Nfc worker thread.");
Shutdown();
return NS_ERROR_FAILURE;
}
mListener = aListener;
mHandler = new NfcMessageHandler();
return NS_OK;
}
NS_IMETHODIMP
NfcService::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
if (mThread) {
mThread->Shutdown();
mThread = nullptr;
}
mConsumer->Shutdown();
return NS_OK;
}
NS_IMETHODIMP
NfcService::SendCommand(JS::HandleValue aOptions, JSContext* aCx)
{
MOZ_ASSERT(NS_IsMainThread());
NfcCommandOptions options;
if (!options.Init(aCx, aOptions)) {
NS_WARNING("Bad dictionary passed to NfcService::SendCommand");
return NS_ERROR_FAILURE;
}
// Dispatch the command to the NFC thread.
CommandOptions commandOptions(options);
nsCOMPtr<nsIRunnable> runnable = new NfcCommandRunnable(mHandler, mConsumer, commandOptions);
mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
return NS_OK;
}
void
NfcService::DispatchNfcEvent(const mozilla::dom::NfcEventOptions& aOptions)
{
MOZ_ASSERT(NS_IsMainThread());
mozilla::AutoSafeJSContext cx;
JS::RootedValue val(cx);
if (!ToJSValue(cx, aOptions, &val)) {
return;
}
mListener->OnEvent(val);
}
void
NfcService::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aData)
{
MOZ_ASSERT(mHandler);
nsCOMPtr<nsIRunnable> runnable = new NfcEventRunnable(mHandler, aData.forget());
mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
}
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(NfcService,
NfcService::FactoryCreate)
NS_DEFINE_NAMED_CID(NS_NFCSERVICE_CID);
static const mozilla::Module::CIDEntry kNfcServiceCIDs[] = {
{ &kNS_NFCSERVICE_CID, false, nullptr, NfcServiceConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kNfcServiceContracts[] = {
{ NS_NFCSERVICE_CONTRACTID, &kNS_NFCSERVICE_CID },
{ nullptr }
};
static const mozilla::Module kNfcServiceModule = {
mozilla::Module::kVersion,
kNfcServiceCIDs,
kNfcServiceContracts,
nullptr
};
} // namespace mozilla
NSMODULE_DEFN(NfcServiceModule) = &mozilla::kNfcServiceModule;

View File

@ -0,0 +1,51 @@
/* 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 NfcService_h
#define NfcService_h
#include "mozilla/ipc/Nfc.h"
#include "mozilla/ipc/UnixSocket.h"
#include "nsCOMPtr.h"
#include "nsINfcService.h"
#include "NfcMessageHandler.h"
class nsIThread;
namespace mozilla {
namespace dom {
class NfcEventOptions;
} // namespace dom
class NfcService MOZ_FINAL : public nsINfcService,
public mozilla::ipc::NfcSocketListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINFCSERVICE
static already_AddRefed<NfcService> FactoryCreate();
void DispatchNfcEvent(const mozilla::dom::NfcEventOptions& aOptions);
virtual void
ReceiveSocketData(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aData) MOZ_OVERRIDE;
nsCOMPtr<nsIThread> GetThread() {
return mThread;
}
private:
NfcService();
~NfcService();
nsCOMPtr<nsIThread> mThread;
nsCOMPtr<nsINfcEventListener> mListener;
nsRefPtr<mozilla::ipc::NfcConsumer> mConsumer;
nsAutoPtr<NfcMessageHandler> mHandler;
};
} // namespace mozilla
#endif // NfcService_h

View File

@ -30,9 +30,6 @@
#ifdef MOZ_B2G_RIL
#include "mozilla/ipc/Ril.h"
#endif
#ifdef MOZ_NFC
#include "mozilla/ipc/Nfc.h"
#endif
#include "mozilla/ipc/KeyStore.h"
#include "nsIObserverService.h"
#include "nsCxPusher.h"
@ -125,10 +122,6 @@ SystemWorkerManager::Shutdown()
RilConsumer::Shutdown();
#endif
#ifdef MOZ_NFC
NfcConsumer::Shutdown();
#endif
nsCOMPtr<nsIWifi> wifi(do_QueryInterface(mWifiWorker));
if (wifi) {
wifi->Shutdown();
@ -207,28 +200,6 @@ SystemWorkerManager::RegisterRilWorker(unsigned int aClientId,
#endif // MOZ_B2G_RIL
}
nsresult
SystemWorkerManager::RegisterNfcWorker(JS::Handle<JS::Value> aWorker,
JSContext* aCx)
{
#ifndef MOZ_NFC
return NS_ERROR_NOT_IMPLEMENTED;
#else
NS_ENSURE_TRUE(aWorker.isObject(), NS_ERROR_UNEXPECTED);
JSAutoCompartment ac(aCx, &aWorker.toObject());
WorkerCrossThreadDispatcher* wctd =
GetWorkerCrossThreadDispatcher(aCx, aWorker);
if (!wctd) {
NS_WARNING("Failed to GetWorkerCrossThreadDispatcher for nfc");
return NS_ERROR_FAILURE;
}
return NfcConsumer::Register(wctd);
#endif // MOZ_NFC
}
nsresult
SystemWorkerManager::InitWifi(JSContext *cx)
{

View File

@ -96,8 +96,13 @@ if CONFIG['MOZ_B2G_RIL']:
]
if CONFIG['MOZ_NFC']:
SOURCES += [
'NfcMessageHandler.cpp',
'NfcService.cpp',
]
XPIDL_SOURCES += [
'nsINfcContentHelper.idl',
'nsINfcService.idl',
]
EXTRA_COMPONENTS += [
'Nfc.js',

View File

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
[scriptable, uuid(976d2074-cb40-41f7-a195-8feee42eaace)]
interface nsINfcEventListener : nsISupports
{
void onEvent(in jsval event);
};
[scriptable, uuid(0329e687-87ff-4cf5-8aa5-e876d9d4a375)]
interface nsINfcService : nsISupports
{
void start(in nsINfcEventListener listener);
void shutdown();
[implicit_jscontext]
void sendCommand(in jsval param);
};

View File

@ -7,13 +7,10 @@
/**
* Information about networks that is exposed to network manager API consumers.
*/
[scriptable, builtinclass, uuid(a9ea96a0-407d-11e3-aa6e-0800200c9a66)]
[scriptable, builtinclass, uuid(4984b669-0ee0-4809-ae96-3358a325a6b0)]
interface nsISystemWorkerManager : nsISupports
{
[implicit_jscontext]
void registerRilWorker(in unsigned long aClientId,
in jsval aWorker);
[implicit_jscontext]
void registerNfcWorker(in jsval aWorker);
};

View File

@ -0,0 +1,46 @@
/* 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/. */
dictionary NDEFRecord
{
byte tnf;
Uint8Array type;
Uint8Array id;
Uint8Array payload;
};
dictionary NfcCommandOptions
{
DOMString type = "";
long sessionId;
DOMString requestId = "";
long powerLevel;
long techType;
sequence<NDEFRecord> records;
};
dictionary NfcEventOptions
{
DOMString type = "";
long status;
long sessionId;
DOMString requestId;
long majorVersion;
long minorVersion;
sequence<DOMString> techList;
sequence<NDEFRecord> records;
boolean isReadOnly;
boolean canBeMadeReadOnly;
long maxSupportedLength;
long powerLevel;
};

View File

@ -602,6 +602,7 @@ if CONFIG['MOZ_NFC']:
'MozNFC.webidl',
'MozNFCPeer.webidl',
'MozNFCTag.webidl',
'NfcOptions.webidl',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':

View File

@ -24,7 +24,6 @@
#include "mozilla/ArrayUtils.h"
#include "nsThreadUtils.h" // For NS_IsMainThread.
USING_WORKERS_NAMESPACE
using namespace mozilla::ipc;
namespace {
@ -35,142 +34,33 @@ const char* NFC_SOCKET_NAME = "/dev/socket/nfcd";
// desktop development.
const uint32_t NFC_TEST_PORT = 6400;
nsRefPtr<mozilla::ipc::NfcConsumer> sNfcConsumer;
class ConnectWorkerToNFC : public WorkerTask
{
public:
ConnectWorkerToNFC()
{ }
virtual bool RunTask(JSContext* aCx);
};
class SendNfcSocketDataTask : public nsRunnable
{
public:
SendNfcSocketDataTask(UnixSocketRawData* aRawData)
: mRawData(aRawData)
SendNfcSocketDataTask(NfcConsumer* aConsumer, UnixSocketRawData* aRawData)
: mConsumer(aConsumer), mRawData(aRawData)
{ }
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sNfcConsumer ||
sNfcConsumer->GetConnectionStatus() != SOCKET_CONNECTED) {
if (!mConsumer ||
mConsumer->GetConnectionStatus() != SOCKET_CONNECTED) {
// Probably shuting down.
delete mRawData;
return NS_OK;
}
sNfcConsumer->SendSocketData(mRawData);
mConsumer->SendSocketData(mRawData);
return NS_OK;
}
private:
NfcConsumer* mConsumer;
UnixSocketRawData* mRawData;
};
bool
PostToNFC(JSContext* aCx,
unsigned aArgc,
JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread");
if (args.length() != 1) {
JS_ReportError(aCx, "Expecting one argument with the NFC message");
return false;
}
JS::Value v = args[0];
JSAutoByteString abs;
void* data;
size_t size;
if (v.isString()) {
JS::Rooted<JSString*> str(aCx, v.toString());
if (!abs.encodeUtf8(aCx, str)) {
return false;
}
data = abs.ptr();
size = abs.length();
} else if (!v.isPrimitive()) {
JSObject* obj = v.toObjectOrNull();
if (!JS_IsTypedArrayObject(obj)) {
JS_ReportError(aCx, "Object passed in wasn't a typed array");
return false;
}
uint32_t type = JS_GetArrayBufferViewType(obj);
if (type != js::Scalar::Int8 &&
type != js::Scalar::Uint8 &&
type != js::Scalar::Uint8Clamped) {
JS_ReportError(aCx, "Typed array data is not octets");
return false;
}
size = JS_GetTypedArrayByteLength(obj);
data = JS_GetArrayBufferViewData(obj);
} else {
JS_ReportError(aCx,
"Incorrect argument. Expecting a string or a typed array");
return false;
}
UnixSocketRawData* raw = new UnixSocketRawData(data, size);
nsRefPtr<SendNfcSocketDataTask> task =
new SendNfcSocketDataTask(raw);
NS_DispatchToMainThread(task);
return true;
}
bool
ConnectWorkerToNFC::RunTask(JSContext* aCx)
{
// Set up the postNFCMessage on the function for worker -> NFC thread
// communication.
NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread");
NS_ASSERTION(!JS_IsRunning(aCx), "Are we being called somehow?");
JS::Rooted<JSObject*> workerGlobal(aCx, JS::CurrentGlobalOrNull(aCx));
return !!JS_DefineFunction(aCx, workerGlobal,
"postNfcMessage", PostToNFC, 1, 0);
}
class DispatchNFCEvent : public WorkerTask
{
public:
DispatchNFCEvent(UnixSocketRawData* aMessage)
: mMessage(aMessage)
{ }
virtual bool RunTask(JSContext* aCx);
private:
nsAutoPtr<UnixSocketRawData> mMessage;
};
bool
DispatchNFCEvent::RunTask(JSContext* aCx)
{
JS::Rooted<JSObject*> obj(aCx, JS::CurrentGlobalOrNull(aCx));
JSObject* array = JS_NewUint8Array(aCx, mMessage->mSize);
if (!array) {
return false;
}
JS::Rooted<JS::Value> arrayVal(aCx, JS::ObjectValue(*array));
memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize);
JS::Rooted<JS::Value> rval(aCx);
return JS_CallFunctionName(aCx, obj, "onNfcMessage", JS::HandleValueArray(arrayVal), &rval);
}
class NfcConnector : public mozilla::ipc::UnixSocketConnector
{
public:
@ -279,8 +169,8 @@ NfcConnector::GetSocketAddr(const sockaddr_any& aAddr,
namespace mozilla {
namespace ipc {
NfcConsumer::NfcConsumer(WorkerCrossThreadDispatcher* aDispatcher)
: mDispatcher(aDispatcher)
NfcConsumer::NfcConsumer(NfcSocketListener* aListener)
: mListener(aListener)
, mShutdown(false)
{
mAddress = NFC_SOCKET_NAME;
@ -288,44 +178,34 @@ NfcConsumer::NfcConsumer(WorkerCrossThreadDispatcher* aDispatcher)
ConnectSocket(new NfcConnector(), mAddress.get());
}
nsresult
NfcConsumer::Register(WorkerCrossThreadDispatcher* aDispatcher)
{
MOZ_ASSERT(NS_IsMainThread());
if (sNfcConsumer) {
return NS_ERROR_FAILURE;
}
nsRefPtr<ConnectWorkerToNFC> connection = new ConnectWorkerToNFC();
if (!aDispatcher->PostTask(connection)) {
return NS_ERROR_UNEXPECTED;
}
// Now that we're set up, connect ourselves to the NFC thread.
sNfcConsumer = new NfcConsumer(aDispatcher);
return NS_OK;
}
void
NfcConsumer::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
if (sNfcConsumer) {
sNfcConsumer->mShutdown = true;
sNfcConsumer->CloseSocket();
sNfcConsumer = nullptr;
}
mShutdown = true;
CloseSocket();
}
bool
NfcConsumer::PostToNfcDaemon(const uint8_t* aData, size_t aSize)
{
MOZ_ASSERT(!NS_IsMainThread());
UnixSocketRawData* raw = new UnixSocketRawData(aData, aSize);
nsRefPtr<SendNfcSocketDataTask> task = new SendNfcSocketDataTask(this, raw);
NS_DispatchToMainThread(task);
return true;
}
void
NfcConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
NfcConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aData)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<DispatchNFCEvent> dre(new DispatchNFCEvent(aMessage.forget()));
mDispatcher->PostTask(dre);
if (mListener) {
mListener->ReceiveSocketData(aData);
}
}
void

View File

@ -9,31 +9,35 @@
#ifndef mozilla_ipc_Nfc_h
#define mozilla_ipc_Nfc_h 1
#include <mozilla/dom/workers/Workers.h>
#include <mozilla/ipc/UnixSocket.h>
namespace mozilla {
namespace ipc {
class NfcSocketListener
{
public:
virtual void ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aData) = 0;
};
class NfcConsumer : public mozilla::ipc::UnixSocketConsumer
{
public:
NfcConsumer(NfcSocketListener* aListener);
virtual ~NfcConsumer() { }
static nsresult Register(mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher);
static void Shutdown();
void Shutdown();
bool PostToNfcDaemon(const uint8_t* aData, size_t aSize);
private:
NfcConsumer(mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher);
virtual void ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage);
virtual void ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aData);
virtual void OnConnectSuccess();
virtual void OnConnectError();
virtual void OnDisconnect();
private:
nsRefPtr<mozilla::dom::workers::WorkerCrossThreadDispatcher> mDispatcher;
NfcSocketListener* mListener;
nsCString mAddress;
bool mShutdown;
};