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.
SetTagText() / SetTagUri()
rec.GetAsTextType()
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).
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.
| Offset | Byte | Valore usato | Significato | Note |
|---|---|---|---|---|
| 0–1 | CCLEN | 00 0F | Lunghezza CC = 15 | Fisso per T4T v2.0 |
| 2 | Mapping Version | 20 | Versione 2.0 | Bit alto=2, basso=0 |
| 3–4 | MLe | 00 3B | Max R-APDU data = 59 byte | ⚠️ NON usare FF FF |
| 5–6 | MLc | 00 34 | Max C-APDU data = 52 byte | ⚠️ NON usare FF FF |
| 7 | TLV Tag | 04 | NDEF File Control TLV | Fisso |
| 8 | TLV Length | 06 | 6 byte di valore TLV | Fisso |
| 9–10 | NDEF File ID | E1 04 | Identificatore file NDEF | Fisso per T4T |
| 11–12 | Max NDEF Size | 00 FF | Max 255 byte | ⚠️ NON usare FF FE (causa crash Android 12+) |
| 13 | Read Access | 00 | Accesso lettura: aperto | Nessuna sicurezza |
| 14 | Write Access | 00 | Accesso scrittura: aperto | Solo lettura in produzione: FF |
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.
| Metodo B4A | Parametri | Descrizione |
|---|---|---|
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. |
| Evento B4A | Parametri | Quando 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. |
wait=false: il thread NFC non blocca in attesa del main thread.
Questo evita il timeout ISO-DEP che causerebbe "read error" sul lettore.
00 A4 04 00 07 D2760000850101 [00]90 00 → evento OnReaderConnected.00 A4 00 0C 02 E1 0390 00. Il byte P2 (0x0C vs 0x00) viene ignorato per compatibilità.
00 B0 00 00 0F (leggi 15 byte)00 A4 00 0C 02 E1 0490 00.
00 B0 00 00 02 (leggi 2 byte a offset 0)[NLEN_hi][NLEN_lo]: la lunghezza del messaggio NDEF.00 0B 90 00 (NLEN=11).
00 B0 00 02 [NLEN] (leggi NLEN byte a offset 2)D1 01 07 54 02 65 6E 74 65 73 74 90 00OnMessageSent.
_ndefData[])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>)
AddFeature non esiste in B4A → omettereAddServiceText è per servizi B4A → usare AddApplicationTexthttp://android.com errato → usare http://schemas.android.com/apk/res/androidandroid:description="testo" causa errore di linking → usare @string/nome' ────────────────────────────────────────────────────────────── ' 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
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
| Aspetto | Dettaglio |
|---|---|
| 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. |