Build the app and follow its runtime boundaries.
The Android client is a Java 21, Android Jetpack application with Gradle Kotlin DSL, ViewBinding, Credential Manager, Retrofit, Firebase, and SLF4J. This guide complements the Javadocs embedded in the source.
Build and test
Install JDK 21 and Android SDK 36, then point local.properties to the SDK. The development debug variant is the quickest local
target.
./gradlew :app:assembleDevDebug
./gradlew :app:installDevDebug
./gradlew :app:testDevDebugUnitTest
./gradlew :app:connectedDevDebugAndroidTest
./gradlew :app:lintDevDebug
The project defines dev, stg, and prod flavors and both debug and release build types. Use the flavor
that
matches the backend and identity configuration under test.
Project map
| Area | Package or file | Responsibility |
|---|---|---|
| Application UI | com.xibernetix.device |
Activities and fragments for role workspaces, devices, authentication, passkeys, settings, and security events. |
| NFC | com.xibernetix.nfc |
Reader sessions, ISO-DEP commands, chip introspection, cryptography, provisioning, authentication, and tag presentation. |
| Bluetooth | com.xibernetix.ble |
BLE discovery, GATT profile interpretation, and session lifecycle. |
| Tokens | com.xibernetix.token |
Authentication-token storage, parsing, and role checks. |
| Network DTOs | com.xibernetix.device.credentials |
Retrofit API contracts, sessions, account DTOs, passkeys, devices, and security events. |
| Resources | app/src/main/res |
Layouts, strings, navigation resources, themes, icons, and ViewBinding inputs. |
NFC control flow
NfcDeviceControlActivityNfcSessionManagerNfcTagProcessorNfcDeviceScannedActivity
| Java type | Control-flow responsibility |
|---|---|
NfcDeviceControlActivity |
Requires the operator role, enables reader mode in onResume, disables it in onPause, receives onTagDiscovered,
and posts the result-screen transition to the UI thread.
|
NfcSessionManager |
Guards authorization, routes NDEF versus ISO-DEP, resolves a model processor, supplies token and phone metadata, handles failures, and closes ISO-DEP. |
NfcTagType4ChipModel |
Maps the first ISO-DEP historical byte to a model enum. Duplicate first-byte mappings are replaced by the last enum declaration. |
NfcTagIntrospector |
Connects, selects the security application, reads GetVersion frames and SDM counter data, and probes factory-default key settings. |
NfcTagProcessor |
Implements the shared decision: try factory-key authentication and provision on success; otherwise authenticate with the diversified key and validate operator ownership. |
NfcTagOperatorIdHandler |
Writes, reads, and protects operator ID file 2, including migration from legacy clear communication to EV2 Full secure messaging. |
IsoDepTagTransceiver |
Builds and transmits the native or wrapped APDU commands used by introspection and secure messaging. |
NfcTagCrypto |
Performs AES operations, CMAC calculation, EV2 IV construction, session-key derivation, rotations, and hexadecimal conversion. |
XibernetixNfcBackendClient |
Adapts Retrofit callbacks to the ordered reader flow and waits up to the configured API timeout for key diversification. |
NfcTagAuthContext |
Carries verification, protocol, secure-session, operator, and display data from processing to the scanned-tag UI. |
Threading and lifetime
NfcAdapter.ReaderCallback.onTagDiscoveredis not the UI thread. Keep the ordered tag exchange there and marshal UI work throughrunOnUiThread.- The tag handle is useful only while the tag remains in the RF field.
NfcSessionManagerowns and closes the ISO-DEP channel. XibernetixNfcBackendClientblocks on a bounded latch because provisioning and authentication cannot be split into unordered callbacks. Never invoke it from the main thread.- Retrofit callbacks used by the legacy NDEF ship path are asynchronous and do not create a verified
NfcTagAuthContext.
Security invariants
- Treat
NfcTagAuthContext.identityVerified()as the final decision. Other populated fields are diagnostic, not proof. - For an existing tag, success requires both mutual cryptographic authentication and a case-insensitive operator-ID match with the JWT subject.
- Master keys are cached through
EncryptedSharedPreferences; session encryption and MAC keys are derived per EV2 session. NfcTagAuthContextcontains master and session key bytes so it can render session diagnostics. Do not log, serialize to analytics, or persist the complete record.- Provisioning writes operator file 2, changes it to Full secure messaging, then changes key slot 0. Command counters must account for those preceding secure operations.
- Do not turn authentication failures into a factory reset or NDEF format attempt. Escalate an uncertain provisioning state.
Backend contracts used by NFC
| Endpoint | Client use |
|---|---|
POST /nfc-tag/{id}/diversify |
Requests the tag-specific diversified key using tag version data, counter, mobile-device identity, and attributes. |
POST /nfc-tags/{id}/authenticate |
Supports the backend challenge-authentication client path. |
POST /nfc/ship |
Ships UID and generated serial information from the legacy NDEF path. |
The main provisioned-tag exchange currently requests the diversified master key and completes EV2 authentication on the phone. Keep DTO names, authorization headers, timeouts, and endpoint paths synchronized with the backend.
Testing strategy
- Local unit tests cover crypto helpers, ISO-DEP response parsing, display models, model selection, JSON contracts, and backend-client behavior.
- Instrumented tests exercise NTAG 424 and DESFire EV3 processors with Android APIs and compatible physical or controlled test infrastructure.
- Before release, test fresh provisioning, repeat authentication, wrong-operator rejection, missing local-key recovery, legacy operator-file migration, tag removal, AUTH_DELAY, and backend timeout.
- Run a real-device smoke test for every application flavor and signing configuration that changes OAuth or backend endpoints.