com.codebutler.farebot.card.felica.FelicaCard.java Source code

Java tutorial

Introduction

Here is the source code for com.codebutler.farebot.card.felica.FelicaCard.java

Source

/*
 * FelicaCard.java
 *
 * Copyright (C) 2011 Eric Butler
 *
 * Authors:
 * Eric Butler <eric@codebutler.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.codebutler.farebot.card.felica;

import android.nfc.Tag;
import android.os.Parcel;
import android.util.Base64;
import android.util.Log;
import com.codebutler.farebot.Utils;
import com.codebutler.farebot.card.Card;
import com.codebutler.farebot.transit.SuicaTransitData;
import com.codebutler.farebot.transit.TransitData;
import com.codebutler.farebot.transit.TransitIdentity;
import net.kazzz.felica.FeliCaTag;
import net.kazzz.felica.command.ReadResponse;
import net.kazzz.felica.lib.FeliCaLib;
import org.apache.commons.lang.ArrayUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class FelicaCard extends Card {
    private FeliCaLib.IDm mIDm;
    private FeliCaLib.PMm mPMm;
    private FelicaSystem[] mSystems;

    public static Creator<FelicaCard> CREATOR = new Creator<FelicaCard>() {
        public FelicaCard createFromParcel(Parcel source) {
            int tagIdLength = source.readInt();
            byte[] tagId = new byte[tagIdLength];
            source.readByteArray(tagId);

            Date scannedAt = new Date(source.readLong());

            FeliCaLib.IDm idm = source.readParcelable(FeliCaLib.IDm.class.getClassLoader());
            FeliCaLib.PMm pmm = source.readParcelable(FeliCaLib.PMm.class.getClassLoader());

            FelicaSystem[] systems = new FelicaSystem[source.readInt()];
            source.readTypedArray(systems, FelicaSystem.CREATOR);

            return new FelicaCard(tagId, scannedAt, idm, pmm, systems);
        }

        public FelicaCard[] newArray(int size) {
            return new FelicaCard[size];
        }
    };

    // https://github.com/tmurakam/felicalib/blob/master/src/dump/dump.c
    // https://github.com/tmurakam/felica2money/blob/master/src/card/Suica.cs
    public static FelicaCard dumpTag(byte[] tagId, Tag tag) throws Exception {
        FeliCaTag ft = new FeliCaTag(tag);

        FeliCaLib.IDm idm = ft.pollingAndGetIDm(FeliCaLib.SYSTEMCODE_ANY);
        FeliCaLib.PMm pmm = ft.getPMm();

        if (idm == null)
            throw new Exception("Failed to read IDm");

        List<FelicaSystem> systems = new ArrayList<FelicaSystem>();

        // FIXME: Enumerate "areas" inside of systems ???

        for (FeliCaLib.SystemCode code : ft.getSystemCodeList()) {
            Log.d("FelicaCard", "Got system code: " + Utils.getHexString(code.getBytes()));

            int systemCode = Utils.byteArrayToInt(code.getBytes());
            //ft.polling(systemCode);

            FeliCaLib.IDm this_idm = ft.pollingAndGetIDm(systemCode);

            Log.d("FelicaCard", " - Got IDm: " + Utils.getHexString(this_idm.getBytes()) + "  compare: "
                    + Utils.getHexString(idm.getBytes()));

            byte[] foo = idm.getBytes();
            ArrayUtils.reverse(foo);
            Log.d("FelicaCard", " - Got Card ID? " + Utils.byteArrayToInt(idm.getBytes(), 2, 6) + "  "
                    + Utils.byteArrayToInt(foo, 2, 6));

            Log.d("FelicaCard", " - Got PMm: " + Utils.getHexString(ft.getPMm().getBytes()) + "  compare: "
                    + Utils.getHexString(pmm.getBytes()));

            List<FelicaService> services = new ArrayList<FelicaService>();

            for (FeliCaLib.ServiceCode serviceCode : ft.getServiceCodeList()) {
                // There appears to be some disagreement over byte order.
                byte[] bytes = serviceCode.getBytes();
                ArrayUtils.reverse(bytes);
                int serviceCodeInt = Utils.byteArrayToInt(bytes);
                serviceCode = new FeliCaLib.ServiceCode(serviceCodeInt);

                List<FelicaBlock> blocks = new ArrayList<FelicaBlock>();

                ft.polling(systemCode);

                byte addr = 0;
                ReadResponse result = ft.readWithoutEncryption(serviceCode, addr);
                while (result != null && result.getStatusFlag1() == 0) {
                    blocks.add(new FelicaBlock(addr, result.getBlockData()));
                    addr++;
                    result = ft.readWithoutEncryption(serviceCode, addr);
                }

                if (blocks.size() > 0) { // Most service codes appear to be empty...
                    FelicaBlock[] blocksArray = blocks.toArray(new FelicaBlock[blocks.size()]);
                    services.add(new FelicaService(serviceCodeInt, blocksArray));
                }
            }

            FelicaService[] servicesArray = services.toArray(new FelicaService[services.size()]);
            systems.add(new FelicaSystem(Utils.byteArrayToInt(code.getBytes()), servicesArray));
        }

        FelicaSystem[] systemsArray = systems.toArray(new FelicaSystem[systems.size()]);
        return new FelicaCard(tagId, new Date(), idm, pmm, systemsArray);
    }

    public FelicaCard(byte[] tagId, Date scannedAt, FeliCaLib.IDm idm, FeliCaLib.PMm pmm, FelicaSystem[] systems) {
        super(tagId, scannedAt);
        mIDm = idm;
        mPMm = pmm;
        mSystems = systems;
    }

    public FeliCaLib.IDm getIDm() {
        return mIDm;
    }

    public FeliCaLib.PMm getPMm() {
        return mPMm;
    }

    // FIXME: Getters that parse IDm...

    // date ????
    /*
    public int getManufactureCode() {
        
    }
        
    public int getCardIdentification() {
        
    }
        
    public int getROMType() {
        
    }
        
    public int getICType() {
        
    }
        
    public int getTimeout() {
        
    }
    */

    public FelicaSystem[] getSystems() {
        return mSystems;
    }

    public FelicaSystem getSystem(int systemCode) {
        for (FelicaSystem system : mSystems) {
            if (system.getCode() == systemCode) {
                return system;
            }
        }
        return null;
    }

    public static FelicaCard fromXml(byte[] tagId, Date scannedAt, Element element) {
        Element systemsElement = (Element) element.getElementsByTagName("systems").item(0);

        NodeList systemElements = systemsElement.getElementsByTagName("system");

        FeliCaLib.IDm idm = new FeliCaLib.IDm(
                Base64.decode(element.getElementsByTagName("idm").item(0).getTextContent(), Base64.DEFAULT));
        FeliCaLib.PMm pmm = new FeliCaLib.PMm(
                Base64.decode(element.getElementsByTagName("pmm").item(0).getTextContent(), Base64.DEFAULT));

        FelicaSystem[] systems = new FelicaSystem[systemElements.getLength()];

        for (int x = 0; x < systemElements.getLength(); x++) {
            Element systemElement = (Element) systemElements.item(x);

            int systemCode = Integer.parseInt(systemElement.getAttribute("code"));

            Element servicesElement = (Element) systemElement.getElementsByTagName("services").item(0);

            NodeList serviceElements = servicesElement.getElementsByTagName("service");

            FelicaService[] services = new FelicaService[serviceElements.getLength()];

            for (int y = 0; y < serviceElements.getLength(); y++) {
                Element serviceElement = (Element) serviceElements.item(y);
                int serviceCode = Integer.parseInt(serviceElement.getAttribute("code"));

                Element blocksElement = (Element) serviceElement.getElementsByTagName("blocks").item(0);

                NodeList blockElements = blocksElement.getElementsByTagName("block");

                FelicaBlock[] blocks = new FelicaBlock[blockElements.getLength()];

                for (int z = 0; z < blockElements.getLength(); z++) {
                    Element blockElement = (Element) blockElements.item(z);
                    byte address = Byte.parseByte(blockElement.getAttribute("address"));
                    byte[] data = Base64.decode(blockElement.getTextContent(), Base64.DEFAULT);

                    blocks[z] = new FelicaBlock(address, data);
                }

                services[y] = new FelicaService(serviceCode, blocks);
            }

            systems[x] = new FelicaSystem(systemCode, services);
        }

        return new FelicaCard(tagId, scannedAt, idm, pmm, systems);
    }

    @Override
    public CardType getCardType() {
        return CardType.FeliCa;
    }

    @Override
    public TransitIdentity parseTransitIdentity() {
        if (SuicaTransitData.check(this))
            return SuicaTransitData.parseTransitIdentity(this);
        return null;
    }

    @Override
    public TransitData parseTransitData() {
        Log.d("FelicaCard", "parseTransitData() called!!");
        if (SuicaTransitData.check(this))
            return new SuicaTransitData(this);
        return null;
    }

    @Override
    public Element toXML() throws Exception {
        Element root = super.toXML();

        Document doc = root.getOwnerDocument();

        Element idmElement = doc.createElement("idm");
        idmElement.setTextContent(Base64.encodeToString(mIDm.getBytes(), Base64.DEFAULT));
        root.appendChild(idmElement);

        Element pmmElement = doc.createElement("pmm");
        pmmElement.setTextContent(Base64.encodeToString(mPMm.getBytes(), Base64.DEFAULT));
        root.appendChild(pmmElement);

        Element systemsElement = doc.createElement("systems");

        for (FelicaSystem system : mSystems) {
            Element systemElement = doc.createElement("system");
            systemElement.setAttribute("code", String.valueOf(system.getCode()));

            Element servicesElement = doc.createElement("services");
            for (FelicaService service : system.getServices()) {
                Element serviceElement = doc.createElement("service");
                serviceElement.setAttribute("code", String.valueOf(service.getServiceCode()));

                Element blocksElement = doc.createElement("blocks");
                for (FelicaBlock block : service.getBlocks()) {
                    Element blockElement = doc.createElement("block");
                    blockElement.setAttribute("address", String.valueOf(block.getAddress()));
                    blockElement.setTextContent(Base64.encodeToString(block.getData(), Base64.DEFAULT));

                    blocksElement.appendChild(blockElement);
                }

                serviceElement.appendChild(blocksElement);

                servicesElement.appendChild(serviceElement);
            }

            systemElement.appendChild(servicesElement);

            systemsElement.appendChild(systemElement);
        }

        root.appendChild(systemsElement);

        return root;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        super.writeToParcel(parcel, flags);
        parcel.writeParcelable(mIDm, flags);
        parcel.writeParcelable(mPMm, flags);
        parcel.writeInt(mSystems.length);
        parcel.writeTypedArray(mSystems, flags);
    }
}