NFC: Managing Near Field Radio Communication
This tutorial demonstrates how you can manage Near Field Communication (NFC) and send and receive NFC Data Exchange Format (NDEF) messages using the NFC peer-to-peer (P2P) mode.
Warm-up
Become familiar with the NFC API basics by learning about:
- NFC
-
Initializing NFC
Initialize NFC for use.
-
Enabling and Disabling NFC
Enable or disable NFC using the toggle application.
-
Working with NFC
Initialize NFC asynchronously, set the NFC tag filter, and register all necessary NFC callbacks.
-
Getting a Cached NFC Message
Initialize NFC synchronously and get a cached NFC message.
-
Using the Card_Emulation Feature
Create a Card Emulation application.
-
NFC Application Control
Using NFC Application Control.
-
Initializing NFC
- NFC P2P bump
-
Initializing NFC P2P
Initialize NFC P2P for use.
-
Sending and Receiving a Message through NFC P2P
Exchange NDEF messages using NFC P2P between 2 devices.
-
Initializing NFC P2P
Initializing NFC
Near Field Communication (NFC) is an international standard (ISO/IEC 18092) that specifies an interface and a protocol for simple wireless interconnection of closely coupled devices. There are 3 groups of application scenarios for NFC:
- Device close to a wireless tag
- Device close to another device (target)
- Device close to a point of sales terminal
In the NFC tutorial, the NFC API (in mobile and wearable applications) is used to write an application which initializes the NFC adapter and registers callbacks called in case of each connection scenario, so the user is able to work with NFC manually. By connecting your device to the other NFC point (tag, device, or sales terminal), you can invoke the specified callback registered earlier. The second scenario of this tutorial shows how to get the message cached by NFC.
To initialize NFC:
-
To use the functions and data types of the NFC API (in mobile and wearable applications), include the <nfc.h> header file in your application:
#include <nfc.h>
-
Check whether the device you want to work with supports NFC. This can be done by calling the nfc_manager_is_supported() function. It takes no parameters and it simply returns true if NFC is supported on the device and false otherwise.
void Network_NFC_startup(void) { gmainloop = g_main_loop_new(NULL, FALSE); bool is_nfc_supported = nfc_manager_is_supported(); if (!is_nfc_supported) dlog_print(DLOG_INFO, LOG_TAG, "is_nfc_supported NOT SUPPORTED"); }
The gmainloop, which is being created here, is used to wait for the results of calling asynchronous functions.
-
When the work with NFC is finished, the nfc_manager_deinitialize() function must be called to clear the environment.
void Network_NFC_cleanup(void) { g_main_loop_unref (gmainloop); nfc_manager_deinitialize(); }
Enabling and Disabling NFC
To enable or disable NFC:
The NFC API does not contain functions for enabling or disabling NFC. Use the NFC toggle application illustrated in the following figure (off screen on the left and on screen on the right).
Figure: NFC toggle application
#include <app_control.h> #include <dlog.h> int nfc_onoff_operation(void) { int ret = 0; app_control_h service = NULL; app_control_create(&service); if (service == NULL) { dlog_print(DLOG_INFO, LOG_TAG, "service_create failed!\n"); return 0; } app_control_set_operation(service, "http://tizen.org/appcontrol/operation/setting/nfc"); app_control_add_extra_data(service, "type", "nfc"); ret = app_control_send_launch_request(service, NULL, NULL); app_control_destroy(service); if (ret == APP_CONTROL_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "Succeeded to NFC On/Off app!\n"); return 0; } else { dlog_print(DLOG_INFO, LOG_TAG, "Failed to relaunch NFC On/Off app!\n"); return -1; } return 0; }
Working with NFC
To work with NFC manually, you need to:
- Initialize NFC
- Register for notifications
- Work with NFC manually
- Clean up at the end
Initializing NFC
To initialize NFC:
-
The first function to be used is nfc_manager_initialize().
int error_code = NFC_ERROR_NONE; error_code = nfc_manager_initialize(); if (NFC_ERROR_NONE != error_code) // Error occurred g_timeout_add(1000, timeout_func, gmainloop); g_main_loop_run(gmainloop);
After calling the nfc_manager_initialize() function run gmainloop to wait for the result of the initialization. It is closed when the time set in the g_timeout_add function elapses. This time is in milliseconds so the timeout_func is called after 1 second passes.
-
When the initialization is finished, call the nfc_manager_set_activation_changed_cb() function. The function registers the callback that is invoked every time the activation state of NFC changes. The parameters are:
- Activation state changed callback
- Data passed to the callback
error_code = nfc_manager_set_activation_changed_cb(on_nfc_activation_changed, NULL);
The code of the on_nfc_activation_changed callback is simple and looks like this:
static void on_nfc_activation_changed(bool activated, void *user_data) { if (activated) dlog_print(DLOG_INFO, LOG_TAG, "NFC activated"); else dlog_print(DLOG_INFO, LOG_TAG, "NFC deactivated"); }
So in our example, this callback only informs the user that the activation state has changed.
-
After this step, the nfc_manager_set_tag_filter() function is used. It declares the tag filtering option. Use a bit operation of the type nfc_tag_filter_e to specify the type of filtering. The default value is NFC_TAG_FILTER_ALL_ENABLE, which means that all tag types are enabled.
nfc_manager_set_tag_filter(NFC_TAG_FILTER_ALL_ENABLE);
-
You need to register callback functions to receive discovery notifications for tag, NDEF, peer-to-peer, secure element, and secure element transaction events. To register a callback function for receiving tag discovery notifications, use the nfc_manager_set_tag_discovered_cb() function. The first argument is the on_nfc_tag_discovered() callback function described later in the tutorial. The second argument is a user data parameter. In our case, that parameter is not needed and you can pass a NULL value to the function. In the same way, register NDEF, peer-to-peer, secure element, and secure element transaction event notifications using:
- Use the nfc_manager_set_ndef_discovered_cb() function to register an NDEF event notifications with the on_nfc_ndef_discovered() callback function
- nfc_manager_set_p2p_target_discovered_cb() to register peer-to-peer event notifications with the on_nfc_p2p_target_discovered() callback function
- nfc_manager_set_se_event_cb() to register secure element event notifications with the on_nfc_se_event() callback function
- nfc_manager_set_se_transaction_event_cb() to register secure element transaction event notifications with the on_nfc_se_transaction_event() callback function
error_code = nfc_manager_set_tag_discovered_cb(on_nfc_tag_discovered, NULL); if (NFC_ERROR_NONE != error_code) // Error occurred error_code = nfc_manager_set_ndef_discovered_cb(on_nfc_ndef_discovered, NULL); if (NFC_ERROR_NONE != error_code) // Error occurred error_code = nfc_manager_set_p2p_target_discovered_cb(on_nfc_p2p_target_discovered, NULL); if (NFC_ERROR_NONE != error_code) // Error occurred error_code = nfc_manager_set_se_event_cb(on_nfc_se_event, NULL); if (NFC_ERROR_NONE != error_code) // Error occurred error_code = nfc_manager_set_se_transaction_event_cb(NFC_SE_TYPE_ESE, on_nfc_se_transaction_event, NULL); if (NFC_ERROR_NONE != error_code) // Error occurred
-
Check whether system handling for tag and target discovery is enabled or disabled. By default it is enabled. Use the nfc_manager_is_system_handler_enabled() function to check the current state. If the function returns FALSE, enable system handling using the nfc_manager_set_system_handler_enable() function and pass the TRUE value as an input parameter.
if (nfc_manager_is_system_handler_enabled() != true) nfc_manager_set_system_handler_enable(true);
Working with NFC Manually
After initializing NFC, you can start using NFC on the device. You can connect to other devices, resulting in launching the code from the registered callbacks.
Working with NFC Tags
When a tag is discovered, the on_nfc_tag_discovered() callback registered before is called. The first parameter of the function is the NFC discovered type. Second is a tag handle and the last is user data. In the described example, the on_nfc_tag_discovered() callback prints out the information about the discovered tag.
At the beginning of this callback, the NFC discovered type is checked. It can be NFC_DISCOVERED_TYPE_ATTACHED if the tag has connected to the device and NFC_DISCOVERED_TYPE_DETACHED if the tag was detached. If the tag is attached, you can print out information read from it.
To get the type of the tag, the nfc_tag_get_type() function must be called. It takes 2 parameters. The first one is the handle to the tag and the second is a pointer to a (nfc_tag_type_e) variable, where the type of the tag is stored.
nfc_tag_get_type(tag, &tag_type);
You can also get the size of the NDEF message stored in the tag by calling the nfc_tag_get_ndef_size() function. The parameters are similar to the ones described above, but instead of the tag_type variable, the pointer is passed to an unsigned int variable where the current size of the tag is stored.
nfc_tag_get_ndef_size(tag, &size);
The nfc_tag_get_maximum_ndef_size() function can be used to get the maximum NDEF message size that can be stored in the tag. It also takes 2 parameters, where the first one is a tag handle and the second is a pointer to an unsigned int where the maximum size of the tag is stored.
nfc_tag_get_maximum_ndef_size(tag, &maximum_ndef_bytes_size);
After getting the basic tag information, retrieve all remaining tag information by calling the nfc_tag_foreach_information() function. This function takes 3 parameters. The first one is the tag that is operated on. The second one is a callback that is called for each found pair of key and value in the tag. The last one is user data that can be passed to the callback, but as there is no need to pass any data, NULL is passed.
error_code = nfc_tag_foreach_information(tag, on_nfc_tag_information_event, NULL); if (NFC_ERROR_NONE != error_code) // Error occurred
The on_nfc_tag_information_event() callback in this tutorial is really simple. It just prints out the found keys and their values.
on_nfc_tag_information_event(const char *key, const unsigned char *value, int value_size, void *user_data) { dlog_print(DLOG_INFO, LOG_TAG, "Title: %s, Value: %s", key, (char*)value); return true; }
true is returned to continue the iteration over all other pairs of keys and values. If false is returned, then the iteration stops.
After these actions, check the nfc tag filter and print it out. Doing this ensures us that the callback was called and informs us what kind of a tag filter is set right now. For example, if the callback has not printed any information about the tag, but it printed out that the tag filter is NFC_TAG_FILTER_ALL_DISABLE, then you know that the filter needs to be set to the proper value to read the specific tag type. To check the current tag filter, you can use the nfc_manager_get_tag_filter() function.
filter = nfc_manager_get_tag_filter();
The last thing left in the described callback is the nfc_tag_read_ndef() function. It enables reading the tag data. 3 parameters are used:
- Tag handle
- Callback invoked when the reading is completed
- User data passed to the callback
error_code = nfc_tag_read_ndef(tag, on_nfc_tag_read_completed, NULL); if (NFC_ERROR_NONE != error_code) // Error occurred
After the tag message has been read, the on_nfc_tag_read_completed() callback is called. It has 3 parameters:
- error_code – defining status of reading the message
- message (nfc_ndef_message_h) – message read from the tag
- user_data – data passed to the callback, NULL if no data need to be passed
static void on_nfc_tag_read_completed(int error_code, nfc_ndef_message_h message, void *user_data) { nfc_ndef_message_read_cb(clone_message(message)); }
If there was no error (error_code == NFC_ERROR_NONE), you can operate on the retrieved message. To present how to use other NFC API functionality, 2 additional functions have been presented. Firstly, clone the message by calling the clone_message() function:
static nfc_ndef_message_h clone_message(nfc_ndef_message_h msg) { unsigned char *rawdata; unsigned int rawdata_size; nfc_ndef_message_h msg_cp; nfc_ndef_message_get_rawdata(msg, &rawdata, &rawdata_size); nfc_ndef_message_create_from_rawdata(&msg_cp, rawdata, rawdata_size); free(rawdata); return msg_cp; }
As it can be seen, 2 NFC API functions are used here. The first one is the nfc_ndef_message_get_rawdata() function. It gets a copy of the bytes array of the NDEF message. It takes 3 parameters:
- The handle to NDEF message
- [out] rawdata – two dimensional bytes array
- [out] rawdata_size – the size of bytes array
[out] parameters are variables that must be passed to be fulfilled inside the called function.
In the end, rawdata must be released with free().
The second function is nfc_ndef_message_create_from_rawdata(), and it creates an NDEF message handle from raw serial bytes. Use it to create a copy of the message retrieved from the tag. 3 parameters need to be provided:
- [out] Handle to the message (nfc_ndef_message_h)
- rawdata
- rawdata_size
Now let's discuss the second function prepared to show the functionality of the NFC API. It takes as a parameter a handle to the tag message. Pass the cloned message to it.
nfc_ndef_message_read_cb(clone_message(message));
First, get the number of records stored in the tag message. To do this, use the nfc_ndef_message_get_record_count() function. It needs the handle to the message that is operated on and the pointer to an integer which is fulfilled by the function with the number of records in the message.
error_code = nfc_ndef_message_get_record_count(message, &count); if (NFC_ERROR_NONE != error_code) // Error occurred
Iterate through all the records in the message and get all information stored in each record. The first called function is nfc_ndef_message_get_record(). This function gets a record from the message by index. It returns a pointer to the record so if you change the record, it directly affects the NDEF message.
error_code = nfc_ndef_message_get_record(message, i, &rec); if (NFC_ERROR_NONE != error_code) // Error occurred
Parameters are:
- Handle to the NDEF message
- Index of the record
- [out] Handle to the record
Now, when the pointer to the specific record exists, get the record data:
- Record ID by calling nfc_ndef_record_get_id()
error_code = nfc_ndef_record_get_id(rec, &id, &id_len); if (NFC_ERROR_NONE != error_code) // Error occurred
- Record type using the nfc_ndef_record_get_type() function
error_code = nfc_ndef_record_get_type(rec, &type_str, &type_len); if (NFC_ERROR_NONE != error_code) // Error occurred
- Record TNF (Type Name Format) with the nfc_ndef_record_get_tnf() function
error_code = nfc_ndef_record_get_tnf(rec, &tnf); if (NFC_ERROR_NONE != error_code) // Error occurred
- Record payload by calling the nfc_ndef_record_get_payload() function
error_code = nfc_ndef_record_get_payload(record, &payload, &payload_len); if (NFC_ERROR_NONE != error_code) // Error occurred
To get more information from the tag, specify what type of a tag message you are dealing with. If there is message with Type = "T" and the TNF is NFC_RECORD_TNF_WELL_KNOWN, then it is possible to get the following data:
- Record text with the nfc_ndef_record_get_text() function
error_code = nfc_ndef_record_get_text(record, &text); if (NFC_ERROR_NONE != error_code) // Error occurred
- Record text language code by using the nfc_ndef_record_get_langcode() function
error_code = nfc_ndef_record_get_langcode(record, &language); if (NFC_ERROR_NONE != error_code) // Error occurred
- Record text encoding type by calling nfc_ndef_record_get_encode_type()
error_code = nfc_ndef_record_get_encode_type(record, &encode); if (NFC_ERROR_NONE != error_code) // Error occurred
If there is a message with Type="U" and TNF is also NFC_RECORD_TNF_WELL_KNOWN, can get the URI using the nfc_ndef_record_get_uri() function:
error_code = nfc_ndef_record_get_uri(record, &uri); if (NFC_ERROR_NONE != error_code) // Error occurred
Finally, if the TNF of the record is NFC_RECORD_TNF_MIME_MEDIA, then it is possible to get the record mime type.
error_code = nfc_ndef_record_get_mime_type(record, &mime); if (NFC_ERROR_NONE != error_code) // Error occurred
Working with NFC NDEF Messages
It is the same as described in working with the NFC tag NDEF message. In the registered callback on_nfc_ndef_discovered(), get the number of records in the message and then iterate through those records. After getting the handles to those records, get the payloads of those records:
static void on_nfc_ndef_discovered(nfc_ndef_message_h message, void *user_data) { int count, i; unsigned int size; unsigned char * payload; nfc_ndef_record_h record; nfc_ndef_message_get_record_count(message, &count); dlog_print(DLOG_INFO, LOG_TAG, "on_nfc_ndef_discovered %d", count); for (i=0; i<count; i++) { nfc_ndef_message_get_record(message, i, &record); nfc_ndef_record_get_payload (record, &payload, &size); dlog_print(DLOG_INFO, LOG_TAG, "Record Number : %d, Payload : %s", i, payload); } }
Working with NFC P2P Target
This section discusses the callback that is invoked on peer-to-peer target discovery. When the device is connected to some P2P target, it is possible to exchange NDEF data with that peer target. It allows:
- Sending NDEF messages
- Receiving NDEF messages
In this tutorial, our registered callback for such a connection is called on_nfc_p2p_target_discovered(). It has the following parameters:
- The type of NFC discovery (nfc_discovered_type_e)
- The handle to the target
- User data
First, check the first parameter (type). If the type is NFC_DISCOVERED_TYPE_ATTACHED, then it means that the remote device was attached to the device.
if (type == NFC_DISCOVERED_TYPE_ATTACHED) { // Remote device is attached; execute code }
Use the nfc_p2p_set_data_received_cb() function to register a callback that is invoked when any data from the connected target is received.
error_code = nfc_p2p_set_data_received_cb(target, on_nfc_p2p_read_completed, 0); if (NFC_ERROR_NONE != error_code) // Error occurred
The code of the registered on_nfc_p2p_read_completed() callback is presented below:
static void on_nfc_p2p_read_completed(nfc_p2p_target_h target, nfc_ndef_message_h message, void *user_data) { int count; dlog_print(DLOG_INFO, LOG_TAG, "on_nfc_p2p_read_completed"); nfc_ndef_message_get_record_count(message, &count); nfc_ndef_message_read_cb(clone_message(message)); }
After reading the message from the remote device, check its number of records using the nfc_ndef_message_get_record_count() function and get more detailed info about the message by calling the nfc_ndef_message_read_cb() function described earlier.
Working with NFC Secure Element
The secure element event notification is received through the on_nfc_se_event() function. The first parameter defines the event type. The second one is user data - in this case it is not used. Depending on the event type, you can take some additional actions.
static void on_nfc_se_event(nfc_se_event_e event, void *user_data) { switch (event) { case NFC_SE_EVENT_START_TRANSACTION: // This event notifies the terminal host that it shall launch // an application associated with an NFC application in a // UICC (Universal Integrated Circuit Card) host dlog_print(DLOG_INFO, LOG_TAG, "NFC EVENT: Start Transaction"); break; case NFC_SE_EVENT_END_TRANSACTION: // This event notifies the terminal host that the current transaction // in process was ended dlog_print(DLOG_INFO, LOG_TAG, "NFC EVENT: End Transaction"); break; case NFC_SE_EVENT_CONNECTIVITY: // It is a ready signal to communicate UICC (Universal Integrated Circuit // Card) with terminal host. \nUICC (Universal Integrated Circuit Card) // creates a pipe and opens the pipe channel.\nThen it sends the signal // to terminal host or host controller dlog_print(DLOG_INFO, LOG_TAG, "NFC EVENT: Connectivity"); break; case NFC_SE_EVENT_FIELD_ON: // When the CLF (Contactless Front-end) detects 5ra RF field, the card // RF gate sends the event #NFC_SE_EVENT_FIELD_ON to the card // application gate.\nWhen there are multiple open card RF gates // the CLF shall send the #NFC_SE_EVENT_FIELD_ON on all open pipes // to these gates. Next the CLF starts the initialization // and anti-collision process as defined in ISO/IEC 14443-3 [6] dlog_print(DLOG_INFO, LOG_TAG, "NFC EVENT: Field ON"); break; case NFC_SE_EVENT_FIELD_OFF: // When the CLF (Contactless Front-end) detects that the RF field // is off, the card RF gate shall send #NFC_SE_EVENT_FIELD_OFF to // the card application gate.\nWhen there are multiple open card RF // gates the CLF shall send the #NFC_SE_EVENT_FIELD_OFF to one gate // only dlog_print(DLOG_INFO, LOG_TAG, "NFC EVENT: Field OFF"); break; case NFC_SE_EVENT_TRANSACTION: // This event notifies that an external reader is trying to access a secure // element dlog_print(DLOG_INFO, LOG_TAG, "NFC EVENT: Remote Start Transaction"); break; default: dlog_print(DLOG_INFO, LOG_TAG, "NFC EVENT: DEFAULT:OTHER"); break; } }
The secure element transaction event gives you 4 information parameters and user data. You can get the application ID (specified in ISO/IEC 7816-4) from the first parameter, size of aid from the second parameter, the parameter list (specified in ISO/IEC 8825-1) from the third parameter, and the size of the parameter from the next function input parameter.
static void on_nfc_se_transaction_event(nfc_se_type_e se_type, unsigned char *aid, int aid_size, unsigned char *param, int param_size, void *user_data) { dlog_print(DLOG_INFO, LOG_TAG, "Secure Element(SIM/UICC(Universal Integrated Circuit Card)) transaction event data"); }
Getting a Cached NFC Message
To get a cached NFC message:
- Initialize NFC.
To initialize NFC, the nfc_manager_initialize() function can be used, as shown in Initializing NFC.
- Set the NFC tag filter.
Setting the NFC tag filter (as shown in Working with NFC Tags). Just as a reminder, setting the tag filter is possible by using the nfc_manager_set_tag_filter() function.
nfc_manager_set_tag_filter(NFC_TAG_FILTER_ALL_ENABLE);
- Enable the NFC system handler.
Before getting the cached message, enable the system handler:
if (nfc_manager_is_system_handler_enabled() != true) nfc_manager_set_system_handler_enable(true);
- Get the cached message.
Get the cached message by calling the nfc_manager_get_cached_message() function. Pass a variable of the nfc_ndef_message_h type, which is fulfilled with the cached message by the function.
nfc_ndef_message_h message = NULL; error_code = nfc_manager_get_cached_message(&message); if (NFC_ERROR_NONE != error_code) // Error occurred
After getting the message, get the detailed information from the message as it was described before. To do this, check whether there were any errors and whether the message is not NULL.
if (message != NULL) { on_nfc_ndef_discovered(clone_message(message), NULL); }
- Clean up at the application end.
Using the Card Emulation Feature
To create a Card Emulation application:
- Initialize the Card Emulation application:
-
To initialize NFC, use the nfc_manager_initialize() function:
int ret = NFC_ERROR_NONE; ret = nfc_manager_initialize(); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_manager_initialize failed : %d", ret); return false; }
- Use the app control to enable NFC.
- Make sure that card emulation is enabled. If not, enable it.
nfc_se_card_emulation_mode_type_e ce_type; ret = nfc_se_get_card_emulation_mode(&ce_type); if (ret == NFC_ERROR_NONE && ce_type != true) { ret = nfc_se_enable_card_emulation(); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_enable_card_emulation failed : %d", ret); return false; } } else { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_get_card_emulation_mode failed : %d", ret); return false; }
-
Specify a "AID" value for the application. Next two step is required.
1. To tell the platform which AIDs groups are requested by application, a metadata must be included in the manifest file.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns="http://tizen.org/ns/packages" api-version="2.3.1" package="org.tizen.basicuiapplication" version="1.0.0"> <profile name="wearable" /> <ui-application appid="org.tizen.basicuiapplication" exec="basicuiapplication" type="capp" multiple="false" taskmanage="true" nodisplay="false"> <icon>basicuiapplication.png</icon> <label>basicuiapplication</label> <metadata key="http://tizen.org/metadata/nfc_cardemulation" value="/shared/res/wallet.xml"/> </ui-application> </manifest>
- The <metadata> is required to contain a <key> and <value> attribute.
- <key> attribute must be "http://tizen.org/metadata/nfc_cardemulation".
- <value> attribute contain the AID xml file path.
<value> attribute is a relative path starting from the app root path.
2. meta-data tag points to an AID xml file. An example of this file with AID declaration is shown below :
<?xml version="1.0" encoding="utf-8"?> <application name = "org.tizen.basicuiapplication"> <wallet> <aid-group category = "payment"> <aid aid = "325041592E5359532E4444463031" se_type = "hce" unlock = "false" power = "sleep"/> </aid-group> </wallet> </application>
- The <application> is required to contain a <name> attribute that contains a application name.
- The <application> must contain one or more <wallet> tags.
- Each <wallet> must contain one or more <aid-group> tags.
- The <aid-group> is required to contain a <category> attribute that contains <payment> or <other>.
- Each <aid-group> must contain one or more <aid> tags, each of which contains a single AID.
- The<aid-group> can have as much as you want aid
- The <aid> is required to contain a <aid>, <se_type>, <unlock>, <power> attribute.
- The <se_type> must contains "hce" or "ese" or "uicc". se_type value can be added later.
- The <unlock> must contains "true" or "false".
- "true" mean "Card can't work, when device is locked."
- "false" mean "Card can work, when device is locked."
- The <power> must contains "on" or "off" or "sleep".
- "on" mean "Card can work, when device is on."
- "off" mean "Card can work, when device is off."
- "sleep" mean "Card can work, when device is sleep."
- The http://tizen.org/privilege/nfc.cardemulation privilege is required for the HCE API. Add the privilege to the tizen-manifest.xml file.
-
- To create a Host based Card Emulation (HCE) application :
- Define HCE event handling.
The application must be able to handle an HCE event from the NFC reader. Define and register a callback that is triggered when data arrives from the NFC reader.
Use the nfc_hce_send_apdu_response() function to send a response to the NFC reader. The actual data moving between the NFC reader and the application can be anything. The APDU protocol only defines as a promise between the application producer and NFC reader.
static void _hce_event_cb(nfc_se_h handle, nfc_hce_event_type_e event, unsigned char *apdu, unsigned int apdu_len, void *user_data) { switch (event) { case NFC_HCE_EVENT_DEACTIVATED: // Do something when NFC_HCE_EVENT_DEACTIVATED event arrives // When the event arrives, apdu and apdu len is NULL and 0 break; case NFC_HCE_EVENT_ACTIVATED: // Do something when NFC_HCE_EVENT_ACTIVATED event arrives // When the event arrives, apdu and apdu len is NULL and 0 break; case NFC_HCE_EVENT_APDU_RECEIVED: { unsigned char resp[] = {0x00, 0x01, 0x02, 0x03}; // Do something when NFC_HCE_EVENT_APDU_RECEIVED event arrives // You can use the arrival apdu and apdu_len // and send a response to the NFC reader nfc_hce_send_apdu_response(handle, NULL, 4); } break; default: // Error handling break; } } ret = nfc_manager_set_hce_event_cb(_hce_event_cb, NULL); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_manager_set_hce_event_cb failed : %d", ret); return false; }
- Implement optional HCE features:
- To determine whether the application is an active handler for a specific AID or category, use the nfc_se_is_activated_handler_for_aid() and nfc_se_is_activated_handler_for_category() functions:
int ret = NFC_ERROR_NONE; const char[] aid = {0x00, 0x01, 0x02, 0x03}; bool is_activated_handler = false; ret = nfc_se_is_activated_handler_for_aid(aid, &is_activated_handler); if (ret != NFC_ERROR_NONE) { if (is_activated_handler == true) { dlog_print(DLOG_INFO, LOG_TAG, "is_activate_handler is true"); // Do something } else { dlog_print(DLOG_INFO, LOG_TAG, "is_activate_handler is false"); // Do something } } else { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_is_activated_handler_for_aid is failed : %d", ret); } ret = nfc_se_is_activated_handler_for_category(NFC_CARD_EMULATION_CATEGORY_PAYMENT, &is_activated_handler); if (ret != NFC_ERROR_NONE) { if (is_activated_handler == true) { dlog_print(DLOG_INFO, LOG_TAG, "is_activate_handler is true"); // Do something } else { dlog_print(DLOG_INFO, LOG_TAG, "is_activate_handler is false"); // Do something } } else { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_is_activated_handler_for_aid is failed : %d", ret); }
- To register or unregister the AID at application runtime, use the nfc_se_register_aid() and nfc_se_unregister_aid() functions:
int ret = NFC_ERROR_NONE; const char[] aid = {0x0A, 0x0B, 0x0C, 0x0D}; ret = nfc_se_register_aid(NFC_CARD_EMULATION_CATEGORY_OTHER, aid); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_register_aid is failed : %d", ret); return false; } ret = nfc_se_unregister_aid(NFC_CARD_EMULATION_CATEGORY_OTHER, aid); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_unregister_aid is failed : %d", ret); return false; }
- To check whether the application has a registered AID (including a registered AID at the install time), use the nfc_se_foreach_registered_aids() function (the callback is called for each AID value separately):
static void _registered_aid_cb(nfc_se_type_e se_type, const char *aid, bool read_only, void *user_data) { dlog_print(DLOG_INFO, LOG_TAG, "registered_aids callback is called"); // Do something } ret = nfc_se_foreach_registered_aids(NFC_CARD_EMULATION_CATEGORY_OTHER, _registered_aid_cb, NULL); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_foreach_registered_aids failed : %d", ret); return false; }
When an application receives an app-control event, application can receive the aid value using the "data" appcontrol extra key.
- To determine whether the application is an active handler for a specific AID or category, use the nfc_se_is_activated_handler_for_aid() and nfc_se_is_activated_handler_for_category() functions:
- When HCE operations are no longer needed, deinitialize the resources:
int ret = NFC_ERROR_NONE; nfc_manager_unset_hce_event_cb(); ret = nfc_manager_deinitialize(); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_manager_deinitialize failed : %d", ret); }
- Define HCE event handling.
The following example shows the entire HCE application sample code:
#include <service_app.h> #include <nfc.h> #include <dlog.h> static void _hce_event_cb(nfc_se_h handle, nfc_hce_event_type_e event, unsigned char *apdu, unsigned int apdu_len, void *user_data) { switch (event) { case NFC_HCE_EVENT_DEACTIVATED: // Do something when NFC_HCE_EVENT_DEACTIVATED event arrives // When the event arrives, apdu and apdu len is NULL and 0 break; case NFC_HCE_EVENT_ACTIVATED: // Do something when NFC_HCE_EVENT_ACTIVATED event arrives // When the event arrives, apdu and apdu len is NULL and 0 break; case NFC_HCE_EVENT_APDU_RECEIVED: { unsigned char resp[] = {0x00, 0x01, 0x02, 0x03}; // Do something when NFC_HCE_EVENT_APDU_RECEIVED event arrives // You can use the arrival apdu and apdu_len // and send a response to the NFC reader nfc_hce_send_apdu_response(handle, NULL, 4); } break; default: // Error handling break; } } bool service_app_create(void *data) { int ret = NFC_ERROR_NONE; nfc_se_card_emulation_mode_type_e ce_type; ret = nfc_manager_initialize(); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_manager_initialize failed : %d", ret); return false; } // App control ret = nfc_se_get_card_emulation_mode(&ce_type); if (ret == NFC_ERROR_NONE && ce_type != true) { nfc_se_enable_card_emulation(); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_enable_card_emulation failed : %d", ret); return false; } } else { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_se_get_card_emulation_mode failed : %d", ret); return false; } ret = nfc_manager_set_hce_event_cb(_hce_event_cb, NULL); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_manager_set_hce_event_cb failed : %d", ret); return false; } return true; } void service_app_terminate(void *data) { int ret = NFC_ERROR_NONE; nfc_manager_unset_hce_event_cb(); ret = nfc_manager_deinitialize(); if (ret != NFC_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "nfc_manager_deinitialize failed : %d", ret); } return; } void service_app_control(app_control_h app_control, void *data) { // Todo: add your code here return; } void service_app_low_memory_callback(void *data) { // Todo: add your code here service_app_exit(); return; } void service_app_low_battery_callback(void *data) { // Todo: add your code here service_app_exit(); return; } int main(int argc, char* argv[]) { char ad[50] = {0,}; service_app_event_callback_s event_callback; event_callback.create = service_app_create; event_callback.terminate = service_app_terminate; event_callback.app_control = service_app_control; event_callback.low_memory = service_app_low_memory_callback; event_callback.low_battery = service_app_low_battery_callback; return service_app_main(argc, argv, &event_callback, ad); }
NFC Application Control
NFC application controls are kinds of event sent by the system to applications when NFC-based payment transaction occurs.
To receive these events, you are required to define the application control information in the Application Control tab of the manifest editor.
The platform supports the following application controls for NFC applications:
http://tizen.org/appcontrol/operation/nfc/card_emulation/default_changed and http://tizen.org/appcontrol/operation/nfc/card_emulation/host_apdu_service and http://tizen.org/appcontrol/operation/nfc/card_emulation/off_host_apdu_service operations.
default_changed operation
The application control with http://tizen.org/appcontrol/operation/nfc/card_emulation/default_changed is sent by the system when the default wallet is changed.
For example, in Setting > NFC > Set Default Wallet App, if the default wallet is changed, an application control with this operation will be sent to the selected application (wallet).
You have to define the app_control_cb() callback and register it to ui_app_lifecycle_callback::app_control.
host_apdu_service operation
The application control with http://tizen.org/appcontrol/operation/nfc/card_emulation/host_apdu_service operation is sent by the system when HCE event occurs.
For example, when a mobile device receives Host based apdu (HCE) event from POS terminal, an application control with this operation will be sent to NFC applications.
You can get the target AID information using app_control_get_extra_data() with "data" key.
You have to define the app_control_cb() callback and register it to ui_app_lifecycle_callback::app_control.
The following table shows the information comes packaged when the http://tizen.org/appcontrol/operation/nfc/card_emulation/host_apdu_service operation is launching.
Extra key | Description |
---|---|
data | Target AID key |
off_host_apdu_service operation
The application control with http://tizen.org/appcontrol/operation/nfc/card_emulation/off_host_apdu_service operation is sent by the system when SE transaction occurs.
For example, when a mobile device receives Off-Host apdu event from POS terminal, an application control with this operation will be sent to NFC applications.
You can get the target AID information using app_control_get_extra_data() with "data" key.
You have to define the app_control_cb() callback and register it to ui_app_lifecycle_callback::app_control.
The following table shows the information comes packaged when the http://tizen.org/appcontrol/operation/nfc/card_emulation/off_host_apdu_service operation is launching.
Extra key | Description |
---|---|
data | Target AID key |
Application control example
To tell the platform which application is register the opeartion, a operation must be included in the manifest file.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns="http://tizen.org/ns/packages" api-version="2.3.1" package="org.tizen.basicuiapplication" version="1.0.0"> <profile name="wearable" /> <ui-application appid="org.tizen.basicuiapplication" exec="basicuiapplication" type="capp" multiple="false" taskmanage="true" nodisplay="false"> <icon>basicuiapplication.png</icon> <label>basicuiapplication</label> <app-control> <operation name="http://tizen.org/appcontrol/operation/nfc/card_emulation/default_changed"/> <operation name="http://tizen.org/appcontrol/operation/nfc/card_emulation/host_apdu_service"/> <operation name="http://tizen.org/appcontrol/operation/nfc/card_emulation/off_host_apdu_service"/> </app-control> </ui-application> </manifest>
The code of the NFC application control is presented below:
... bool service_app_control(app_control_h service, void *data) { struct _appdata *ad = user_data; char *operation, *aid; int ret = 0; app_control_get_operation(service, &operation); if ((strncmp(operation, NFC_APPCONTROL_STRING, strlen(NFC_APPCONTROL_STRING)) == 0)) { dlog_print(DLOG_INFO, "HCETESTAPP", "nfc appcontrol operation : [%s]", operation); app_control_get_extra_data(service, "data", &aid); dlog_print(DLOG_INFO, "HCETESTAPP", "nfc appcontrol uri : [%s]", aid); } } ... int main(int argc, char* argv[]) { char ad[50] = {0,}; service_app_event_callback_s event_callback; event_callback.create = service_app_create; event_callback.terminate = service_app_terminate; event_callback.app_control = service_app_control; event_callback.low_memory = service_app_low_memory_callback; event_callback.low_battery = service_app_low_battery_callback; return service_app_main(argc, argv, &event_callback, ad); }
Initializing NFC P2P
To initialize NFC P2P:
Make sure you have 2 target devices that support the NFC P2P mode. Note that the device screen should be unlocked to use NFC.
-
To use the functions and data types of the NFC API (in mobile and wearable applications), include the <nfc.h> header file in your application:
#include <nfc.h>
- To start using the NFC API, initialize the API by calling the nfc_manager_initialize() function:
nfc_manager_initialize();
-
After the initialization of the API manager, ensure that NFC is supported and activated on the device. The nfc_manager_is_supported() function checks whether NFC is supported. The nfc_manager_is_activated() function gets the NFC activation state.
if (!nfc_manager_is_supported()) { // Report error, end the application } if (!nfc_manager_is_activated()) { // Report error, switch on NFC }
-
At the end of the application life-cycle, call the nfc_manager_deinitialize() function. It releases all resources of the NFC manager and disconnects the session between it and your application.
nfc_manager_deinitialize();
Sending and Receiving a Message through NFC P2P
To send and receive messages using the NFC P2P mode (a simple NDEF message containing a business card (name, phone number, and e-mail address) of the device owner is prepared and exchanged with the second device):
- Prepare the NDEF message.
An NDEF message consists of several NDEF records. A record payload type is determined by two values: the TNF (Type Name Format) and type. There are a few TNFs and related types of the NDEF records, like text record, URI record, and MIME record. In this tutorial, only text records are used.
The sample message in this tutorial contains a name, phone number, and e-mail address of the device owner. Values can be stored in a file or taken from the UI of the application – in this tutorial getting values has been omitted.
To create a text record, use the nfc_ndef_record_create_text() function. Its arguments are a record handle, the text to store, the language code (for example en-US or ko-KR), and the encoding type. The following example creates 3 records for a name, phone number, and e-mail address:
nfc_ndef_record_h ndef_name_record = NULL; nfc_ndef_record_h ndef_phone_record = NULL; nfc_ndef_record_h ndef_email_record = NULL; const char *name = "John Doe"; const char *phone = "+82556666888"; const char *email = "john.doe@tizen.org"; nfc_ndef_record_create_text(&ndef_name_record, name, "en-US", NFC_ENCODE_UTF_8); nfc_ndef_record_create_text(&ndef_phone_record, phone, "en-US", NFC_ENCODE_UTF_8); nfc_ndef_record_create_text(&ndef_email_record, email, "en-US", NFC_ENCODE_UTF_8);
When the records are created, they should be appended to a message. Before that, create and initialize an NDEF message using the nfc_ndef_message_create() function. As an argument, pass a handle to the created message.
nfc_ndef_message_h ndef_message = NULL; nfc_ndef_message_create(&ndef_message);
Append the created records to the message using the nfc_ndef_message_append_record() function. This function appends the record with the next index. To insert a record at the specified index, use the nfc_ndef_message_insert_record() function instead.
nfc_ndef_message_append_record(ndef_message, ndef_name_record); nfc_ndef_message_append_record(ndef_message, ndef_phone_record); nfc_ndef_message_append_record(ndef_message, ndef_email_record);
- Notify the application about the discovered P2P target.
To exchange messages using P2P, firstly register a callback for receiving notifications about discovered P2P targets using the nfc_manager_set_p2p_target_discovered_cb() function. When the P2P target is discovered, the callback provides a handle to that device and information on whether it is attached or detached.
nfc_manager_set_p2p_target_discovered_cb(on_target_discovered, NULL);
- Notify the application about the received data.
In this tutorial, both devices receive and send a message to each other, so when another P2P target is attached, register a callback for receiving notifications about received data from this device. Use the nfc_p2p_set_data_received_cb() function (the best way is to place this code in the callback called after the P2P device is discovered). Specify the peer target handle – it was provided by the previously set callback.
nfc_p2p_set_data_received_cb(target, on_p2p_data_received, NULL);
- Send a message to another device.
When another P2P device is attached, send the prepared message to it. You can use the nfc_p2p_send() function if you do not want to check permissions. Provide a target handle and a sent message handle. You can also set a callback called when the sending is completed.
nfc_p2p_send(target, ndef_message, NULL, NULL);
- Receive a message from another device.
When the callback about receive data is called, the device receives a message from another device. The callback provides a handle to the received message and a handle to the message source.
Get the number of records in the received message using the nfc_ndef_message_get_record_count() function. In this example, the number should be 3, since there are 3 records a name, phone number, and e-mail address.
int count; nfc_ndef_message_get_record_count(message, &count);
To get a specified record from the message, use the nfc_ndef_message_get_record() function. Specify a message handle, a record index, and a handle to store the obtained record. When the text record is obtained, get the stored text using the nfc_ndef_record_get_text() function. In the tutorial, there are 3 text records to obtain.
nfc_ndef_record_h ndef_record; char *name = NULL; nfc_ndef_message_get_record(message, 0, &ndef_record); nfc_ndef_record_get_text(ndef_record, &name); char *phone = NULL; nfc_ndef_message_get_record(message, 1, &ndef_record); nfc_ndef_record_get_text(ndef_record, &phone); char *email = NULL; nfc_ndef_message_get_record(message, 2, &ndef_record); nfc_ndef_record_get_text(ndef_record, &email);
Then you can use obtained values to create, for example, a new contact.
Note that depending on the record type, some obtained values should be freed and others should not. For example, you should free the obtained text from a text record. See the documentation to know how to get the payload of other types of records.