Closed OlivierGrenoble closed 3 years ago
To be sure I understand this correctly, you're saying that Android NFC service reads content of NDEF tags and starts an "NFC tag read" activity after website has successfully written something to the NFC tag.
In that case, I wonder if the browser should "catch" all NFC tags discovered events and dispatch them appropriately, so that web developers don't have to run scan()
to catch them manually.
What do you think @leonhsl?
This is what the ignoreRead was for
@beaufortfrancois I still had the plan to join the reader/writer to avoid cases like these
ignoreRead
is used with an NDEFReader scan session. I think @OlivierGrenoble is using only NDEFWriter there. In that case, it is not necessarily clear he needs to run NDEFReader scan().
I still had the plan to join the reader/writer to avoid cases like these
I would support that, but IIRC wasn't there an argument to keep them separate because they might be handled in completely different code? IIRC you had a use case for that in a web app.
So if we keep them separate, we need a control or a default policy at writer level. Let's see if default policy is enough.
This is what the ignoreRead was for
On my side it doesn't work. I have set it to true and I still get the native processing of NDEF when I call the write function.
With the previous WebNFC API I didn't have this problem so I guess that, at that time, ignoreRead was working
To be sure I understand this correctly, you're saying that Android NFC service reads content of NDEF tags and starts an "NFC tag read" activity after website has successfully written something to the NFC tag.
Yes. I start with a Tag containing no NDEF. I use the write function to write an URI. The then() function is called indicating that the write was successful. At the same time, the NFC service processes the URI NDEF and proposes to open Chrome. If I accept, Chrome is opened on the URI that I have written.
A side note: Chrome could be opened directly (it would be the case for most users) but, on my phone, I have several applications able to process this NDEF so Android asks which one to launch.
I don't think this relates to ignoreRead
, which is only for ignoring active WebNFC scan()
sessions when writing and does not (cannot) affect how Android handles the tag.
Did this work for you? I mean can it forbid Android from reading the tag? @OlivierGrenoble
I have been told to run scan() and discard the onreading events.
I don't think this relates to
ignoreRead
, which is only for ignoring active WebNFCscan()
sessions when writing and does not (cannot) affect how Android handles the tag.
Thanks for this clarification, that 's good to know.
Did this work for you? I mean can it forbid Android from reading the tag? @OlivierGrenoble
I have been told to run scan() and discard the onreading events.
No, for the moment I haven't been able to get it work. I have started a scan() before calling the write function but the difficult point is to know when to abort this scan. I have done it in the then() function of writer.write() but I still get the native processing. Based on what you said, I'm now thinking that I should set ignoreRead to false to be sure that my callbacks "catched" the read notification and discard it.
Would you have an example of code writing an NDEF without triggering the native processing of this NDEF?
My 2 cents: I have the feeling that we are not going in the right direction here. It is too cumbersome.
Saying it again in case it was not clear, I feel like this is the UA responsibility to block/catch "discovered NFC tags events" and dispatch them appropriately.
@OlivierGrenoble How would like it work?
Yes, starting a scan
beforehand is not helpful for the scenario, no matter ignoreRead
is true
or false
.
And, by my current understanding, seems Browser has no way to block the native processing of NDEF? I mean, Browser is just one of the clients of Android NFC service, it can get the "discovered NFC tags events" but is not able to block other clients getting them.
And, by my current understanding, seems Browser has no way to block the native processing of NDEF? I mean, Browser is just one of the clients of Android NFC service, it can get the "discovered NFC tags events" but is not able to block other clients getting them.
When reading NDEF tags with Web NFC, the native "Android NFC tag" activity is not launched. This is the root issue from what I understand. Chrome should catch it and dispatch it to the website using Web NFC scan. If not, it should "hold" it.
@OlivierGrenoble Can you share/record a screencast of what's currently happening so that everyone get a good understanding?
@OlivierGrenoble How would like it work?
I need some time to think about it. Here are some elements:
In my application, I didn't want to have a scan() listening for NFC events all the time because this would take the hand over the native processing of an NFC event. So I'm calling scan() only when the user has clicked on a button to read the tag content.
We can maybe find inspiration from what is done in Android with enableforegrounddispatch() function: https://developer.android.com/reference/android/nfc/NfcAdapter#enableForegroundDispatch(android.app.Activity,%20android.app.PendingIntent,%20android.content.IntentFilter%5B%5D,%20java.lang.String%5B%5D%5B%5D)
The WebPage would subscribe to get the NFC notifications but only when it is in the foreground. If the user changes Tab in Chrome or goes to another application, the WebPage would stop catching the NFC events otherwise it would prevent other applications from getting them.
@OlivierGrenoble Can you share/record a screencast of what's currently happening so that everyone get a good understanding?
Here is a screencast (in a zip file). I have clicked on "Write NFC Tag", then on "Write URI", then I have taped the NFC Tag. The URI is written successfully. As you can see, I immediately gets a notification to open the URI in Chrome.
The WebPage would subscribe to get the NFC notifications but only when it is in the foreground. If the user changes Tab in Chrome or goes to another application, the WebPage would stop catching the NFC events otherwise it would prevent other applications from getting them.
What I have told here was too simple. I don't think that the web page should receive every NFC events while it is in foreground. I think that it should listen to NFC events only when it is expecting something.
Here is a screencast (in a zip file). I have clicked on "Write NFC Tag", then on "Write URI", then I have taped the NFC Tag. The URI is written successfully. As you can see, I immediately gets a notification to open the URI in Chrome.
Based on the video I would say things work as they are designed to work (*) and also according to your preference to not interfere with other apps on reading.
(*) But IIUC you would prefer that Web NFC implementation would create and "own" a sort of "tag writing session" in a way that re-reading that tag would get dispatched to Web NFC and discarded there?
Of course the app could do that by:
ignoreRead=true
However, others may come and say "I specifically want to test written tags by allowing default dispatching of NFC content."
So Web NFC could include another write option to control that policy. The default behaviour could be discussed further.
Now IIUC @kenchris, the intent with ignoreRead
was actually that behaviour (in which case we need to update the write algorithm and don't need another write option).
Here is a screencast (in a zip file). I have clicked on "Write NFC Tag", then on "Write URI", then I have taped the NFC Tag. The URI is written successfully. As you can see, I immediately gets a notification to open the URI in Chrome.
Based on the video I would say things work as they are designed to work (*) and also according to your preference to not interfere with other apps on reading.
I confirm. I have started a scan, then moved to another Chrome Tab and then taped a tag. The native processing is executed so that's fine.
(*) But IIUC you would prefer that Web NFC implementation would create and "own" a sort of "tag writing session" in a way that re-reading that tag would get dispatched to Web NFC and discarded there?
Yes and I was proposing to make it the default behavior.
Of course the app could do that by:
- setting up a scan
- write with
ignoreRead=true
- ignoring the read event. But I understood that was not your preference, right?
This is not easy to do. For the moment I haven't succeed to get a version working reliably. Here is my code. I had to use ignoreRead=false in order to be sure that the NFC event get hooked by my application and doesn't get processed natively.
function writeUriNdef(uriTxt) {
console.log("writeUriNdef: " + uriTxt);
if (!('NDEFWriter' in window)) {
showWebNfcWarning();
return;
}
if (!window.isSecureContext) {
showSecureContextWarning();
return;
}
var message = {
records : [
{
recordType: "url",
data: uriTxt
}
]
};
console.log("Please tap the NFC tag to write...");
showDialog("Please tap the NFC tag to write...", "", cancelPushRequest);
writerAbortController = new AbortController();
writerAbortController.signal.onabort = event => {
console.log("All NFC Write operations have been aborted.");
};
readerAbortController = new AbortController();
readerAbortController.signal.onabort = event => {
console.log("All NFC Read operations have been aborted.");
};
const reader = new NDEFReader();
const writer = new NDEFWriter();
reader.scan({ signal: readerAbortController.signal })
.then(() => {
reader.onreading = event => {
console.log("NFC read event ignored (write in progress)");
};
}).catch(error => {
console.log("Read failed: " + error.name);
});
writer.write(message, { signal: writerAbortController.signal , ignoreRead: false })
.then(() => {
console.log("NDEF written successfully.");
showDialog("NDEF written successfully.");
readerAbortController.abort();
})
.catch((error) => {
console.log("error: " + error.name);
switch(error.name) {
case "NotSupportedError":
showDialog('Please turn NFC ON');
break;
case "AbortError":
//showDialog('Abort error');
break;
default:
showDialog('Push Failed, please try again: ' + error.name);
break;
}
});
}
function cancelPushRequest() {
console.log("cancelPushRequest");
writerAbortController.abort();
readerAbortController.abort();
}
Here is the console: script.js:413 writeUriNdef: http://www.st.com/st25 script.js:434 Please tap the NFC tag to write... script.js:453 NFC read event ignored (write in progress) script.js:461 NDEF written successfully. script.js:484 cancelPushRequest script.js:439 All NFC Write operations have been aborted. script.js:444 All NFC Read operations have been aborted.
"NFC read event ignored" has been called, which is fine but I still get the native processing.
Now IIUC @kenchris, the intent with
ignoreRead
was actually that behaviour (in which case we need to update the write algorithm and don't need another write option).
2 years ago, I was using the previous WebNFC API and I didn't have this problem. I was using ignoreRead=true and I think that it was discarding the read event.
@leonhsl, @kenchris , @beaufortfrancois: any objections against changing the write algorithm to use ignoreRead
as described above?
Can you summarize?
I dont really follow these comments.
ignoreRead was added because if you want to write to a tag, that tag might have existing content, and you don't want a read event dispatches for that during the write (as data is already read when the nfc tag is powered up)
Yep I have the same understanding on ignoreRead
with the above comment.
ignoreRead was added because if you want to write to a tag, that tag might have existing content, and you don't want a read event dispatches for that during the write (as data is already read when the nfc tag is powered up)
Yep I have the same understanding on
ignoreRead
with the above comment.
Ok. What is your suggestion about the "write" issue? (When writting an NDEF, it is immediately processed by the NFC service).
What you are saying is that what you just wrote (not previous content) gets dispatched right after it was written?
That doesn't seem that useful to me. Is this always the case?
So this didn't happen before? I wonder what changed this. Maybe we disconnect from the tag just after and that is why
What you are saying is that what you just wrote (not previous content) gets dispatched right after it was written?
Yes. I'm sure of that because I have done the test with an "empty" tag.
That doesn't seem that useful to me. Is this always the case?
Yes
So this didn't happen before? I wonder what changed this. Maybe we disconnect from the tag just after and that is why
I didn't have this issue with the previous API (the one with push())
ignoreRead was added because if you want to write to a tag, that tag might have existing content, and you don't want a read event dispatches for that during the write (as data is already read when the nfc tag is powered up)
What you are saying is that what you just wrote (not previous content) gets dispatched right after it was written?
If ignoreRead
controls the first case (suppress reading of existing content before writing),
what should control suppressing reading after writing?
ignoreRead
controls bothignoreRead
controls the first (as now) and by default we suppress reads right after writes (impl needs to catch and ignore the read intent)Opinions?
A read after should only happen if you lost contact with the tag and regained it. Maybe that happens because we call "close()" on the connection and so maybe we should not before the tag is out of reach..
@OlivierGrenoble thank you for giving the real usage feedback for the WebNFC API. We'd like to hear sound like this. I did some tests based on your info and understand your request. You want to write the tag only, without a following Activity Chooser showing. Actually, I saw this notice before on the old version API occasionally and from the code perspection, WebNFC implementation in Chromium can do nothing to prevent this because it is an OS action. See these: https://developer.android.com/guide/topics/connectivity/nfc/nfc#filtering-intents https://android.stackexchange.com/questions/96564/tapping-an-nfc-tag-which-contains-an-url-how-can-i-override-the-default-actio
Android system will try to start a proper Activity based on the content in a discoverred tag. If there is no proper Activity, it will do nothing, such as writing a text will not open a browser.
WebNFC API is based on Android NFC library, and just being noticed when tag discovered. That means WebNFC is just one of the listeners to the tag events, it can't interfere the default process, can't stop other Application to read the tag after writting. I investigated the lower level interfaces to see if there is some way to stop the event propgation, just like "event.stopPropagation()" in JS. But unfortunately, I haven't found it. Maybe we can suggest Android NFC library to support such usage.
@riju @reillyeon
@kenchris's suggestion sounds promising. It it possible that having an active read on the Java side right after the tag is written would suppress the OS default?
Right, now WebNFC impl closes the connection to the tag right after we completed all pending read/write operations on this tag.
Maybe we can just leave the connection there until the tag on its own gets out of range, thus WebNFC can hold this tag exclusively to prevent others from accessing it?
A read after should only happen if you lost contact with the tag and regained it. Maybe that happens because we call "close()" on the connection and so maybe we should not before the tag is out of reach..
I think that it is indeed interesting to investigate when to close the connection. There are 2 ways to use NFC tags:
When using the second solution, it will be annoying if the native processing is executed when non wanted. The application should be able to control that. I mean, it should be able to let the NFC service handle the NFC Event or it should be able to handle it itself.
In Android application this is controlled with enableforegrounddispatch(). When the application is in foreground, it can call this function to hook the NFC events. By this way, it will be notified when a tag is tapped and the native processing will not be executed. Calling this function is optional so the application has the ability to choose who should receive the NFC event.
I did some tests based on your info and understand your request. You want to write the tag only, without a following Activity Chooser showing.
Correct. I will try to share my web app today. It is not finished but it can allow every one to play with the write function and see this issue.
Here is my web app: https://www.myst25.com/ST25NFCWebApp/ You can use it to see the problem when writing an NDEF.
NB: This is still under development.
Hello, How can we progress for the Write issue?
Maybe we can just leave the connection there until the tag on its own gets out of range, thus WebNFC can hold this tag exclusively to prevent others from accessing it?
Is that something you could prototype so that we can try it out in Chromium?
Hi @OlivierGrenoble, I made a change to fix this. https://chromium-review.googlesource.com/c/chromium/src/+/2126470
I tested it on my side and it works. If you need, I can provide you an APK.
Thanks @DonnaWuDongxia for proposing this chromium CL. It seems like your implementation will leave "Android NFC Reader Mode" enabled after at least one successful write/scan Web NFC operation has been called. But shouldn't we at least disable it when tag goes out of range?
Thank you @DonnaWuDongxia for working on this.
It seems like your implementation will leave "Android NFC Reader Mode" enabled after at least one successful write/scan Web NFC operation has been called.
The question is indeed to know when to release the hook of NFC events. If you release it while the smartphone is still on the tag, the NFC service will process the NFC event (and we are back to the first problem).
But shouldn't we at least disable it when tag goes out of range?
I doubt that you can be notified when the tag gets out of range.
Some thoughts: We are trying here to find out when to release the NFC hook. What do you think of choosing the same strategy as the one chosen for Android API? I mean: Exposing enableforegrounddispatch() and disableforegrounddispatch() in webNFC API. By this way the application has the full control to decide when it wants to receive the NFC events or when it want to let the native execution run.
@OlivierGrenoble We use another way to read/write tags with NfcAdapter#enableReaderMode/disableReaderMode, not the Intent filter way. Please refer this and the Android Doc.
I think this is a more proper way for a runtime, and you can filter tags with WebNFC API.
@DonnaWuDongxia Ok thanks for this important précision. Back to the question from François, when shall disableReaderMode() be called? If the phone is still on the tag, the NFC service will process the NDEF message.
@beaufortfrancois @OlivierGrenoble Disable the ReadMode when the tag is out of range is a good thinking, I'm tring on it.
@beaufortfrancois We've got a listener(android.nfc.NfcAdapter.OnTagRemovedListener) to use and it works but with a high level API(Level 24), we are now at min-level 19. Do we need to sacrifice the compliance? I prefer not.
@OlivierGrenoble It seems the site https://www.myst25.com/ST25NFCWebApp/ has some issue can't trigger right NFC request, so the system launcher will still show. I use the following code piece.
const writer = new NDEFWriter();
writer.write({
records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }]
}).then(() => {
console.log("Message written.");
}).catch(_ => {
console.log("Write failed :-( try again.");
});
@beaufortfrancois We've got a listener(android.nfc.NfcAdapter.OnTagRemovedListener) to use and it works but with a high level API(Level 24), we are now at min-level 19. Do we need to sacrifice the compliance? I prefer not.
Difficult choice. 58% of the phones are currently using a version >= to API Level 24.
@OlivierGrenoble It seems the site https://www.myst25.com/ST25NFCWebApp/ has some issue can't trigger right NFC request, so the system launcher will still show. I use the following code piece.
const writer = new NDEFWriter(); writer.write({ records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }] }).then(() => { console.log("Message written."); }).catch(_ => { console.log("Write failed :-( try again."); });
What kind of issue do you see with https://www.myst25.com/ST25NFCWebApp/ ? Do you see this issue only with the code that you have updated?
Did you have a look at how it is implemented (ontagremoved): https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/nfc/NfcAdapter.java
Maybe we can polyfill it somehow
Unfortunately it looks like OnTagRemovedListener
is only used for the ignore()
method (also from API 24) which will cause the tag to be entirely ignored until it is removed. This is behavior I imagine we would want to have a site opt-in to because otherwise it would prevent multiple pushes to the card.
I am okay with there being functionality which is unavailable on older versions of a platform.
Yes,OnTagRemovedListener
is only used for theignore()
, I misunderstood it before. I don't think a site need to be in a state of ignoring some tag that will prevent multiple operations as @reillyeon mentioned. It seems that Android have no passive notifition for a tag removing. Actively, we can check a tag's state by catching IOException in polling way, that is not suitable for our case.
When writting an NDEF with writer.write(), I have noticed that the NFC service is immediately processing the NDEF freshly written. I have been told to run scan() and discard the onreading events.
Isn't that a pity that it is not the default behavior? When writing to a tag, one seldom needs the processing by NFC service of the NDEF freshly written. That makes the things harder for someone wanting to write an NDEF into an NFC tag.