corvairjo corvairjo - 1 month ago 13
Android Question

How to detect the type of an NFC chip

Does anyone know how I can find out via the Android NFC API which NFC chip is used in a tag which I am reading? Ideally I like to get the both the chip model and the manufacturer.

Is that available somewhere in the Tag object?

For clarification, I don't mean the reader chip in the phone, but the chip in the NFC tag.

Answer

I'm developing an application in Android for NFC read/write/authentication operations. Here are some code parts you would like.

Android has 3 main types determined for cards, those are Mifare Classic, Mifare Ultralight and Isodep (this is the type of Desfire and Desfire EV1). So, as I get a tag touched, I run this operation:

private String[] getTagInfo(Intent intent) {
    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    String prefix = "android.nfc.tech.";
    String[] info = new String[2];

    // UID
    byte[] uid = tag.getId();
    info[0] = "UID In Hex: " + Utils.convertByteArrayToHexString(uid) + "\n" +
              "UID In Dec: " + Utils.convertByteArrayToDecimal(uid) + "\n\n";

    // Tech List
    String[] techList = tag.getTechList();
    String techListConcat = "Technologies: ";
    for(int i = 0; i < techList.length; i++) {
        techListConcat += techList[i].substring(prefix.length()) + ",";
    }
    info[0] += techListConcat.substring(0, techListConcat.length() - 1) + "\n\n";

    // Mifare Classic/UltraLight Info
    info[0] += "Card Type: ";
    String type = "Unknown";
    for(int i = 0; i < techList.length; i++) {
        if(techList[i].equals(MifareClassic.class.getName())) {
            info[1] = "Mifare Classic";
            MifareClassic mifareClassicTag = MifareClassic.get(tag);

            // Type Info
            switch (mifareClassicTag.getType()) {
            case MifareClassic.TYPE_CLASSIC:
                type = "Classic";
                break;
            case MifareClassic.TYPE_PLUS:
                type = "Plus";
                break;
            case MifareClassic.TYPE_PRO:
                type = "Pro";
                break;
            }
            info[0] += "Mifare " + type + "\n";

            // Size Info
            info[0] += "Size: " + mifareClassicTag.getSize() + " bytes \n" +
                    "Sector Count: " + mifareClassicTag.getSectorCount() + "\n" +
                    "Block Count: " + mifareClassicTag.getBlockCount() + "\n";
        } else if(techList[i].equals(MifareUltralight.class.getName())) {
            info[1] = "Mifare UltraLight";
            MifareUltralight mifareUlTag = MifareUltralight.get(tag);

            // Type Info
            switch (mifareUlTag.getType()) {
            case MifareUltralight.TYPE_ULTRALIGHT:
                type = "Ultralight";
                break;
            case MifareUltralight.TYPE_ULTRALIGHT_C:
                type = "Ultralight C";
                break;
            }
            info[0] += "Mifare " + type + "\n";
        } else if(techList[i].equals(IsoDep.class.getName())) {
            info[1] = "IsoDep";
            IsoDep isoDepTag = IsoDep.get(tag);
            info[0] += "IsoDep \n";
        } else if(techList[i].equals(Ndef.class.getName())) {
            Ndef ndefTag = Ndef.get(tag);
            info[0] += "Is Writable: " + ndefTag.isWritable() + "\n" +
                    "Can Make ReadOnly: " + ndefTag.canMakeReadOnly() + "\n";
        } else if(techList[i].equals(NdefFormatable.class.getName())) {
            NdefFormatable ndefFormatableTag = NdefFormatable.get(tag);
        }
    } 

    return info;
}

However, this doesn't directly get the type of Desfire and Desfire EV1. For that you need to send some bytes to card:

static final byte GET_MANUFACTURING_DATA = (byte) 0x60;

public DesfireManufacturingData getManufacturingData() throws Exception {
    byte[] respBuffer = sendRequest(GET_MANUFACTURING_DATA);
    if (respBuffer.length != 28)
        throw new Exception("Invalid response");
    return new DesfireManufacturingData(respBuffer);
}

private byte[] sendRequest (byte command) throws Exception {
    return sendRequest(command, null);
}

private byte[] sendRequest (byte command, byte[] parameters) throws Exception {
    ByteArrayOutputStream output = new ByteArrayOutputStream();

    byte[] recvBuffer = mTagTech.transceive(Utils.wrapMessage(command, parameters));

    while (true) {
        if (recvBuffer[recvBuffer.length - 2] != (byte) 0x91)
            throw new Exception("Invalid response");

        output.write(recvBuffer, 0, recvBuffer.length - 2);

        byte status = recvBuffer[recvBuffer.length - 1];
        if (status == OPERATION_OK) {
            break;
        } else if (status == ADDITIONAL_FRAME) {
            recvBuffer = mTagTech.transceive(Utils.wrapMessage(GET_ADDITIONAL_FRAME, null));


        } else if (status == PERMISSION_DENIED) {
            throw new Exception("Permission denied");
        }else if (status == LENGTH_ERROR) {
            throw new Exception("Length Error");
        } 
        else if (status == AUTHENTICATION_ERROR) {
            throw new Exception("Authentication error");
        }else if (status == PARAMETER_ERROR) {
            throw new Exception("Parameter Error");
        }else if (status == DUPLICATE_ERROR) {
            throw new Exception("Duplicate Error");
        }else if (status == NO_SUCH_KEY) {
            throw new Exception("No such key");
        }else {
            throw new Exception("Unknown status code: " + Integer.toHexString(status & 0xFF));
        }
    }

    return output.toByteArray();
}

After you initialize the Manufacturing data, you can easily reach its parts. DesfireManufacturingData class is for evaluating the response from the tag into meaningful parts, but I'll only give link of it: Desfire Manufacturing Data. Also, I must say this is the most all-around project I have found in the Internet with open source, but it only has reading operations for Desfire tags, not write and authenticate. Hope this helps!