Search code examples
androidnfcrfidmifare

How to send 7 bit commands to M1 card by NFC on Android?


I'm having trouble using NFC on Android. I want to send some data to M1 card. I can send commands using the transceive() method. But the transceive() method has a byte[] argument, and a byte is 8 bit. I want to send 0b1000000 (short frame) to my M1 card. How send that to M1 card?

 /**
 * Send raw NfcA data to a tag and receive the response.
 *
 * <p>This is equivalent to connecting to this tag via {@link NfcA}
 * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
 * tags are based on {@link NfcA} technology.
 *
 * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
 * that can be sent with {@link #transceive}.
 *
 * <p>This is an I/O operation and will block until complete. It must
 * not be called from the main application thread. A blocked call will be canceled with
 * {@link IOException} if {@link #close} is called from another thread.
 *
 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
 *
 * @see NfcA#transceive
 */
public byte[] transceive(byte[] data) throws IOException {
    return transceive(data, true);
}

My code is:

 /**
 * Write a block of 16 byte data to tag.
 * @param sectorIndex The sector to where the data should be written
 * @param blockIndex The block to where the data should be written
 * @param data 16 byte of data.
 * @param key The MIFARE Classic key for the given sector.
 * @param useAsKeyB If true, key will be treated as key B
 * for authentication.
 * @return The return codes are:<br />
 * <ul>
 * <li>0 - Everything went fine.</li>
 * <li>1 - Sector index is out of range.</li>
 * <li>2 - Block index is out of range.</li>
 * <li>3 - Data are not 16 bytes.</li>
 * <li>4 - Authentication went wrong.</li>
 * <li>-1 - Error while writing to tag.</li>
 * </ul>
 * @see #authenticate(int, byte[], boolean)
 */
public int writeBlock(int sectorIndex, int blockIndex, byte[] data,
        byte[] key, boolean useAsKeyB) {
    if (getSectorCount()-1 < sectorIndex) {
        return 1;
    }
    if (mMFC.getBlockCountInSector(sectorIndex)-1 < blockIndex) {
        return 2;
    }
    if (data.length != 16) {
        return 3;
    }
    int block = mMFC.sectorToBlock(sectorIndex) + blockIndex;

    // write chinese card
    if (block == 0) {
        // Write block.
        try {
            mMFC.transceive(new byte[]{(byte)0x50, (byte)0x00, (byte)0x57, (byte)0xCD});
            mMFC.transceive(new byte[]{(byte)0x40}); // TODO: here need send 0b1000000(7 bit) , not 0b01000000(8 bit)
            mMFC.transceive(new byte[]{(byte)0x43});
            mMFC.writeBlock(block, data);
        } catch (IOException e) {
            Log.e(LOG_TAG, "Error while writing block to tag.", e);
            return -1;
        }
        return 0;
    }

    if (!authenticate(sectorIndex, key, useAsKeyB)) {
        return 4;
    }
    // Write block.
    try {
        mMFC.writeBlock(block, data);
    } catch (IOException e) {
        Log.e(LOG_TAG, "Error while writing block to tag.", e);
        return -1;
    }
    return 0;
}

Solution

  • What you are trying to do is simply not possible on Android. The Android NFC API does not provide any method to send short frames (7 bit frame as defined in ISO/IEC 14443-3 Type A) directly.

    In fact, the transceive() method of NfcA does not only send the byte array that is passed as its argument. Instead, it will also cause the CRC checksum to be automatically appended to the frame. As a consequence, you can only exchange normal frames (as defined in ISO/IEC 14443-3 Type A) using the transceive() method.

    Since you are using MIFARE Classic (or rather some UID-changable clones), you will experience even further limitations on Android devices that support MIFARE Classic (i.e. devices with an NFC chipset manufactured by NXP): If a tag is detected as MIFARE Classic, the NFC controller will be switched into a special mode where it interprets high-level versions of the MIFARE Classic commands and automatically translates them to their low-level (and potentially encrypted) versions. This is necessary since MIFARE Classic does not fully follow the framing rules of ISO/IEC 14443-3 Type A. Unfortunately, this also typically prevents you from sending any raw frames directly to these tags at all.