de.berlin.magun.nfcmime.core.RfidDAO.java Source code

Java tutorial

Introduction

Here is the source code for de.berlin.magun.nfcmime.core.RfidDAO.java

Source

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.berlin.magun.nfcmime.core;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;

import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.nfc.tech.NfcV;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.ArrayUtils;
import org.apache.http.util.EncodingUtils;

/**
 * Data access object class that contains various read/write functions for RFID
 * transponders
 * @author Johannes Bolz
 */
public class RfidDAO {

    private NdefRecord[] records;

    /** 
     * @param tag Tag
     * @return a hexadecimal String representation of a Tag's ID or UID
     */
    public String getTagId(Tag tag) {

        // get the ID byte array
        byte[] id = tag.getId();

        if (NfcV.get(tag) != null) {
            ArrayUtils.reverse(id);
        }

        return new String(Hex.encodeHex(id));
    }

    /**
     * @param tag Tag
     * @return true, if the Tag is NDEF formatted 
     */
    public boolean isNdef(Tag tag) {
        return (Ndef.get(tag) != null);
    }

    /**
     * @param tag Tag
     * @return true, if the Tag is NDEF formatable.
     */
    public boolean isNdefFormatable(Tag tag) {
        return (NdefFormatable.get(tag) != null);
    }

    /**
     * @param tag Tag
     * @return an ArrayList of NdefMimeRecord objects out of the Tag. Performs a read operation 
     * on the NFC adapter.
     * @throws IOException
     * @throws FormatException
     */
    public ArrayList<NdefMimeRecord> getMimeRecords(Tag tag) {
        readRecords(tag);
        return extractMimeRecords(this.records);
    }

    /**
     * Reads URI records from NDEF records
     * @param tag
     * @return an ArrayList of URIs, represented as Strings.
     */
    public ArrayList<String> getUriRecords(Tag tag) {
        readRecords(tag);
        if (this.records != null) {
            ArrayList<String> uriStrings = new ArrayList<String>();
            for (int i = 0; i < this.records.length; i++) {
                if (this.records[i].getTnf() == NdefRecord.TNF_WELL_KNOWN
                        && Arrays.equals(this.records[i].getType(), NdefRecord.RTD_URI)) {
                    uriStrings.add(EncodingUtils.getString(ArrayUtils.remove(records[i].getPayload(), 0), "UTF-8"));
                }
            }
            return uriStrings;
        } else {
            return null;
        }
    }

    /**
     * Checks all NDEF records against the CRC value contained in the last record.
     * @return true, if the checksum is correct.
     */
    public boolean verifyMimeRecords() {
        CrcGenerator generator = new CrcGenerator();
        NdefRecord checksumRecord = this.records[this.records.length - 1];
        NdefRecord[] payloadRecords = (NdefRecord[]) ArrayUtils.remove(this.records, this.records.length - 1);
        if (checksumRecord.getTnf() == NdefRecord.TNF_MIME_MEDIA
                && EncodingUtils.getString(checksumRecord.getPayload(), "UTF-8").startsWith("crc32:")) {
            String checksumStr = EncodingUtils.getString(checksumRecord.getPayload(), "UTF--8");
            long checksum = Long.parseLong(checksumStr.substring(checksumStr.indexOf("crc32:") + 6));
            return generator.checkHash(payloadRecords, checksum);
        } else {
            return false;
        }
    }

    /**
     * @param tag Tag
     * @return an ArrayList of cached NdefMimeRecords, i.e. doesn't perform a read operation
     * on the NFC adapter. Will return null if the cache doesn't hold a Ndef object.
     */
    public ArrayList<NdefMimeRecord> getCachedMimeRecords(Tag tag) {
        NdefSingleton.getInstance().setNdef(Ndef.get(tag));
        if (NdefSingleton.getInstance().getNdef() != null) {
            if (NdefSingleton.getInstance().getNdef().getCachedNdefMessage() != null) {
                this.records = NdefSingleton.getInstance().getNdef().getCachedNdefMessage().getRecords();
                return extractMimeRecords(this.records);
            }
        }
        return null;
    }

    /**
     * @param records Ndefrecord[] array
     * @return an ArrayList of the NdefRecords within records. Will only put MIME formatted
     * NdefRecords in the ArrayList. Returns null if the array is null.
     */
    private ArrayList<NdefMimeRecord> extractMimeRecords(NdefRecord[] records) {
        if (records == null) {
            return null; // TODO Give more specific feedback?
        }
        ArrayList<NdefMimeRecord> recordlist = new ArrayList<NdefMimeRecord>();
        for (int i = 0; i < records.length; i++) {
            // check if NdefRecord is MIME formatted
            if (records[i].getTnf() == NdefRecord.TNF_MIME_MEDIA) {
                recordlist.add(new NdefMimeRecord(records[i].getPayload(), records[i].getType()));
            }
        }
        return recordlist;
    }

    /**
     * Stores a NDEF into the {@link NdefMessageSingleton}.
     * @param tag
     */
    private void readRecords(Tag tag) {
        NdefSingleton.getInstance().setNdef(Ndef.get(tag));
        // Create timestamp for NDEF message.
        long timestamp = (new Date()).getTime();
        // Create ReadTask, which will the message into a Map in NdefMessageSingleton, identified by timestamp.
        ReadTask rt = new ReadTask(timestamp);
        IOThread readThread = new IOThread();
        readThread.run(rt);

        // Retrieve NDEF message out of NdefMessageSingleton by it's timestamp ID, extract records.
        NdefMessage m = NdefMessageSingleton.getInstance().getMessage(timestamp);
        if (m != null) {
            this.records = NdefMessageSingleton.getInstance().getMessage(timestamp).getRecords();
        } else {
            this.records = null;
        }
    }

    /**
     * Writes an NDEF message on a NFC-compliant RFID transponder.
     * @param tag Tag
     * @param message the NdefMessage to write
     * @throws Exception
     */
    public void writeMessage(Tag tag, NdefMessage message) throws Exception {

        // use NdefSingleton for I/O operations (which is also accessed by the separate Thread)
        NdefSingleton.getInstance().setNdef(Ndef.get(tag));

        // make sure the tag can be written to
        if (!NdefSingleton.getInstance().getNdef().isWritable()) {
            throw new Exception("Tag is read-only!"); // TODO Create custom Exception for that purpose
        }

        // make sure the NDEF message is neither null, nor larger than the available tag memory
        if (message != null) {
            if (message.toByteArray().length > NdefSingleton.getInstance().getNdef().getMaxSize()) {
                throw new Exception("NDEF message too long!"); // TODO Create custom Exception for that purpose
            }
        } else {
            throw new NullPointerException();
        }

        // create and run a IOThread that writes the message
        WriteTask wt = new WriteTask(message);
        IOThread writeThread = new IOThread();
        writeThread.run(wt);

    }

    /**
     * Formats an NDEF formatable RFID transponder, so NDEF messages can be written to it.
     * @param tag Tag
     */
    public void formatNdef(Tag tag) {
        // check if Tag is NDEF formattable. If it is, NDEF-format it
        if (NdefFormatable.get(tag) != null) {
            // use NdefSingleton for I/O operations
            NdefSingleton.getInstance().setNdefFormatable(NdefFormatable.get(tag));
            FormatThread ft = new FormatThread();
            ft.run();
        }
    }

}