Add com.codename1.nfc: NDEF read/write, ISO-DEP/MIFARE/FeliCa, HCE#4996
Open
shai-almog wants to merge 9 commits into
Open
Add com.codename1.nfc: NDEF read/write, ISO-DEP/MIFARE/FeliCa, HCE#4996shai-almog wants to merge 9 commits into
shai-almog wants to merge 9 commits into
Conversation
Promotes NFC to a first-class device API alongside Location, Capture and
the recently-added Biometrics. A single Nfc entry point exposes the
platform NFC controller and the typed errors / async returns mirror the
com.codename1.security design.
Public surface (com.codename1.nfc):
- Nfc, NfcException, NfcError (NOT_AVAILABLE / DISABLED / TAG_LOST /
READ_ONLY / CAPACITY_EXCEEDED / UNKNOWN_AID / ...).
- NdefMessage + NdefRecord with typed factories (createUri,
createText, createMime, createExternal, createApplicationRecord)
and a parser tolerant of the SR / IL flags.
- Tag, TagType, TagTechnology + per-technology subclasses: IsoDep,
MifareClassic, MifareUltralight, NfcA / NfcB / NfcF (FeliCa) /
NfcV.
- NfcReadOptions (alert copy, tech filter, FeliCa system codes,
auto-SELECT AIDs, timeout) and NfcListener for multi-tag sessions.
- HostCardEmulationService + ApduResponse for HCE.
Android port wraps NfcAdapter.enableReaderMode + android.nfc.tech.*;
HostApduService is bridged by CodenameOneHostApduService. iOS port
wraps NFCNDEFReaderSession + NFCTagReaderSession (NFCISO7816Tag,
NFCMiFareTag, NFCFeliCaTag) and weak-links CoreNFC.framework. JavaSE
simulator gets a Simulate -> NFC submenu (virtual tag with editable
NDEF, configurable read outcome, tag-tap, HCE APDU dialog).
The iOS native block is gated on a new CN1_INCLUDE_NFC define that
IPhoneBuilder uncomments only when the classpath scanner saw a
com.codename1.nfc reference. Apps that never use NFC ship without any
CoreNFC symbols and pass Apple's API-usage scan without a CoreNFC
privacy manifest.
Build pipeline (maven/codenameone-maven-plugin):
- Android: auto-inject android.permission.NFC + uses-feature when
the scanner sees com.codename1.nfc.*; add BIND_NFC_SERVICE +
nfc.hce feature + the CodenameOneHostApduService manifest entry +
a generated res/xml/apduservice.xml when HostCardEmulationService
is referenced or the android.hceAids hint is set.
- iOS: link CoreNFC.framework + default NFCReaderUsageDescription +
readersession.formats entitlement; for HCE, inject
com.apple.developer.nfc.hce + select-identifiers entitlements
derived from ios.hceAids / android.hceAids.
The matching backend changes ship in BuildDaemon PR #80.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AndroidGradleBuilder.build() already declares a `File xmlDir` near the top of the method (it points to res/xml inside the unzipped sources). The HCE block introduced in the prior commit declared a second `File xmlDir` pointing at src/main/res/xml, which javac rejects as a variable already defined in the method. Rename the HCE-side local to hceXmlDir so it no longer shadows the existing variable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 20 screenshots: 20 matched. |
Contributor
Cloudflare Preview
|
Collaborator
Author
|
Compared 11 screenshots: 11 matched. |
Contributor
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
The linker on native-ios CI rejected the NFC build:
Undefined symbols for architecture arm64:
"_com_codename1_impl_ios_IOSNative_isNfcSupported___R_boolean",
referenced from:
_com_codename1_impl_ios_IOSNfc_isSupported___R_boolean in
com_codename1_impl_ios_IOSNfc.o
ParparVM emits the canonical `___R_<returnType>` suffix on non-void
native method symbols generated from recently-introduced Java sources.
The IOSBiometrics block predates the convention switch and is kept on
the legacy `__` form for compatibility, but newly-added natives have to
use the suffix or the link step fails.
Rename the four boolean-returning NFC natives:
- isNfcSupported__ -> isNfcSupported___R_boolean
- canReadNfc__ -> canReadNfc___R_boolean
- canReadNfcTags__ -> canReadNfcTags___R_boolean
- canHostEmulateNfc__ -> canHostEmulateNfc___R_boolean
Void NFC natives (startNdefRead etc.) already use the parameter-only
form which is unchanged between the two conventions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
build-test (8) flagged three DM_DEFAULT_ENCODING violations in NdefRecord.toUtf8 / .toAscii / .fromUtf8 -- each catch block was falling back to s.getBytes() / new String(bytes) without specifying an encoding, which SpotBugs treats as platform-dependent and forbids. UTF-8 and US-ASCII are both required by the JLS to be present on every JVM (rt.jar's StandardCharsets enumerates them as required), so the catch is dead code. Match the pattern StringUtil.getBytes already uses in this codebase and throw a RuntimeException instead of falling through to the default encoding. Also fix two Microsoft.Contractions warnings on Near-Field-Communication.asciidoc -- the developer-guide quality gate treats Vale errors as build-breaking. Rewrite "does not" -> "doesn't" on lines 89 and 95 to match the rest of the guide's style. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
build-test (8) flagged ten SpotBugs violations across the NFC code:
- Nfc.readNdef / writeNdef declare anonymous SuccessCallback chains
inside instance methods, so javac synthesises an outer-Nfc
reference into each ($1, $2, $3, $4) even though none of them
touch `this`. Extract the chaining work to chainReadNdef /
chainWriteNdef static helpers so the inner classes live in a
static context.
- AndroidNfc.deliverTag's callSerially Runnable captured a synthetic
outer-AndroidNfc reference for the same reason. Hoist the Runnable
into a scheduleTagDelivery static helper that takes the snapshot
explicitly.
- IOSNfc.IOSTag's read/write/lock methods used an anonymous
TagOpInvoker functional interface implementation per call. Replace
the indirection with a static bindTagOp helper that returns the
IOSNative bridge (or rejects the AsyncResource) plus direct
invocations from each IOSTag method; the inner interface is gone.
- IOSNfc.registerHostCardEmulationService /
unregisterHostCardEmulationService were `synchronized` (instance
lock) while mutating the static `hceService` field, which would
not serialise updates if multiple IOSNfc instances ever existed.
Switch to synchronized(IOSNfc.class) so all callers serialise
against the same monitor (SpotBugs
SSD_DO_NOT_USE_INSTANCE_LOCK_ON_SHARED_STATIC_DATA).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PMD flagged five forbidden violations in the NFC core sources:
- MissingOverride x 4: the anonymous SuccessCallback / Throwable
callbacks inside Nfc.chainReadNdef and Nfc.chainWriteNdef were
missing the @OverRide annotation on each onSucess(...) method.
Add @OverRide on every anonymous implementation (PMD only flagged
the outer four but the inner ones are equivalent so they get the
annotation too).
- ForLoopCanBeForeach: NfcReadOptions.setIsoSelectAids walked the
incoming byte[][] with an explicit index; the index was never used
on its own. Convert to an enhanced-for loop over the array.
The build-test (8) PMD scan is restricted to core sources (per the
QUALITY_REPORT_TARGET_DIRS list), so AndroidNfc / IOSNfc / JavaSENfc
do not need analogous annotation churn -- the existing Android port
uses both styles freely.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
The Ports/Android module compiles with javac.source=1.6 (see Ports/Android/nbproject/project.properties), so locals captured by anonymous inner classes have to be declared final --- Java 8's effectively-final semantics don't apply at -source 1.6. build-test (8) blew up on the Ant-driven Android compile step with four errors against AndroidNfc.armReader: the activity, a (NfcAdapter), flags and extras locals were referenced inside the runOnUiThread Runnable but were not declared final. Add the final modifier on each. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SpotBugs MS_PKGPROTECT (Normal / Low) flagged 14 fields across
ApduResponse, MifareClassic and NdefRecord: each was a public static
final byte[]. The `final` only protects the reference, not the array
contents, so one caller could mutate the shared instance and corrupt
every subsequent read of the well-known constant.
Convert all 14 to public static accessor methods that return a fresh
defensive copy:
- NdefRecord.RTD_TEXT / RTD_URI / RTD_SMART_POSTER / RTD_ANDROID_APP
-> rtdText() / rtdUri() / rtdSmartPoster() / rtdAndroidApp().
The underlying byte arrays stay as private static finals so the
factory methods inside NdefRecord (createText, createUri,
createApplicationRecord, getTextPayload, getUriPayload) keep
referencing the shared instance directly --- they don't mutate.
- ApduResponse.SW_SUCCESS / SW_FILE_NOT_FOUND / SW_INS_NOT_SUPPORTED
/ SW_CLA_NOT_SUPPORTED / SW_WRONG_LENGTH /
SW_SECURITY_NOT_SATISFIED / SW_UNKNOWN_ERROR -> swSuccess() etc.
HCE response paths allocate a fresh 2-byte array per call which
is negligible against the APDU round-trip cost.
- MifareClassic.KEY_DEFAULT / KEY_MIFARE_APPLICATION_DIRECTORY /
KEY_NFC_FORUM -> keyDefault() / keyMifareApplicationDirectory() /
keyNfcForum(). Each returns a fresh 6-byte copy so an app passing
the key to authenticateSectorWithKeyA can't be tripped up if the
platform Mifare implementation mutates the input.
Update internal callers (CodenameOneHostApduService, IOSNfc.nativeHceApdu,
JavaSENfc.simulateApdu / SimIsoDep, Nfc class-level Javadoc, and the
HCE example in Near-Field-Communication.asciidoc).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # Ports/iOSPort/nativeSources/IOSNative.m # scripts/cn1playground/tools/src/main/java/com/codenameone/playground/tools/GenerateCN1AccessRegistry.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Promotes NFC to a first-class device API alongside Location, Capture and the recently-added Biometrics. A single
Nfcentry point exposes the platform NFC controller; typed errors / async returns mirror thecom.codename1.securitydesign.The matching cloud-build daemon changes ship in BuildDaemon PR codenameone/BuildDaemon#80.
Public surface (
com.codename1.nfc)Nfc,NfcException,NfcError(NOT_AVAILABLE / DISABLED / TAG_LOST / READ_ONLY / CAPACITY_EXCEEDED / UNKNOWN_AID / ...).NdefMessage+NdefRecordwith typed factories (createUri,createText,createMime,createExternal,createApplicationRecord) and a parser tolerant of the SR / IL flags.Tag,TagType,TagTechnology+ per-technology subclasses:IsoDep,MifareClassic,MifareUltralight,NfcA/NfcB/NfcF(FeliCa) /NfcV.NfcReadOptions(alert copy, tech filter, FeliCa system codes, auto-SELECT AIDs, timeout) andNfcListenerfor multi-tag sessions.HostCardEmulationService+ApduResponsefor HCE.Ports
NfcAdapter.enableReaderMode+android.nfc.tech.*;HostApduServiceis bridged byCodenameOneHostApduServiceso subclassingcom.codename1.nfc.HostCardEmulationServiceis enough -- the manifest entry +apduservice.xmlare generated at build time.NFCNDEFReaderSession+NFCTagReaderSession(NFCISO7816Tag,NFCMiFareTag,NFCFeliCaTag) and weak-linksCoreNFC.framework. The native block is gated on a newCN1_INCLUDE_NFCdefine thatIPhoneBuilderuncomments only when the classpath scanner saw acom.codename1.nfcreference. Apps that never use NFC ship without any CoreNFC symbols and pass Apple's API-usage scan without a CoreNFC privacy manifest.Simulate -> NFCsubmenu (virtual tag with editable NDEF, configurable read outcome, tag-tap, HCE APDU dialog).falsefromcanRead()and completes every async call withNfcError.NOT_AVAILABLE.Build pipeline (maven/codenameone-maven-plugin)
android.permission.NFC+<uses-feature android:name="android.hardware.nfc">when the scanner seescom.codename1.nfc.*; addBIND_NFC_SERVICE+nfc.hcefeature + theCodenameOneHostApduServicemanifest entry + a generatedres/xml/apduservice.xmlwhenHostCardEmulationServiceis referenced or theandroid.hceAidshint is set.CoreNFC.framework+ defaultNFCReaderUsageDescription+readersession.formatsentitlement; for HCE, injectcom.apple.developer.nfc.hce+select-identifiersentitlements derived fromios.hceAids/android.hceAids. TheCN1_INCLUDE_NFCdefine inCodenameOne_GLViewController.his flipped on only when NFC is referenced.Test plan
com.codename1.nfc.*on both Android and iOS; verify no NFC manifest / plist / entitlement entries are injected and that the iOS binary contains no CoreNFC symbols (App Store API-usage scan).Nfc.getInstance().readNdef(...); verify Android gets theNFCpermission + feature and iOS getsCoreNFC.framework+ the reader-session entitlement +NFCReaderUsageDescription.HostCardEmulationServicewithandroid.hceAidsset; verify Android gets theCodenameOneHostApduServicemanifest entry + a generatedapduservice.xml, and iOS gets the HCE entitlements + select-identifiers.Nfc.getInstance().writeNdef(...); verify the tag is updated.tag.getIsoDep().transceive(selectAid); verify the SW1/SW2 status word comes back.Simulate -> NFCmenu and exercise the virtual tag (URI / text edits, tap, read-only toggle, HCE APDU).LI_LAZY_INIT_STATIC).🤖 Generated with Claude Code