NFC
Note: NFC functionality is not part of the AD Device Manager SDK. It is a standard Android platform feature. This guide shows how NFC is used in the Quick Start application as a reference implementation — for reading a printer's Bluetooth MAC address from an NFC tag and writing a MAC address to one.
Overview
The Quick Start app uses NFC in two ways:
- Read — Tap an NFC tag on a printer to extract its Bluetooth MAC address and connect automatically.
- Write — Write a printer's Bluetooth MAC address onto an NFC tag for later use.
The app supports two NFC tag technologies:
- NfcA (ISO 14443-A, e.g. MIFARE Ultralight / NTAG)
- NfcV (ISO 15693)
The MAC address is stored as 12 ASCII hex characters across memory blocks 4–6 (4 bytes per block).
AndroidManifest Setup
Declare NFC permission and the ACTION_TAG_DISCOVERED intent filter in your AndroidManifest.xml:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<activity android:name=".MainActivity" ...>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Setting android:required="false" means the app can still be installed on devices without NFC hardware.
Initialising the NFC Adapter
Get and check the NfcAdapter in onCreate():
private NfcAdapter nfcAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
// Device has no NFC hardware
logger.error("NFC is not available on this device.");
} else if (!nfcAdapter.isEnabled()) {
// NFC hardware present but disabled by the user
Toast.makeText(this, "NFC is disabled. Please enable NFC in settings.", Toast.LENGTH_LONG).show();
}
}
Enabling Foreground Dispatch
Foreground dispatch ensures your app intercepts NFC tag events while it is in the foreground, even if it was not launched by an NFC intent.
Enable it in onResume() and disable it in onPause():
@Override
protected void onResume() {
super.onResume();
if (nfcAdapter != null && nfcAdapter.isEnabled()) {
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_MUTABLE);
// ACTION_TAG_DISCOVERED catches any tag type
IntentFilter[] intentFilters = new IntentFilter[]{
new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
};
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null);
}
}
@Override
protected void onPause() {
super.onPause();
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(this);
}
}
Handling Incoming NFC Intents
When a tag is tapped, the system delivers the intent via onNewIntent():
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleNfcIntent(intent);
}
private void handleNfcIntent(Intent intent) {
if (intent == null) return;
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag == null) return;
String action = intent.getAction();
if (isInWriteMode()) {
// Write mode: write MAC address to the tag
writeNfcTag(tag);
} else {
// Read mode: read MAC address from the tag and connect
readMacFromTag(tag);
}
}
Reading a MAC Address from a Tag
The MAC address is stored as 12 ASCII hex characters across memory blocks 4–6 (4 bytes per block). The app tries NfcA first, then falls back to NfcV:
private void readMacFromTag(Tag tag) {
try {
// Try NfcA (ISO 14443-A)
NfcA nfca = NfcA.get(tag);
if (nfca != null) {
nfca.connect();
StringBuilder macAscii = new StringBuilder();
for (int page = 4; page <= 6; page++) {
byte[] cmd = new byte[]{(byte) 0x30, (byte) page}; // READ command
byte[] response = nfca.transceive(cmd);
macAscii.append(new String(response, 0, 4, StandardCharsets.US_ASCII));
}
nfca.close();
String mac = macAscii.toString().trim()
.replaceAll("(.{2})(?!$)", "$1:"); // format as AA:BB:CC:DD:EE:FF
connectWithMac(mac);
return;
}
// Fallback: NfcV (ISO 15693)
NfcV nfcv = NfcV.get(tag);
if (nfcv != null) {
nfcv.connect();
StringBuilder macAscii = new StringBuilder();
for (int block = 4; block <= 6; block++) {
// ISO 15693 Read Single Block: Flags (0x02), Command (0x20), block number
byte[] cmd = new byte[]{(byte) 0x02, (byte) 0x20, (byte) block};
byte[] response = nfcv.transceive(cmd);
if (response != null && response.length > 1) {
// First byte is the status flag, skip it
macAscii.append(new String(response, 1, response.length - 1, StandardCharsets.US_ASCII));
}
}
nfcv.close();
String mac = macAscii.toString().trim()
.replaceAll("(.{2})(?!$)", "$1:");
connectWithMac(mac);
}
} catch (Exception e) {
logger.error("Error reading NFC tag", e);
}
}
Writing a MAC Address to a Tag
The MAC address (12 ASCII hex chars, no separators) is split into three 4-byte blocks and written starting at block/page 4:
private void writeNfcTag(Tag tag) {
// Normalize MAC: strip separators, uppercase -> "AABBCCDDEEFF"
String macHex = normalizeMac(macAddressToWrite); // 12 chars
String[] blocks = {
macHex.substring(0, 4),
macHex.substring(4, 8),
macHex.substring(8, 12)
};
try {
// Try NfcA (MIFARE Ultralight / NTAG)
NfcA nfca = NfcA.get(tag);
if (nfca != null) {
nfca.connect();
for (int i = 0; i < blocks.length; i++) {
int page = 4 + i;
byte[] payload = blocks[i].getBytes(StandardCharsets.US_ASCII);
// WRITE command: 0xA2, <page>, <4 bytes>
byte[] cmd = new byte[]{(byte) 0xA2, (byte) page,
payload[0], payload[1], payload[2], payload[3]};
nfca.transceive(cmd);
}
nfca.close();
logger.info("MAC written to NfcA tag successfully.");
return;
}
// Fallback: NfcV (ISO 15693)
NfcV nfcv = NfcV.get(tag);
if (nfcv != null) {
nfcv.connect();
for (int i = 0; i < blocks.length; i++) {
int block = 4 + i;
byte[] payload = blocks[i].getBytes(StandardCharsets.US_ASCII);
// ISO 15693 Write Single Block: Flags (0x02), Command (0x21), block, data
byte[] cmd = new byte[]{(byte) 0x02, (byte) 0x21, (byte) block,
payload[0], payload[1], payload[2], payload[3]};
byte[] response = nfcv.transceive(cmd);
if (response == null || response.length == 0 || response[0] != 0x00) {
logger.error("NfcV write failed for block {}", block);
break;
}
}
nfcv.close();
logger.info("MAC written to NfcV tag successfully.");
}
} catch (Exception e) {
logger.error("Error writing NFC tag", e);
}
}
Connecting After Reading
Once the MAC address is extracted from the tag, pass it to the SDK to establish a Bluetooth connection:
private void connectWithMac(String macAddress) {
BluetoothConnection connection = new BluetoothConnection(
DeviceConnection.PrinterType.EDGE, macAddress, null);
DeviceAdapter deviceAdapter = deviceManager.getAdapterForDevice(connection);
deviceAdapter.connect()
.thenRun(() -> logger.info("Connected via NFC to: " + macAddress))
.exceptionally(e -> {
logger.error("Connection failed: " + e.getMessage());
return null;
});
}
Next Steps
- Getting Started - Bluetooth discovery and connection
- Printing - Print labels once connected
- RFID Printing - Print and encode RFID labels