A comprehensive Android library for Host Card Emulation (HCE) and NFC terminal communication with schema-based protocol abstraction.
- Host Card Emulation (HCE): Emulate smart cards on Android devices
- Schema-based Protocol Definition: Declarative APDU protocol definitions
- Factory Pattern: Generate both server (card) and client (terminal) implementations from schemas
- Annotation-based Routing: Simple controller mapping with
@ApduController
and@ApduMapping
- Automatic Setup: Annotation processor handles all Android manifest configuration
- Flexible Business Logic: Full developer control over command processing and response handling
- Type Safety: Compile-time validation and runtime schema compliance
├── hce-library/ # Core NFC library module
├── hce-processor/ # Annotation processor for automatic setup
└── app/ # Sample application demonstrating usage
- Include in your project: Copy the
hce-library
andhce-processor
modules to your project - Add to settings.gradle:
include ':hce-library' include ':hce-processor'
- Add dependencies in your app's
build.gradle
:dependencies { implementation project(':hce-library') annotationProcessor project(':hce-processor') }
- Build the library:
./gradlew :hce-library:assembleRelease
- Find the AAR in
hce-library/build/outputs/aar/hce-library-release.aar
- Include in your project:
dependencies { implementation files('libs/hce-library-release.aar') annotationProcessor project(':hce-processor') // Still need the processor }
Add publishing configuration to hce-library/build.gradle
:
apply plugin: 'maven-publish'
publishing {
publications {
release(MavenPublication) {
from components.release
groupId = 'com.codingr.nfclib'
artifactId = 'hce-library'
version = '1.0.0'
}
}
}
public class MyCardSchema implements ApduSchema {
public static final String GET_DATA = "GET_DATA";
@Override
public String getName() { return "MyCard"; }
@Override
public String[] getSupportedAids() {
return new String[]{"F0010203040506"};
}
@Override
public List<ApduCommandSpec> getCommands() {
return Arrays.asList(
new ApduCommandSpec.Builder(GET_DATA, (byte) 0x80, (byte) 0x10)
.name("Get Data")
.possibleResponses(Arrays.asList(
new ApduResponseSpec.Builder("SUCCESS", ApduUtil.SW_OK)
.dataLength(1, 255)
.build()
))
.build()
);
}
}
@ApduController(aids = {"F0010203040506"}) // ← This triggers automatic setup!
public class MyCardController extends SchemaBasedController {
public MyCardController() {
super(new MyCardSchema());
}
@ApduMapping(command = {(byte) 0x80, (byte) 0x10, (byte) 0x00, (byte) 0x00})
public ApduResponse getData(byte[] apdu) {
ApduCommandSpec commandSpec = getSchema().findCommandByApdu(apdu);
return handleSchemaCommand(commandSpec, apdu);
}
@Override
public ApduResponse handleSchemaCommand(ApduCommandSpec commandSpec, byte[] apduBytes) {
switch (commandSpec.getCommandId()) {
case MyCardSchema.GET_DATA:
return ok("Hello World!".getBytes());
default:
return error(CardError.INS_NOT_SUPPORTED);
}
}
}
That's it! The annotation processor automatically:
- ✅ Adds NFC permissions to your AndroidManifest.xml
- ✅ Registers the ApduRouterService
- ✅ Creates aid_list.xml with your controller's AIDs
- ✅ Sets up all required NFC hardware declarations
public class MyCardClient {
private ClientFactory.GenericSchemaClient client;
public void connectAndUse() throws ClientFactory.NfcCommunicationException {
MyCardSchema schema = new MyCardSchema();
NfcTransport transport = new AndroidNfcTransport(); // Your implementation
client = ClientFactory.createGenericClient(schema, transport,
new MyResponseHandler());
// Use schema-defined AIDs and commands
client.selectApplication("F0010203040506");
String result = client.sendCommand(MyCardSchema.GET_DATA, null);
System.out.println("Received: " + result);
}
class MyResponseHandler implements ClientFactory.ClientResponseHandler {
@Override
public <T> T handleResponse(ApduCommandSpec commandSpec,
ApduResponseSpec responseSpec, byte[] rawResponse) {
if (responseSpec != null && responseSpec.isSuccess()) {
byte[] data = responseSpec.extractData(rawResponse);
return (T) new String(data);
}
return (T) "Error";
}
}
}
Unlike other NFC libraries, you don't need to manually add:
- ❌ NFC permissions to AndroidManifest.xml
- ❌ Service declarations
- ❌ aid_list.xml files
- ❌ Intent filters or meta-data
Just add @ApduController
to your controller class and everything is handled automatically!
- Android API Level: 21+ (Android 5.0)
- NFC Hardware: Required
- HCE Support: Required
- Java Version: 8+
If you're migrating from manual NFC setup:
- Remove manual manifest entries (the processor handles them)
- Replace your service class with
@ApduController
on your controller - Use
@ApduMapping
for command routing - Optionally adopt schema-based approach for better structure
[Add your license here]