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(-)
This commit is contained in:
dlee 2015-01-09 10:42:48 +08:00
parent a2cbd8e9e6
commit 680ab4c258
11 changed files with 173 additions and 4 deletions

View File

@ -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) {

View File

@ -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;

View File

@ -18,12 +18,14 @@ enum NfcRequest {
WriteNDEFReq,
MakeReadOnlyReq,
FormatReq,
TransceiveReq,
};
enum NfcResponse {
GeneralRsp = 1000,
ChangeRFStateRsp,
ReadNDEFRsp,
TransceiveRsp
};
enum NfcNotification {

View File

@ -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<const uint8_t*>(aParcel.readInplace(length)), length);
return true;
}

View File

@ -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<int32_t> mPendingReqQueue;
nsTArray<nsString> mRequestIdQueue;

View File

@ -38,6 +38,16 @@ struct CommandOptions
COPY_OPT_FIELD(mTechType, 0)
COPY_OPT_FIELD(mIsP2P, false)
mTechnology = aOther.mTechnology.WasPassed() ?
static_cast<int32_t>(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<NDEFRecordStruct> mRecords;
int32_t mTechnology;
nsTArray<uint8_t> mCommand;
};
struct EventOptions
@ -112,6 +124,7 @@ struct EventOptions
int32_t mOriginIndex;
nsTArray<uint8_t> mAid;
nsTArray<uint8_t> mPayload;
nsTArray<uint8_t> mResponse;
};
} // namespace mozilla

View File

@ -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

View File

@ -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
*

View File

@ -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;
},

View File

@ -109,4 +109,10 @@ partial interface MozNFCTag {
[ChromeOnly]
void notifyLost();
/**
* Send raw command to tag and receive the response.
*/
[ChromeOnly, Throws]
Promise<Uint8Array> transceive(NFCTechType tech, Uint8Array command);
};

View File

@ -21,6 +21,9 @@ dictionary NfcCommandOptions
boolean isP2P;
sequence<MozNDEFRecordOptions> 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;
};