HceManager

Documentazione Tecnica — Wrapper B4A per NFC Host Card Emulation
NFC Forum T4T v2.0 ISO/IEC 14443-4 ISO/IEC 7816-4 Android API 19+ B4A Wrapper

📋 Indice

  1. Architettura del sistema
  2. Ruoli: Trasmettitore vs Ricevitore
  3. Protocollo NFC Forum Type 4 Tag (T4T)
  4. Capability Container (CC File)
  5. API pubblica — HceManager
  6. Flusso APDU dettagliato
  7. Configurazione Manifest B4A
  8. Codice B4A — Sender (HCE)
  9. Codice B4A — Reader (NFC)
  10. Limitazioni e note

1. Architettura del sistema

HceManager implementa il profilo NFC Forum Type 4 Tag (T4T) su Android tramite HostApduService. Il dispositivo emula un tag NFC fisico senza hardware dedicato (Secure Element), rispondendo ai comandi APDU del lettore.

┌─────────────────────────────────────────────────────────────────────────┐ │ STACK NFC FORUM TYPE 4 TAG │ └─────────────────────────────────────────────────────────────────────────┘ Applicazione B4A ┌──────────────────────────────────┐ │ nfc.SetTagText("testo") │ ← API utente │ nfc.SetTagUri("https://...") │ │ nfc.SetAsPreferredService │ └──────────────┬───────────────────┘ │ buildNdefFile() ▼ ┌──────────────────────────────────┐ │ HceManager │ ← Wrapper B4A (questo file) │ ┌────────────────────────────┐ │ │ │ _ndefData[] │ │ NDEF file in memoria: │ │ [NLEN_hi][NLEN_lo] │ │ [00][0B][D1 01 07 54 02 65 6E ...] │ │ [NdefMessage bytes...] │ │ │ └────────────────────────────┘ │ │ ┌────────────────────────────┐ │ │ │ HceService │ │ ← extends HostApduService │ │ processCommandApdu() │ │ │ └───────────┬────────────────┘ │ └──────────────┼───────────────────┘ │ APDU exchange ▼ ┌──────────────────────────────────┐ │ Android NFC Stack │ ← OS gestisce ISO-DEP / T4T │ HostEmulationManager │ │ NFC Controller (hardware) │ └──────────────┬───────────────────┘ │ RF 13.56 MHz ▼ ┌──────────────────────────────────┐ │ Lettore NFC │ ← Altro telefono / reader fisico │ (NFC Tools, app B4A reader...) │ └──────────────────────────────────┘

2. Ruoli: Trasmettitore vs Ricevitore

📤 Telefono A — TRASMETTITORE
Libreria: HceManager.jar
Comportamento: Passivo — simula un tag NFC fisico.
Non invia attivamente: risponde solo quando interrogato.

Metodo chiave: SetTagText() / SetTagUri()
📥 Telefono B — RICEVITORE
Libreria: NFC (inclusa in B4A)
Comportamento: Attivo — avvicina il telefono al trasmettitore.
Legge il tag e ottiene il messaggio NDEF.

Metodo chiave: rec.GetAsTextType()
Telefono A (HceManager) Telefono B (NFC Reader) ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ │ │ │ │ nfc.SetTagText("Ciao!") │ │ NFC.EnableForeground │ │ nfc.SetAsPreferredService │ │ Dispatch(Me) │ │ │ │ │ │ [ATTENDE] │◄─)))──)))─│ [AVVICINA IL TELEFONO] │ │ │ 13.56MHz │ │ │ HceService risponde │──────────►│ Activity_NewIntent │ │ agli APDU del lettore │ APDU │ HandleIntent │ │ │ exchange │ rec.GetAsTextType → "Ciao!"│ └─────────────────────────────┘ └─────────────────────────────┘ PASSIVO ATTIVO

3. Protocollo NFC Forum Type 4 Tag (T4T)

Il protocollo T4T definisce una sequenza di comandi APDU che il lettore invia per leggere un messaggio NDEF da un tag (fisico o emulato via HCE).

LETTORE (Telefono B) HCE SERVICE (Telefono A) │ │ │── SELECT AID ──────────────────────────────►│ │ 00 A4 04 00 07 D2760000850101 [00] │ ← AID NFC Forum NDEF │◄─ 90 00 ────────────────────────────────────│ ← OK (+ evento OnReaderConnected) │ │ │── SELECT CC file (E103) ───────────────────►│ │ 00 A4 00 0C 02 E1 03 │ │◄─ 90 00 ────────────────────────────────────│ ← OK │ │ │── READ BINARY CC ──────────────────────────►│ │ 00 B0 00 00 0F │ ← leggi 15 byte │◄─ [CC file 15 byte] + 90 00 ────────────────│ ← CC con MLe, MLc, NDEF file ID │ │ │── SELECT NDEF file (E104) ─────────────────►│ │ 00 A4 00 0C 02 E1 04 │ │◄─ 90 00 ────────────────────────────────────│ ← OK │ │ │── READ BINARY NLEN ────────────────────────►│ │ 00 B0 00 00 02 │ ← leggi 2 byte (lunghezza) │◄─ [00 0B] + 90 00 ──────────────────────────│ ← NLEN = 11 byte │ │ │── READ BINARY NDEF data ───────────────────►│ │ 00 B0 00 02 0B │ ← leggi 11 byte da offset 2 │◄─ [D1 01 07 54 02 65 6E 74 65 73 74] 90 00 ─│ ← NDEF message (+ OnMessageSent) │ │ ▼ ▼ Dispatch NDEF_DISCOVERED intent sessione terminata → NFC library → GetAsTextType → "test"

4. Capability Container (CC File)

Il CC file è un blocco di 15 byte che il lettore legge per conoscere le caratteristiche del tag NDEF. I valori sono critici: valori errati causano link loss su Android 12/13.

OffsetByteValore usatoSignificatoNote
0–1CCLEN00 0FLunghezza CC = 15Fisso per T4T v2.0
2Mapping Version20Versione 2.0Bit alto=2, basso=0
3–4MLe00 3BMax R-APDU data = 59 byte⚠️ NON usare FF FF
5–6MLc00 34Max C-APDU data = 52 byte⚠️ NON usare FF FF
7TLV Tag04NDEF File Control TLVFisso
8TLV Length066 byte di valore TLVFisso
9–10NDEF File IDE1 04Identificatore file NDEFFisso per T4T
11–12Max NDEF Size00 FFMax 255 byte⚠️ NON usare FF FE (causa crash Android 12+)
13Read Access00Accesso lettura: apertoNessuna sicurezza
14Write Access00Accesso scrittura: apertoSolo lettura in produzione: FF
⚠️ Problema noto Android 12/13 (fix applicato)
Con MLe=FF FF, MLc=FF FF o Max NDEF Size=FF FE, Android 12/13 chiude la connessione dopo la lettura del CC file (link loss) senza leggere il file NDEF. Usare i valori della tabella sopra.
Fonte: stackoverflow.com/a/75933443

5. API pubblica — HceManager

Metodi pubblici

Metodo B4AParametriDescrizione
Initialize(ba, EventName) BA, String Inizializza il wrapper. Chiamare con Me, "nfc". Va in Activity_Create.
SetTagText(text) String Imposta il testo da emulare come NDEF Text Record (TNF=Well-Known, Type='T'). Usa NdefRecord.createTextRecord("en", text) (API 21+). Max ~245 byte UTF-8.
SetTagUri(uri) String Imposta un URI come NDEF URI Record (TNF=Well-Known, Type='U'). Esempio: "https://example.com". Apre il browser sul lettore.
SetAsPreferredService Imposta questo servizio come preferito. Evita il selettore di app. Chiamare in Activity_Resume. Richiede API 21+.
UnsetPreferredService Rimuove la preferenza. Chiamare in Activity_Pause.

Eventi

Evento B4AParametriQuando scatta
nfc_OnReaderConnected Il lettore ha inviato il comando SELECT AID (step 1 del protocollo T4T).
nfc_OnMessageSent Il lettore ha letto l'ultimo byte del file NDEF. Una sola volta per sessione NFC.
ℹ️ Thread safety
Gli eventi vengono lanciati con wait=false: il thread NFC non blocca in attesa del main thread. Questo evita il timeout ISO-DEP che causerebbe "read error" sul lettore.

6. Flusso APDU dettagliato

1
SELECT Application00 A4 04 00 07 D2760000850101 [00]
Il lettore richiede l'applicazione NDEF (AID = D2760000850101).
Risposta: 90 00 → evento OnReaderConnected.
Il match usa startsWith per tollerare il byte Le opzionale in coda.
2
SELECT CC file00 A4 00 0C 02 E1 03
Il lettore seleziona il Capability Container (file E103).
Risposta: 90 00. Il byte P2 (0x0C vs 0x00) viene ignorato per compatibilità.
3
READ BINARY CC00 B0 00 00 0F (leggi 15 byte)
Restituisce i 15 byte del CC file. Il lettore analizza MLe, MLc e l'ID del file NDEF.
4
SELECT NDEF file00 A4 00 0C 02 E1 04
Il lettore seleziona il file NDEF (E104, come indicato nel CC).
Risposta: 90 00.
5
READ BINARY NLEN00 B0 00 00 02 (leggi 2 byte a offset 0)
Restituisce [NLEN_hi][NLEN_lo]: la lunghezza del messaggio NDEF.
Esempio per "test": 00 0B 90 00 (NLEN=11).
6
READ BINARY NDEF data00 B0 00 02 [NLEN] (leggi NLEN byte a offset 2)
Restituisce il messaggio NDEF serializzato.
Esempio: D1 01 07 54 02 65 6E 74 65 73 74 90 00
→ evento OnMessageSent.

Struttura del file NDEF in memoria (_ndefData[])

_ndefData[] per SetTagText("test"): Offset Valore Significato ────── ─────── ────────────────────────────────────────── 0 00 NLEN high byte (lunghezza NdefMessage) 1 0B NLEN low byte (11 = lunghezza NdefMessage) 2 D1 MB=1 ME=1 SR=1 TNF=001 (Well-Known, Short Record) 3 01 Type Length = 1 4 07 Payload Length = 7 5 54 Type = 'T' (Text Record) 6 02 Status byte: UTF-8, lang code length = 2 7 65 Lang[0] = 'e' 8 6E Lang[1] = 'n' 9 74 Text: 't' 10 65 Text: 'e' 11 73 Text: 's' 12 74 Text: 't' ▲ ▲ ▲ │ │ └── Payload NdefRecord │ └── NdefMessage header (4 byte) └── T4T NDEF file header (2 byte NLEN)

7. Configurazione Manifest B4A

Incollare nel Manifest Editor (Tools → Manifest Editor) dell'app Sender:

' ── Permesso NFC ──────────────────────────────────────────────
AddPermission(android.permission.NFC)

' ── Registrazione servizio HCE ─────────────────────────────────
' NOTA: AddApplicationText aggiunge dentro <application>
'       AddServiceText è per servizi B4A-compilati (NON usare qui)
AddApplicationText(
<service
    android:name="com.hce.wrapper.HceManager$HceService"
    android:exported="true"
    android:permission="android.permission.BIND_NFC_SERVICE">
    <intent-filter>
        <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
    </intent-filter>
    <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
        android:resource="@xml/apduservice"/>
</service>)

' ── Descrittore AID (XML risorsa) ──────────────────────────────
' NOTA: namespace OBBLIGATORIO = schemas.android.com (NON android.com)
'       android:description richiede @string (NON testo letterale)
CreateResource(xml, apduservice.xml,
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/hce_description"
    android:requireDeviceUnlock="false">
    <aid-group android:description="@string/hce_aid_description" android:category="other">
        <aid-filter android:name="D2760000850101"/>
    </aid-group>
</host-apdu-service>)

' ── Stringhe (richieste per android:description) ───────────────
CreateResource(values, strings_hce.xml,
<resources>
    <string name="hce_description">HCE NFC Emulator</string>
    <string name="hce_aid_description">NFC Forum Type 4</string>
</resources>)
⚠️ Errori comuni nel Manifest

8. Codice B4A — Sender (HCE)

' ──────────────────────────────────────────────────────────────
' PROGETTO: HCE Sender
' Libreria richiesta: HceManager (HceManager.jar + HceManager.xml)
' ──────────────────────────────────────────────────────────────

Sub Process_Globals
    ' HceManager usa campi statici: dichiarare a livello di processo
    Private nfc As HceManager
End Sub

Sub Globals
    Dim lblStato   As Label
    Dim edtTesto   As EditText
    Dim btnTesto   As Button
    Dim btnUri     As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
        nfc.Initialize(Me, "nfc")
        nfc.SetTagText("Ciao da Android HCE!")   ' default
    End If
    ' ... (aggiunta viste UI) ...
End Sub

Sub Activity_Resume
    ' OBBLIGATORIO: evita il selettore di app ogni scansione
    nfc.SetAsPreferredService
End Sub

Sub Activity_Pause(UserClosed As Boolean)
    ' OBBLIGATORIO: rimuovere la preferenza in background
    nfc.UnsetPreferredService
End Sub

Sub btnTesto_Click
    nfc.SetTagText(edtTesto.Text)            ' Imposta Text Record
End Sub

Sub btnUri_Click
    nfc.SetTagUri(edtTesto.Text)             ' Imposta URI Record
End Sub

' ── Evento: lettore ha selezionato l'applicazione (step 1) ──
Sub nfc_OnReaderConnected
    lblStato.Text = "Lettore connesso!"
    Log("HCE: lettore connesso")
End Sub

' ── Evento: lettore ha letto tutto il file NDEF (step 6) ────
Sub nfc_OnMessageSent
    lblStato.Text = "Messaggio inviato!"
    Log("HCE: messaggio inviato")
End Sub

9. Codice B4A — Reader (NFC)

Manifest Reader: AddPermission(android.permission.NFC) + SetActivityAttribute(android:launchMode, singleTop)
Librerie: NFC + JavaObject (dipendenza obbligatoria della libreria NFC).

' ──────────────────────────────────────────────────────────────
' PROGETTO: NFC Reader
' Legge tag NFC fisici e tag emulati via HCE
' ──────────────────────────────────────────────────────────────

Sub Globals
    ' NFC è owner=activity → dichiarare in Globals
    Dim NFC      As NFC
    Dim lblStato As Label
    Dim lblTesto As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If NFC.IsSupported = False Then
        lblStato.Text = "NFC non disponibile" : Return
    End If
    If NFC.IsEnabled = False Then
        lblStato.Text = "NFC disabilitato" : Return
    End If
    ' Gestisce il caso in cui l'app è stata APERTA da un tag
    HandleIntent(Activity.GetStartingIntent)
End Sub

Sub Activity_Resume
    If NFC.IsSupported And NFC.IsEnabled Then
        NFC.EnableForegroundDispatch(Me)
    End If
End Sub

Sub Activity_Pause(UserClosed As Boolean)
    If NFC.IsSupported Then
        NFC.DisableForegroundDispatch(Me)
    End If
End Sub

' Chiamato quando un tag viene avvicinato con l'app già aperta
Sub Activity_NewIntent(Intent As Intent)
    HandleIntent(Intent)
End Sub

Sub HandleIntent(Intent As Intent)
    If Intent.IsInitialized = False Then Return
    If NFC.IsNdefIntent(Intent) = False Then
        lblStato.Text = "Tag rilevato (non NDEF)" : Return
    End If

    Dim records As List = NFC.GetNdefRecords(Intent)
    Dim sb As StringBuilder
    sb.Initialize

    For i = 0 To records.Size - 1
        Dim rec As NdefRecord       ' shortname della libreria NFC
        rec = records.Get(i)

        Dim testo As String = rec.GetAsTextType   ' "" se non è Text
        Dim uri   As String = rec.GetAsUriType    ' "" se non è URI

        If testo.Length > 0 Then
            sb.Append(testo)
        Else If uri.Length > 0 Then
            sb.Append(uri)
        Else
            Dim payload() As Byte = rec.GetPayload
            sb.Append("[HEX] ").Append(BytesToHex(payload))
        End If
    Next

    lblStato.Text = "Ricevuto! (" & records.Size & " record)"
    lblTesto.Text = sb.ToString
    Log("NFC: " & sb.ToString)
End Sub

Private Sub BytesToHex(data() As Byte) As String
    Dim sb As StringBuilder
    sb.Initialize
    For i = 0 To data.Length - 1
        Dim b As Int = Bit.AND(data(i), 0xFF)
        If b < 16 Then sb.Append("0")
        sb.Append(Bit.ToHexString(b)).Append(" ")
    Next
    Return sb.ToString.Trim
End Sub

10. Limitazioni e note importanti

AspettoDettaglio
Max testo ~245 byte UTF-8. Caratteri ASCII: 245 char. Caratteri accentati/UTF-8 multi-byte: meno. Il CC dichiara Max NDEF Size = 255; il logcat avvisa se superato.
API minima HCE richiede API 19. createTextRecord e SetAsPreferredService richiedono API 21. In pratica tutti i dispositivi HCE hanno API 21+.
Android-to-Android Due telefoni Android si connettono in modalità P2P (Beam) prima del T4T. Disabilitare Beam nelle impostazioni NFC, oppure il lettore usa enableReaderMode.
Schermo spento HCE funziona con schermo spento se android:requireDeviceUnlock="false". SetAsPreferredService richiede Activity attiva.
Conflitti AID L'AID D2760000850101 è usato da tutte le app NDEF T4T. Se più app lo registrano, il sistema mostra il selettore. SetAsPreferredService risolve in foreground.
Debug adb logcat -s HceManager — mostra ogni APDU ricevuto e la risposta.
🚫 Cosa NON funziona con HCE