NFC Programming in Android | NFC Android Tutorial

Android 10 and higher

Devices running Android 10 (API level 29) or higher support
Secure
NFC. While Secure
NFC is on, all card emulators (host applications and off-host applications) are
unavailable when the device screen is off. While Secure NFC is off, off-host
applications are available when the device screen is off. You can check for
Secure NFC support using
isSecureNfcSupported().

On devices running Android 10 and higher, the same functionality for setting
android:requireDeviceUnlock to true applies as with devices
running Android 9 and lower, but only when Secure NFC is turned off.

Android 12 and higher

In apps that target Android 12 (API level 31) and higher, you can enable NFC
payments without the device’s screen on by setting
requireDeviceScreenOn to
false.

Android 13 and higher

To better fit the default payment selection list in the Settings UI, adjust the
banner requirement to a square icon. Ideally, it should be identical to the
application launcher icon design. This adjustment creates more consistency and a
cleaner look.

Android 9 and lower

On devices that run Android 9 (API level 28) and lower, the NFC controller and
the application processor are turned off completely when the screen of the
device is turned off. HCE services therefore don’t work when the screen is off.

Also on Android 9 and lower, HCE services can function from the lock screen.
However, this is controlled by the android:requireDeviceUnlock attribute in
the <host-apdu-service> tag of your HCE service. By default, device unlock is
not required, and your service is invoked even if the device is locked.

Android nfc references

1.     Download and save the entire software project

2.     Android NFC

Apdu data exchange

As noted earlier, HCE implementations support only a single logical channel.
Attempting to select applications on different logical channels doesn’t work on
a HCE device.

Check for hce support

Your application can check whether a device supports HCE by checking for the
FEATURE_NFC_HOST_CARD_EMULATION
feature. Use the <uses-feature>
tag in the manifest of your application to declare that your app uses the HCE
feature, and whether it is required for the app to function or not.

Check if your service is the default

Applications can check whether their HCE service is the default service for a
certain category by using the
isDefaultServiceForCategory()
API.

If your service isn’t the default, you can request it to be made the default
using
ACTION_CHANGE_DEFAULT.

Coexistence with secure element cards

This section is of interest for developers who have deployed an application
that relies on a secure element for card emulation. Android’s HCE implementation
is designed to work in parallel with other methods of implementing card
emulation, including the use of secure elements.

This coexistence is based on a principle called AID routing. The NFC
controller keeps a routing table that consists of a (finite) list of routing
rules. Each routing rule contains an AID and a destination. The destination can
either be the host CPU, where Android apps are running, or a connected secure
element.

When the NFC reader sends an APDU with a SELECT AID, the NFC controller parses
it and checks whether the AIDs match with any AID in its routing table. If it
matches, that APDU and all APDUs following it are sent to the destination
associated with the AID, until another SELECT AID APDU is received or the NFC
link is broken.

Figure 4 illustrates this architecture:

Getting uid of nfc in android

I am learning Android and NFC programming through the official android developer tutorial. What I want is to write an app that will be triggered by NFC tag. When the app starts, I want it to display a toast message containing the UID of the scanned tag. My simple code to achieve this is:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
  Toast.makeText(getApplicationContext(), "UID: "   bin2hex(tag.getId()),    Toast.LENGTH_SHORT).show();
}
// Parsing binary to string
static String bin2hex(byte[] data) {
    return String.format("%0"   (data.length * 2)   "X", new BigInteger(1,data));
}

I also update the manifest file to enable NFC as shown below:

<uses-permission android:name="android.permission.NFC"/>
  ....
<uses-feature android:name="android.hardware.nfc" android:required="true" />

<application  
    <activity
        android:name="com.testapp.testnfc.MainActivity"
        android:label="@string/app_name" >
        .....
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="text/plain" />
   </intent-filter>
    </activity>
</application>

Then I use “NFC Task Launcher” app from Play Store to write into my tag so that the tag will trigger my new app. After creating the tag, tapping on it will successfully start my new app, but the app fails to display the toast of the UID and it will crash stating “Unfortunately Test NFC has stopped.” What did I miss to cause this crash?

Host-based card emulation

When an NFC card is emulated using host-based card emulation, the data is routed
directly to the host CPU instead of being routed to a secure element. Figure 2
illustrates how host-based card emulation works:

Diagram with NFC reader going through an NFC controller to retrieve information from the CPU
Figure 2. NFC card emulation without a secure element.

Implement an hce service

To emulate an NFC card using host-based card emulation, you need to create a
Service component that handles the NFC transactions.

Iso-dep activation

After the Nfc-A protocol is activated, the ISO-DEP protocol activation is
initiated by the NFC reader. It sends a “RATS” (Request for Answer To Select)
command. The RATS response, the ATS, is completely generated by the NFC
controller and not configurable by HCE services.

The section below provides more details on the individual bytes of the ATS
response provided by the NFC controller on a HCE device:

  • TL: length of the ATS response. Must not indicate a length greater than 20
    bytes.
  • T0: bits 5, 6 and 7 must be set on all HCE devices, indicating TA(1), TB(1)
    and TC(1) are included in the ATS response. Bits 1 to 4 indicate the FSCI,
    coding the maximum frame size. On HCE devices the value of FSCI must be
    between 0h and 8h.
  • T(A)1: defines bitrates between reader and emulator, and whether they can be
    asymmetric. There are no bitrate requirements or guarantees for HCE devices.
  • T(B)1: bits 1 to 4 indicate the Start-up Frame Guard time Integer (SFGI). On
    HCE devices, SFGI must be <= 8h. Bits 5 to 8 indicate the Frame Waiting time
    Integer (FWI) and codes the Frame Waiting Time (FWT). On HCE devices, FWI must
    be <= 8h.
  • T(C)1: bit 5 indicates support for “Advanced Protocol features”. HCE devices
    may or may not support “Advanced Protocol features”. Bit 2 indicates support
    for DID. HCE devices may or may not support DID. Bit 1 indicates support for
    NAD. HCE devices must not support NAD and set bit 1 to zero.
  • Historical bytes: HCE devices may return up to 15 historical bytes. NFC
    readers willing to interact with HCE services should make no assumptions about
    the contents of the historical bytes or their presence.

Note that many HCE devices are likely made compliant with protocol requirements
that the payment networks united in EMVCo have specified in their “Contactless
Communication Protocol” specification. In particular:

  • FSCI in T0 must be between 2h and 8h.
  • T(A)1 must be set to 0x80, indicating only the 106 kbit/s bitrate is
    supported, and asymmetric bitrates between reader and emulator are not
    supported.
  • FWI in T(B)1 must be <= 7h.

Nfc intent filter

There are three different filters for tags:

  1. ACTION_NDEF_DISCOVERED
  2. ACTION_TECH_DISCOVERED
  3. ACTION_TAG_DISCOVERED

The list is sorted from the highest to the lowest priority.

Now what happens when a tag is attached to the smartphone? If the system detects a tag with NDEF support, an Intent is triggered. An ACTION_TECH_DISCOVERED Intent is triggered if no Activity from any app is registered for the NDEF Intent or if the tag does not support NDEF.

In summary this means that each app needs to filter after the Intent with the highest priority. In our case, this is the NDEF Intent. We implement the ACTION_TECH_DISCOVERED Intent first to highlight the difference between priorities.

Nfc technologies

There are a variety of NFC tags that can be read with a smartphone. The spectrum ranges from simple stickers and key rings to complex cards with integrated cryptographic hardware. Tags also differ in their chip technology. The most important is NDEF, which is supported by most tags.

Only the NFC Data Exchange Format (NDEF) is discussed in this tutorial.

Off-host service invocation

Android never starts or binds to a service that is declared as “off-host,”
because the actual transactions are executed by the secure element and not by
the Android service. The service declaration merely allows applications to
register AIDs present on the secure element.

Protocol parameters and details

This section is of interest for developers that want to understand what protocol
parameters HCE devices use during the anti-collision and activation phases of
the NFC protocols. This allows building a reader infrastructure that is
compatible with Android HCE devices.

Reading data from an ndef tag

The last step is to read the data from the tag. The explanations are inserted at the appropriate places in the code once again. The NdefReaderTask is a private inner class.

Reading nfc tags and stickers in android

Figure 1 shows some miscellaneous NFC tags and stickers. They are available in many stores and vary in memory sizes and supported technologies.

Figure 1: NFC Tags / Stickers

As we mention in the previous section, NdefMessage is the most generic way of exchanging NFC data. You can still define your own non-NDEF data format but that is beyond the scope of this tutorial. To illustrate how data is parsed and handled by the tag dispatch system, we only focus on the simple plain text type in our example. For other data types, you should check into the official site for the complete listing.

NfcAdapter is used to check the device NFC support. We use the option enableForegroundDispatch to indicate the tag dispatch is handled when the app is running on the foreground. The foreground dispatch system allows an activity to intercept an intent and claim priority over other activities that handle the same intent.

In onNewIntent(), we try to parse all NDEF messages and their records. Since there are several different data types, the example only tries to parse the pain text type as defined inNdefRecord.RTD_TEXT. Figure 2 shows the parsed result by receiving the data send by the beaming example in the next section.

public class TagDispatch extends Activity {
    private TextView mTextView;
    private NfcAdapter mNfcAdapter;
    private PendingIntent mPendingIntent;
    private IntentFilter[] mIntentFilters;
    private String[][] mNFCTechLists;
 
    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
 
        setContentView(R.layout.main);
        mTextView = (TextView)findViewById(R.id.tv);
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
 
        if (mNfcAdapter != null) {
            mTextView.setText("Read an NFC tag");
        } else {
            mTextView.setText("This phone is not NFC enabled.");
        }
 
        // create an intent with tag data and deliver to this activity
        mPendingIntent = PendingIntent.getActivity(this, 0,
            new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
 
        // set an intent filter for all MIME data
        IntentFilter ndefIntent = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        try {
            ndefIntent.addDataType("*/*");
            mIntentFilters = new IntentFilter[] { ndefIntent };
        } catch (Exception e) {
            Log.e("TagDispatch", e.toString());
        }
 
        mNFCTechLists = new String[][] { new String[] { NfcF.class.getName() } };
    }
 
    @Override
    public void onNewIntent(Intent intent) {
        String action = intent.getAction();
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
 
        String s = action   "nn"   tag.toString();
 
        // parse through all NDEF messages and their records and pick text type only
        Parcelable[] data = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (data != null) {
            try {
                for (int i = 0; i < data.length; i  ) {
                    NdefRecord [] recs = ((NdefMessage)data[i]).getRecords();
                    for (int j = 0; j < recs.length; j  ) {
                        if (recs[j].getTnf() == NdefRecord.TNF_WELL_KNOWN &&
                            Arrays.equals(recs[j].getType(), NdefRecord.RTD_TEXT)) {
                            byte[] payload = recs[j].getPayload();
                            String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
                            int langCodeLen = payload[0] & 0077;
 
                            s  = ("nnNdefMessage["   i   "], NdefRecord["   j   "]:n""  
                                 new String(payload, langCodeLen   1, payload.length - langCodeLen - 1,
                                 textEncoding)   """);
                        }
                    }
                }
            } catch (Exception e) {
                Log.e("TagDispatch", e.toString());
            }
        }
 
        mTextView.setText(s);
    }
 
    @Override
    public void onResume() {
        super.onResume();
 
        if (mNfcAdapter != null)
            mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, mIntentFilters, mNFCTechLists);
    }
 
    @Override
    public void onPause() {
        super.onPause();
 
        if (mNfcAdapter != null)
            mNfcAdapter.disableForegroundDispatch(this);
    }
}

Figure 2: NFC Tag Dispatch Example

Secure element aid registration

Applications using a secure element for card emulation can declare an
off-host service in their manifest. The declaration of such a service is
almost identical to the declaration of an HCE service. The exceptions are as
follows:

Service manifest declaration and aid registration

You must declare your service in the manifest as usual, but you must add some
additional pieces to the service declaration as well:

Useful apps

To check whether data is read and written properly, I personally like to use following apps:

Sending nfc data to other devices through android beam

Sending NFC data is through the support of Android Beam, so this activity is commonly referred to as “beaming”. Many NDEF data types can be beamed and of course, you can also customize your own format. The example illustrates how you can create the records for plain text type.

public class BeamData extends Activity {
    private NfcAdapter mNfcAdapter;
    private TextView mTextView;
    private NdefMessage mNdefMessage;
 
    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
 
        setContentView(R.layout.main);
        mTextView = (TextView)findViewById(R.id.tv);
 
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
 
        if (mNfcAdapter != null) {
            mTextView.setText("Tap to beam to another NFC device");
        } else {
            mTextView.setText("This phone is not NFC enabled.");
        }
 
        // create an NDEF message with two records of plain text type
        mNdefMessage = new NdefMessage(
                       new NdefRecord[] {
                       createNewTextRecord("First sample NDEF text record", Locale.ENGLISH, true),
                       createNewTextRecord("Second sample NDEF text record", Locale.ENGLISH, true) });
    }
 
    public static NdefRecord createNewTextRecord(String text, Locale locale, boolean encodeInUtf8) {
        byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
 
        Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
        byte[] textBytes = text.getBytes(utfEncoding);
 
        int utfBit = encodeInUtf8 ? 0 : (1 << 7);
        char status = (char)(utfBit   langBytes.length);
 
        byte[] data = new byte[1   langBytes.length   textBytes.length];
        data[0] = (byte)status;
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        System.arraycopy(textBytes, 0, data, 1   langBytes.length, textBytes.length);
 
        return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
    }
 
    @Override
    public void onResume() {
        super.onResume();
 
        if (mNfcAdapter != null)
            mNfcAdapter.enableForegroundNdefPush(this, mNdefMessage);
    }
 
    @Override
    public void onPause() {
        super.onPause();
 
        if (mNfcAdapter != null)
            mNfcAdapter.disableForegroundNdefPush(this);
    }
}

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector