Summary of Android NFC function usage

Posted by snrecords on Mon, 31 Jan 2022 22:12:25 +0100

Recently, I sorted out and took over the project code, which is useful for NFC function. I took a time to understand it today. It should not be difficult to use it simply. I don't know about advanced extensions. Here's a summary!

Knowledge understanding

I saw a blog about NFC function on the Internet, which is divided into theory and practice. The theory is to roughly understand the function of NFC. After reading it, I think I have a very detailed concept, but his article is a little messy. After looking at the official documents, I found that the official documents are also good, and there is a Chinese version. In fact, I should look at the official documents first:

https://developer.android.google.cn/guide/topics/connectivity/nfc

Here is the reference blog:

There are too many things. In fact, we only need to master how to use them. There are two ways to explain below.

Foreground use

1. Configure permissions

Configure androidmenifest xml:

<!--NFC jurisdiction-->
<uses-permission android:name="android.permission.NFC" />
<!-- It is required that the current equipment must have NFC chip -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
2. Get NfcAdapter object
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate();
        mNfcAdapter= NfcAdapter.getDefaultAdapter(this);//NfcAdapter object for the device
        if(mNfcAdapter==null){//Judge whether the device supports NFC function
            Toast.makeText(this,"Device does not support NFC function!",Toast.LENGTH_SHORT);
            finish();
            return;
        }
        if (!mNfcAdapter.isEnabled()){//Judge whether the device NFC function is on
            Toast.makeText(this,"Please open it in system settings NFC function!",Toast.LENGTH_SHORT);
            finish();
            return;
        }
        mPendingIntent=PendingIntent.getActivity(this,0,new Intent(this,getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0);//When an Intent Tag object is detected, an Intent Tag will be set_ Top flag, open only the current activity
    }

Write it here in onCreate!

3. Turn on / off the foreground publishing system
	//Page get focus
	@Override
    protected void onResume() {
        super.onResume();
        if (mNfcAdapter!=null){          mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,null,null);//Open the foreground publishing system to make the page better than other nfc processing When a Tag tag is detected, mpendingtent will be executed
        }
    }

	//Page lost focus
	@Override
    protected void onPause() {
        super.onPause();
        if(mNfcAdapter!=null){
            mNfcAdapter.disableForegroundDispatch(this);//Close the foreground publishing system
        }
    }

It is reasonable to use NFC only when the current page is displayed.

4. Get TAG object
	@Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        mTag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);//Get Tag tag object
        String[] techList=mTag.getTechList();
        System.out.println("Label supported tachnology Type:");
        for (String tech:techList){
            System.out.println(tech);
        }
    }

Because the set SINGLE_TOP flag, so get TAG in onNewIntent. The type is TAG.

Parsing TAG data

Here we need to see the technical standards we support. See the following figure:

ClassDescription
TagTechnologyThis interface must be implemented by all tag technology classes below.
NfcASupport the operation of ISO 14443-3a standard. Provides access to NFC-A (ISO 14443-3A) properties and I/O operations.
NfcBProvides access to NFC-B (ISO 14443-3B) properties and I/O operations.
NfcFProvides access to NFC-F (JIS 6319-4) properties and I/O operations.
NfcVProvides access to NFC-V (ISO 15693) properties and I/O operations.
IsoDepProvides access to ISO-DEP (ISO 14443-4) properties and I/O operations.
NdefProvides access to data and other operations for tags formatted as nDef. Provides access to NDEF data and operations on NFC tags that have been formatted as NDEF.
NdefFormatableProvide a formatting operation for tag s that can be formatted into NDEF format
MifareClassicIf the android device supports MIFARE, provide properties and I/O operations for MIFARE Classic targets.
MifareUltralightIf the android device supports MIFARE, it provides properties and I/O operations for the MIFARE Ultralight target.

You can get the technical type of the Tag through getTechList() of the Tag object. Write an NDEF format reading below:

//Read data in Ndef tag
    private void readNdef(){
        if (mTag==null){
            Toast.makeText(this,"Unrecognized label type!",Toast.LENGTH_SHORT);
            finish();
            return;
        }
        Ndef ndef=Ndef.get(mTag);//Get ndef object
        try {
            ndef.connect();//connect
            NdefMessage ndefMessage=ndef.getNdefMessage();//Get NdefMessage object
            if (ndefMessage!=null)               readEdt.setText(parseTextRecord(ndefMessage.getRecords()[0]));
            Toast.makeText(this,"Data reading succeeded!",Toast.LENGTH_SHORT);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (FormatException e) {
            e.printStackTrace();
        }finally {
            try {
                ndef.close();//Close link
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Parse NDEF text data, starting from the third byte and the following text data
     * @param ndefRecord
     * @return
     */
    public static String parseTextRecord(NdefRecord ndefRecord) {
        /**
         * Judge whether the data is in NDEF format
         */
        //Judge TNF
        if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
            return null;
        }
        //Judge the type of variable length
        if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
            return null;
        }
        try {
            //Get the byte array and analyze it
            byte[] payload = ndefRecord.getPayload();
            //Next, start the first byte and status byte of NDEF text data
            //Judge whether the text is based on UTF-8 or UTF-16, take the hexadecimal 80 on the "bit and" of the first byte, and the hexadecimal 80, that is, the highest bit is 1,
            //Other bits are 0, so the highest bit will be retained after the "bit and" operation
            String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
            //3f the highest two bits are 0 and the sixth bit is 1, so the sixth bit is obtained after bit and operation
            int languageCodeLength = payload[0] & 0x3f;
            //Next, start the second byte of NDEF text data, language coding
            //Get language code
            String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
            //Next, start the byte after NDEF text data and parse the text
            String textRecord = new String(payload, languageCodeLength + 1,
                    payload.length - languageCodeLength - 1, textEncoding);
            return textRecord;
        } catch (Exception e) {
            throw new IllegalArgumentException();
        }
    }

If you want to use it in detail, you can look at the official documents, which should get the data.

Use in Intent form

The intent form is to match the intent implementation of NFC through the intent filter to obtain data. The process is similar to the above:

1. Permission configuration

ditto

2,AndroidManifest.xml related configuration
<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.nfc.action.TAG_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.nfc.action.TECH_DISCOVERED" />
    </intent-filter>
    <meta-data
        android:name="android.nfc.action.TECH_DISCOVERED"
        android:resource="@xml/nfc_tech_filter" />
</activity>

Here are three NCF related intent filters and specify our technologies list.

3. Configure technologies list
<resources>
    <!-- Can handle all Android Supportive NFC type -->
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

Because tech is used in our intent filter_ Discovered, which requires us to specify the list of technologies to be supported in the resource file.

4. Get NfcAdapter object
// 1. Initialize NFC adapter
private void initNfc() {
    // Get the system default NFC adapter
    mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
    if (mNfcAdapter == null) {
        tv_nfc_result.setText("The current mobile phone does not support NFC");
    } else if (!mNfcAdapter.isEnabled()) {
        tv_nfc_result.setText("Please enable it in system settings first NFC function");
    } else {
        tv_nfc_result.setText("Current mobile phone support NFC");
    }
}

As above, obtain the nfadapter object in the onCreate method.

5. Check NFC Intent startup
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        ...
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] rawMessages =
                intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMessages != null) {
                NdefMessage[] messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i++) {
                    messages[i] = (NdefMessage) rawMessages[i];
                }
                // Process the messages array.
                ...
            }
        }
    }

Intent can contain the following extra, depending on the scanned label:

  • EXTRA_TAG (required): a tag object that represents the scanned tag.
  • EXTRA_NDEF_MESSAGES (optional): a set of nDef messages parsed from the tag. This extra is for action_ NDEF_ Required for discovered intent.
  • EXTRA_ID (optional): the low-level ID of the label.

The code of the official website taken here can be opened through intent filter. The activity may be in onCreate or onNewIntent. At the same time, it is necessary to judge the action.

6. Get TAG object
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

Here, you can get the TAG object in Intent.

7. Parsing TAG data

Here's another one. Write about the MifareClassic class.

 	// Read the information of community access card
    public String readGuardCard(Tag tag) {
        MifareClassic classic = MifareClassic.get(tag);
        for (String tech : tag.getTechList()) {
            Log.d(TAG, "Current device support" + tech); //Display device support technology
        }
        String info;
        try {
            classic.connect(); // Connect card data
            int type = classic.getType(); //Gets the type of TAG
            String typeDesc;
            if (type == MifareClassic.TYPE_CLASSIC) {
                typeDesc = "Traditional type";
            } else if (type == MifareClassic.TYPE_PLUS) {
                typeDesc = "Enhancement type";
            } else if (type == MifareClassic.TYPE_PRO) {
                typeDesc = "Professional type";
            } else {
                typeDesc = "unknown type";
            }
            info = String.format("\t Card type:%s\n\t Number of sectors:%d\n\t Number of blocks:%d\n\t Storage space:%d byte",
                    typeDesc, classic.getSectorCount(), classic.getBlockCount(), classic.getSize());
        } catch (Exception e) {
            e.printStackTrace();
            info = e.getMessage();
        } finally { // No matter whether an exception occurs or not, the resource should be released
            try {
                classic.close(); // Release card data
            } catch (Exception e) {
                e.printStackTrace();
                info = e.getMessage();
            }
        }
        return info;
    }
 
    public static String ByteArrayToHexString(byte[] bytesId) {
        int i, j, in;
        String[] hex = {
                "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"
        };
        String output = "";
 
        for (j = 0; j < bytesId.length; ++j) {
            in = bytesId[j] & 0xff;
            i = (in >> 4) & 0x0f;
            output += hex[i];
            i = in & 0x0f;
            output += hex[i];
        }
        return output;
    }

The following is the method description of MifareClassic class:

  • Get: get the information of the card object from the Tag object. This method is static.
  • connect: Link card data.
  • close: release card data.
  • getType: get the type of card. TYPE_CLASSIC stands for traditional type, TYPE_PLUS indicates the type of enhancement,
  • TYPE_PRO indicates the professional type.
  • getSectorCount: get the number of sectors of the card.
  • getBlockCount: get the number of blocks of the card.
  • getSize: get the storage space size of the card, in bytes.

epilogue

The official documents are recommended here. The contents should be complete. The above two methods should be able to obtain data. Of course, they are only simple to use, and the complex ones have to be studied by yourself.

end

Topics: Android