From 680ab4c2587614e1e10fed46d8caaafea6ec18c2 Mon Sep 17 00:00:00 2001 From: dlee Date: Fri, 9 Jan 2015 10:42:48 +0800 Subject: [PATCH] Bug 1109456 - Support NFC tag transceive WebAPI. r=smaug, yoshi --- dom/nfc/NfcContentHelper.js | 33 ++++++++++++++++++++++ dom/nfc/gonk/Nfc.js | 7 ++++- dom/nfc/gonk/NfcGonkMessage.h | 2 ++ dom/nfc/gonk/NfcMessageHandler.cpp | 54 ++++++++++++++++++++++++++++++++++++ dom/nfc/gonk/NfcMessageHandler.h | 4 ++- dom/nfc/gonk/NfcOptions.h | 13 +++++++++ dom/nfc/gonk/NfcService.cpp | 6 ++++ dom/nfc/nsINfcContentHelper.idl | 27 ++++++++++++++++-- dom/nfc/nsNfc.js | 19 +++++++++++++ dom/webidl/MozNFCTag.webidl | 6 ++++ dom/webidl/NfcOptions.webidl | 6 ++++ 11 files changed, 173 insertions(+), 4 deletions(-) --- dom/nfc/NfcContentHelper.js | 33 ++++++++++++++++++ dom/nfc/gonk/Nfc.js | 7 +++- dom/nfc/gonk/NfcGonkMessage.h | 2 ++ dom/nfc/gonk/NfcMessageHandler.cpp | 54 ++++++++++++++++++++++++++++++ dom/nfc/gonk/NfcMessageHandler.h | 4 ++- dom/nfc/gonk/NfcOptions.h | 13 +++++++ dom/nfc/gonk/NfcService.cpp | 6 ++++ dom/nfc/nsINfcContentHelper.idl | 27 +++++++++++++-- dom/nfc/nsNfc.js | 19 +++++++++++ dom/webidl/MozNFCTag.webidl | 6 ++++ dom/webidl/NfcOptions.webidl | 6 ++++ 11 files changed, 173 insertions(+), 4 deletions(-) diff --git a/dom/nfc/NfcContentHelper.js b/dom/nfc/NfcContentHelper.js index 30fdbf298762..5133050df7e3 100644 --- a/dom/nfc/NfcContentHelper.js +++ b/dom/nfc/NfcContentHelper.js @@ -55,6 +55,7 @@ const NFC_IPC_MSG_NAMES = [ "NFC:WriteNDEFResponse", "NFC:MakeReadOnlyResponse", "NFC:FormatResponse", + "NFC:TransceiveResponse", "NFC:ConnectResponse", "NFC:CloseResponse", "NFC:CheckP2PRegistrationResponse", @@ -174,6 +175,18 @@ NfcContentHelper.prototype = { }); }, + transceive: function transceive(sessionToken, technology, command, callback) { + let requestId = callback.getCallbackId(); + this._requestMap[requestId] = callback; + + cpmm.sendAsyncMessage("NFC:Transceive", { + requestId: requestId, + sessionToken: sessionToken, + technology: technology, + command: command + }); + }, + connect: function connect(techType, sessionToken, callback) { let requestId = callback.getCallbackId(); this._requestMap[requestId] = callback; @@ -280,6 +293,9 @@ NfcContentHelper.prototype = { case "NFC:CheckP2PRegistrationResponse": this.handleCheckP2PRegistrationResponse(result); break; + case "NFC:TransceiveResponse": + this.handleTransceiveResponse(result); + break; case "NFC:ConnectResponse": // Fall through. case "NFC:CloseResponse": case "NFC:WriteNDEFResponse": @@ -394,6 +410,23 @@ NfcContentHelper.prototype = { // The receiver must check the boolean mapped status code to handle. callback.notifySuccessWithBoolean(!result.errorMsg); }, + + handleTransceiveResponse: function handleTransceiveResponse(result) { + let requestId = result.requestId; + let callback = this._requestMap[requestId]; + if (!callback) { + debug("not firing message handleTransceiveResponse for id: " + requestId); + return; + } + delete this._requestMap[requestId]; + + if (result.errorMsg) { + callback.notifyError(result.errorMsg); + return; + } + + callback.notifySuccessWithByteArray(result.response); + }, }; function TagNDEFInfo(tagType, maxNDEFSize, isReadOnly, isFormatable) { diff --git a/dom/nfc/gonk/Nfc.js b/dom/nfc/gonk/Nfc.js index 2afaddd5b8cc..1b3579a94f37 100644 --- a/dom/nfc/gonk/Nfc.js +++ b/dom/nfc/gonk/Nfc.js @@ -65,7 +65,8 @@ const NFC_IPC_MSG_ENTRIES = [ "NFC:Close", "NFC:WriteNDEF", "NFC:MakeReadOnly", - "NFC:Format"] }, + "NFC:Format", + "NFC:Transceive"] }, { permission: "nfc-share", messages: ["NFC:SendFile", @@ -518,6 +519,7 @@ Nfc.prototype = { case "ReadNDEFResponse": case "MakeReadOnlyResponse": case "FormatResponse": + case "TransceiveResponse": case "WriteNDEFResponse": this.sendNfcResponse(message); break; @@ -574,6 +576,9 @@ Nfc.prototype = { case "NFC:Format": this.sendToNfcService("format", message.data); break; + case "NFC:Transceive": + this.sendToNfcService("transceive", message.data); + break; case "NFC:Connect": this.sendToNfcService("connect", message.data); break; diff --git a/dom/nfc/gonk/NfcGonkMessage.h b/dom/nfc/gonk/NfcGonkMessage.h index 5b5ce3cd3ef2..a0215307749c 100644 --- a/dom/nfc/gonk/NfcGonkMessage.h +++ b/dom/nfc/gonk/NfcGonkMessage.h @@ -18,12 +18,14 @@ enum NfcRequest { WriteNDEFReq, MakeReadOnlyReq, FormatReq, + TransceiveReq, }; enum NfcResponse { GeneralRsp = 1000, ChangeRFStateRsp, ReadNDEFRsp, + TransceiveRsp }; enum NfcNotification { diff --git a/dom/nfc/gonk/NfcMessageHandler.cpp b/dom/nfc/gonk/NfcMessageHandler.cpp index 0c44417eb309..73210552d29b 100644 --- a/dom/nfc/gonk/NfcMessageHandler.cpp +++ b/dom/nfc/gonk/NfcMessageHandler.cpp @@ -22,6 +22,7 @@ static const char* kReadNDEFRequest = "readNDEF"; static const char* kWriteNDEFRequest = "writeNDEF"; static const char* kMakeReadOnlyRequest = "makeReadOnly"; static const char* kFormatRequest = "format"; +static const char* kTransceiveRequest = "transceive"; static const char* kConnectRequest = "connect"; static const char* kCloseRequest = "close"; @@ -30,6 +31,7 @@ static const char* kReadNDEFResponse = "ReadNDEFResponse"; static const char* kWriteNDEFResponse = "WriteNDEFResponse"; static const char* kMakeReadOnlyResponse = "MakeReadOnlyResponse"; static const char* kFormatResponse = "FormatResponse"; +static const char* kTransceiveResponse = "TransceiveResponse"; static const char* kConnectResponse = "ConnectResponse"; static const char* kCloseResponse = "CloseResponse"; @@ -58,6 +60,9 @@ NfcMessageHandler::Marshall(Parcel& aParcel, const CommandOptions& aOptions) } else if (!strcmp(type, kFormatRequest)) { result = FormatRequest(aParcel, aOptions); mPendingReqQueue.AppendElement(NfcRequest::FormatReq); + } else if (!strcmp(type, kTransceiveRequest)) { + result = TransceiveRequest(aParcel, aOptions); + mPendingReqQueue.AppendElement(NfcRequest::TransceiveReq); } else if (!strcmp(type, kConnectRequest)) { result = ConnectRequest(aParcel, aOptions); mPendingReqQueue.AppendElement(NfcRequest::ConnectReq); @@ -88,6 +93,9 @@ NfcMessageHandler::Unmarshall(const Parcel& aParcel, EventOptions& aOptions) case NfcResponse::ReadNDEFRsp: result = ReadNDEFResponse(aParcel, aOptions); break; + case NfcResponse::TransceiveRsp: + result = TransceiveResponse(aParcel, aOptions); + break; case NfcNotification::Initialized: result = InitializeNotification(aParcel, aOptions); break; @@ -196,6 +204,24 @@ NfcMessageHandler::ReadNDEFResponse(const Parcel& aParcel, EventOptions& aOption return true; } +bool +NfcMessageHandler::TransceiveResponse(const Parcel& aParcel, EventOptions& aOptions) +{ + aOptions.mType = NS_ConvertUTF8toUTF16(kTransceiveResponse); + aOptions.mErrorCode = aParcel.readInt32(); + aOptions.mSessionId = aParcel.readInt32(); + + NS_ENSURE_TRUE(!mRequestIdQueue.IsEmpty(), false); + aOptions.mRequestId = mRequestIdQueue[0]; + mRequestIdQueue.RemoveElementAt(0); + + if (aOptions.mErrorCode == NfcErrorCode::Success) { + ReadTransceiveResponse(aParcel, aOptions); + } + + return true; +} + bool NfcMessageHandler::WriteNDEFRequest(Parcel& aParcel, const CommandOptions& aOptions) { @@ -225,6 +251,23 @@ NfcMessageHandler::FormatRequest(Parcel& aParcel, const CommandOptions& aOptions return true; } +bool +NfcMessageHandler::TransceiveRequest(Parcel& aParcel, const CommandOptions& aOptions) +{ + aParcel.writeInt32(NfcRequest::TransceiveReq); + aParcel.writeInt32(aOptions.mSessionId); + aParcel.writeInt32(aOptions.mTechnology); + + uint32_t length = aOptions.mCommand.Length(); + aParcel.writeInt32(length); + + void* data = aParcel.writeInplace(length); + memcpy(data, aOptions.mCommand.Elements(), length); + + mRequestIdQueue.AppendElement(aOptions.mRequestId); + return true; +} + bool NfcMessageHandler::ConnectRequest(Parcel& aParcel, const CommandOptions& aOptions) { @@ -374,3 +417,14 @@ NfcMessageHandler::WriteNDEFMessage(Parcel& aParcel, const CommandOptions& aOpti return true; } + +bool +NfcMessageHandler::ReadTransceiveResponse(const Parcel& aParcel, EventOptions& aOptions) +{ + uint32_t length = aParcel.readInt32(); + + aOptions.mResponse.AppendElements( + static_cast(aParcel.readInplace(length)), length); + + return true; +} diff --git a/dom/nfc/gonk/NfcMessageHandler.h b/dom/nfc/gonk/NfcMessageHandler.h index 0068060684db..8963a3b9cda4 100644 --- a/dom/nfc/gonk/NfcMessageHandler.h +++ b/dom/nfc/gonk/NfcMessageHandler.h @@ -32,6 +32,8 @@ private: bool WriteNDEFRequest(android::Parcel& aParcel, const CommandOptions& options); bool MakeReadOnlyRequest(android::Parcel& aParcel, const CommandOptions& options); bool FormatRequest(android::Parcel& aParcel, const CommandOptions& options); + bool TransceiveRequest(android::Parcel& aParcel, const CommandOptions& options); + bool TransceiveResponse(const android::Parcel& aParcel, EventOptions& aOptions); bool ConnectRequest(android::Parcel& aParcel, const CommandOptions& options); bool CloseRequest(android::Parcel& aParcel, const CommandOptions& options); @@ -42,7 +44,7 @@ private: bool ReadNDEFMessage(const android::Parcel& aParcel, EventOptions& aOptions); bool WriteNDEFMessage(android::Parcel& aParcel, const CommandOptions& aOptions); - + bool ReadTransceiveResponse(const android::Parcel& aParcel, EventOptions& aOptions); private: nsTArray mPendingReqQueue; nsTArray mRequestIdQueue; diff --git a/dom/nfc/gonk/NfcOptions.h b/dom/nfc/gonk/NfcOptions.h index d337aedd35e3..bf689216a750 100644 --- a/dom/nfc/gonk/NfcOptions.h +++ b/dom/nfc/gonk/NfcOptions.h @@ -38,6 +38,16 @@ struct CommandOptions COPY_OPT_FIELD(mTechType, 0) COPY_OPT_FIELD(mIsP2P, false) + mTechnology = aOther.mTechnology.WasPassed() ? + static_cast(aOther.mTechnology.Value()) : + -1; + + if (aOther.mCommand.WasPassed()) { + dom::Uint8Array const & currentValue = aOther.mCommand.InternalValue(); + currentValue.ComputeLengthAndData(); + mCommand.AppendElements(currentValue.Data(), currentValue.Length()); + } + if (!aOther.mRecords.WasPassed()) { return; } @@ -80,6 +90,8 @@ struct CommandOptions int32_t mTechType; bool mIsP2P; nsTArray mRecords; + int32_t mTechnology; + nsTArray mCommand; }; struct EventOptions @@ -112,6 +124,7 @@ struct EventOptions int32_t mOriginIndex; nsTArray mAid; nsTArray mPayload; + nsTArray mResponse; }; } // namespace mozilla diff --git a/dom/nfc/gonk/NfcService.cpp b/dom/nfc/gonk/NfcService.cpp index 0faf8e953e43..800c1f006967 100644 --- a/dom/nfc/gonk/NfcService.cpp +++ b/dom/nfc/gonk/NfcService.cpp @@ -198,6 +198,12 @@ public: event.mPayload.Value().Init(Uint8Array::Create(cx, mEvent.mPayload.Length(), mEvent.mPayload.Elements())); } + if (mEvent.mResponse.Length() > 0) { + event.mResponse.Construct(); + event.mResponse.Value().Init( + Uint8Array::Create(cx, mEvent.mResponse.Length(), mEvent.mResponse.Elements())); + } + #undef COPY_FIELD #undef COPY_OPT_FIELD diff --git a/dom/nfc/nsINfcContentHelper.idl b/dom/nfc/nsINfcContentHelper.idl index e44253766996..0e48d56aa003 100644 --- a/dom/nfc/nsINfcContentHelper.idl +++ b/dom/nfc/nsINfcContentHelper.idl @@ -86,7 +86,7 @@ interface nsINfcEventListener : nsISupports void notifyRFStateChange(in DOMString rfState); }; -[scriptable, uuid(a8ef3590-d853-4766-b54a-a4547da4dde4)] +[scriptable, uuid(6c913015-9658-46a9-88d9-6ecfda2bd020)] interface nsINfcRequestCallback : nsISupports { DOMString getCallbackId(); @@ -97,10 +97,12 @@ interface nsINfcRequestCallback : nsISupports void notifySuccessWithNDEFRecords(in nsIVariant records); + void notifySuccessWithByteArray(in nsIVariant array); + void notifyError(in DOMString errorMsg); }; -[scriptable, uuid(c5fdf956-735e-45d3-aa25-3a871bd3e2f8)] +[scriptable, uuid(0f8aae32-9920-491e-a197-8995941d54df)] interface nsINfcContentHelper : nsISupports { void init(in nsIDOMWindow window); @@ -157,6 +159,27 @@ interface nsINfcContentHelper : nsISupports void format(in DOMString sessionToken, in nsINfcRequestCallback callback); + /** + * Send raw command to the tag and receive the response. + * + * @param sessionToken + * Current token + * + * @param technology + * Tag technology + * + * @param command + * Command to send + * + * @param callback + * Called when request is finished + * + */ + void transceive(in DOMString sessionToken, + in DOMString technology, + in nsIVariant command, + in nsINfcRequestCallback callback); + /** * Enable I/O operations to the tag * diff --git a/dom/nfc/nsNfc.js b/dom/nfc/nsNfc.js index 7033155ba7df..dff81518b2f8 100644 --- a/dom/nfc/nsNfc.js +++ b/dom/nfc/nsNfc.js @@ -74,6 +74,15 @@ NfcCallback.prototype = { resolver.resolve(aRecords); }, + notifySuccessWithByteArray: function notifySuccessWithByteArray(aArray) { + let resolver = this.takePromiseResolver(atob(this._requestId)); + if (!resolver) { + debug("can not find promise resolver for id: " + this._requestId); + return; + } + resolver.resolve(aArray); + }, + notifyError: function notifyError(aErrorMsg) { let resolver = this.takePromiseResolver(atob(this._requestId)); if (!resolver) { @@ -192,6 +201,16 @@ MozNFCTagImpl.prototype = { return callback.promise; }, + transceive: function transceive(tech, cmd) { + if (this.isLost) { + throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid"); + } + + let callback = new NfcCallback(this._window); + this._nfcContentHelper.transceive(this.session, tech, cmd, callback); + return callback.promise; + }, + notifyLost: function notifyLost() { this.isLost = true; }, diff --git a/dom/webidl/MozNFCTag.webidl b/dom/webidl/MozNFCTag.webidl index 07f564b4ef05..f89056270649 100644 --- a/dom/webidl/MozNFCTag.webidl +++ b/dom/webidl/MozNFCTag.webidl @@ -109,4 +109,10 @@ partial interface MozNFCTag { [ChromeOnly] void notifyLost(); + + /** + * Send raw command to tag and receive the response. + */ + [ChromeOnly, Throws] + Promise transceive(NFCTechType tech, Uint8Array command); }; diff --git a/dom/webidl/NfcOptions.webidl b/dom/webidl/NfcOptions.webidl index 7ff86b06fa32..758ddb798e86 100644 --- a/dom/webidl/NfcOptions.webidl +++ b/dom/webidl/NfcOptions.webidl @@ -21,6 +21,9 @@ dictionary NfcCommandOptions boolean isP2P; sequence records; + + NFCTechType technology; + Uint8Array command; }; dictionary NfcEventOptions @@ -51,4 +54,7 @@ dictionary NfcEventOptions DOMString origin; Uint8Array aid; Uint8Array payload; + + // Tag transceive response data + Uint8Array response; };