migrated all farebot code to kotlin
This commit is contained in:
		@ -1,268 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Utils.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;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.AlertDialog;
 | 
			
		||||
import android.content.DialogInterface;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.view.WindowManager;
 | 
			
		||||
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireException;
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireFileSettings;
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireProtocol;
 | 
			
		||||
 | 
			
		||||
import org.w3c.dom.Node;
 | 
			
		||||
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import javax.xml.transform.OutputKeys;
 | 
			
		||||
import javax.xml.transform.Result;
 | 
			
		||||
import javax.xml.transform.Source;
 | 
			
		||||
import javax.xml.transform.Transformer;
 | 
			
		||||
import javax.xml.transform.TransformerFactory;
 | 
			
		||||
import javax.xml.transform.dom.DOMSource;
 | 
			
		||||
import javax.xml.transform.stream.StreamResult;
 | 
			
		||||
 | 
			
		||||
public class Utils {
 | 
			
		||||
 | 
			
		||||
	private static final String TAG = Utils.class.getName();
 | 
			
		||||
 | 
			
		||||
    public static void showError (final Activity activity, Exception ex) {
 | 
			
		||||
        Log.e(activity.getClass().getName(), ex.getMessage(), ex);
 | 
			
		||||
        new AlertDialog.Builder(activity)
 | 
			
		||||
            .setMessage(Utils.getErrorMessage(ex))
 | 
			
		||||
            .show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void showErrorAndFinish (final Activity activity, Exception ex) {
 | 
			
		||||
        try {
 | 
			
		||||
            Log.e(activity.getClass().getName(), Utils.getErrorMessage(ex));
 | 
			
		||||
            ex.printStackTrace();
 | 
			
		||||
 | 
			
		||||
            new AlertDialog.Builder(activity)
 | 
			
		||||
                .setMessage(Utils.getErrorMessage(ex))
 | 
			
		||||
                .setCancelable(false)
 | 
			
		||||
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
 | 
			
		||||
                    public void onClick(DialogInterface arg0, int arg1) {
 | 
			
		||||
                        activity.finish();
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .show();
 | 
			
		||||
        } catch (WindowManager.BadTokenException unused) {
 | 
			
		||||
            /* Ignore... happens if the activity was destroyed */
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getHexString (byte[] b) throws Exception {
 | 
			
		||||
        String result = "";
 | 
			
		||||
        for (int i=0; i < b.length; i++) {
 | 
			
		||||
            result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getHexString (byte[] b, String defaultResult) {
 | 
			
		||||
        try {
 | 
			
		||||
            return getHexString(b);
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            return defaultResult;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static byte[] hexStringToByteArray (String s) {
 | 
			
		||||
        if ((s.length() % 2) != 0) {
 | 
			
		||||
            throw new IllegalArgumentException("Bad input string: " + s);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        int len = s.length();
 | 
			
		||||
        byte[] data = new byte[len / 2];
 | 
			
		||||
        for (int i = 0; i < len; i += 2) {
 | 
			
		||||
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
 | 
			
		||||
                                 + Character.digit(s.charAt(i+1), 16));
 | 
			
		||||
        }
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public static byte[] intToByteArray(int value) {
 | 
			
		||||
        return new byte[] {
 | 
			
		||||
                (byte)(value >>> 24),
 | 
			
		||||
                (byte)(value >>> 16),
 | 
			
		||||
                (byte)(value >>> 8),
 | 
			
		||||
                (byte)value};
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    
 | 
			
		||||
    public static int byteArrayToInt(byte[] b) {
 | 
			
		||||
        return byteArrayToInt(b, 0);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static int byteArrayToInt(byte[] b, int offset) {
 | 
			
		||||
        return byteArrayToInt(b, offset, b.length);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static int byteArrayToInt(byte[] b, int offset, int length) {
 | 
			
		||||
        return (int) byteArrayToLong(b, offset, length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static long byteArrayToLong(byte[] b, int offset, int length) {
 | 
			
		||||
        if (b.length < length)
 | 
			
		||||
            throw new IllegalArgumentException("length must be less than or equal to b.length");
 | 
			
		||||
 | 
			
		||||
        long value = 0;
 | 
			
		||||
        for (int i = 0; i < length; i++) {
 | 
			
		||||
            int shift = (length - 1 - i) * 8;
 | 
			
		||||
            value += (b[i + offset] & 0x000000FF) << shift;
 | 
			
		||||
        }
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static byte[] byteArraySlice(byte[] b, int offset, int length) {
 | 
			
		||||
        byte[] ret = new byte[length];
 | 
			
		||||
        for (int i = 0; i < length; i++)
 | 
			
		||||
            ret[i] = b[offset+i];
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String xmlNodeToString (Node node) throws Exception {
 | 
			
		||||
        // The amount of code required to do simple things in Java is incredible.
 | 
			
		||||
        Source source = new DOMSource(node);
 | 
			
		||||
        StringWriter stringWriter = new StringWriter();
 | 
			
		||||
        Result result = new StreamResult(stringWriter);
 | 
			
		||||
        TransformerFactory factory = TransformerFactory.newInstance();
 | 
			
		||||
        Transformer transformer = factory.newTransformer();
 | 
			
		||||
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
 | 
			
		||||
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
 | 
			
		||||
        transformer.setURIResolver(null);
 | 
			
		||||
        transformer.transform(source, result);
 | 
			
		||||
        return stringWriter.getBuffer().toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getErrorMessage (Throwable ex) {
 | 
			
		||||
        String errorMessage = ex.getLocalizedMessage();
 | 
			
		||||
        if (errorMessage == null)
 | 
			
		||||
            errorMessage = ex.getMessage();
 | 
			
		||||
        if (errorMessage == null)
 | 
			
		||||
            errorMessage = ex.toString();
 | 
			
		||||
 | 
			
		||||
        if (ex.getCause() != null) {
 | 
			
		||||
            String causeMessage = ex.getCause().getLocalizedMessage();
 | 
			
		||||
            if (causeMessage == null)
 | 
			
		||||
                causeMessage = ex.getCause().getMessage();
 | 
			
		||||
            if (causeMessage == null)
 | 
			
		||||
                causeMessage = ex.getCause().toString();
 | 
			
		||||
 | 
			
		||||
            if (causeMessage != null)
 | 
			
		||||
                errorMessage += ": " + causeMessage;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return errorMessage;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static <T> T findInList(List<T> list, Matcher<T> matcher) {
 | 
			
		||||
        for (T item : list) {
 | 
			
		||||
            if (matcher.matches(item)) {
 | 
			
		||||
                return item;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static interface Matcher<T> {
 | 
			
		||||
        public boolean matches(T t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int convertBCDtoInteger(byte data) {
 | 
			
		||||
        return (((data & (char)0xF0) >> 4) * 10) + ((data & (char)0x0F));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int getBitsFromInteger(int buffer, int iStartBit, int iLength) {
 | 
			
		||||
        return (buffer >> (iStartBit)) & ((char)0xFF >> (8 - iLength));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Based on function from mfocGUI by 'Huuf' (http://www.huuf.info/OV/) */
 | 
			
		||||
    public static int getBitsFromBuffer(byte[] buffer, int iStartBit, int iLength) {
 | 
			
		||||
        int iEndBit = iStartBit + iLength - 1;
 | 
			
		||||
        int iSByte = iStartBit / 8;
 | 
			
		||||
        int iSBit = iStartBit % 8;
 | 
			
		||||
        int iEByte = iEndBit / 8;
 | 
			
		||||
        int iEBit = iEndBit % 8;
 | 
			
		||||
 | 
			
		||||
        if (iSByte == iEByte) {
 | 
			
		||||
            return (int)(((char)buffer[iEByte] >> (7 - iEBit)) & ((char)0xFF >> (8 - iLength)));
 | 
			
		||||
        } else {
 | 
			
		||||
            int uRet = (((char)buffer[iSByte] & (char)((char)0xFF >> iSBit)) << (((iEByte - iSByte - 1) * 8) + (iEBit + 1)));
 | 
			
		||||
 | 
			
		||||
            for (int i = iSByte + 1; i < iEByte; i++) {
 | 
			
		||||
                uRet |= (((char)buffer[i] & (char)0xFF) << (((iEByte - i - 1) * 8) + (iEBit + 1)));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uRet |= (((char)buffer[iEByte] & (char)0xFF)) >> (7 - iEBit);
 | 
			
		||||
 | 
			
		||||
            return uRet;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	public static DesfireFileSettings selectAppFile(DesfireProtocol tag, int appID, int fileID) {
 | 
			
		||||
		try {
 | 
			
		||||
			tag.selectApp(appID);
 | 
			
		||||
		} catch (DesfireException e) {
 | 
			
		||||
			Log.w(TAG,"App not found");
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		try {
 | 
			
		||||
			return tag.getFileSettings(fileID);
 | 
			
		||||
		} catch (DesfireException e) {
 | 
			
		||||
			Log.w(TAG,"File not found");
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static boolean arrayContains(int[] arr, int item) {
 | 
			
		||||
		for (int i: arr)
 | 
			
		||||
			if (i==item)
 | 
			
		||||
				return true;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static boolean containsAppFile(DesfireProtocol tag, int appID, int fileID) {
 | 
			
		||||
		try {
 | 
			
		||||
			tag.selectApp(appID);
 | 
			
		||||
		} catch (DesfireException e) {
 | 
			
		||||
			Log.w(TAG,"App not found");
 | 
			
		||||
			Log.w(TAG, e);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		try {
 | 
			
		||||
			return arrayContains(tag.getFileList(),fileID);
 | 
			
		||||
		} catch (DesfireException e) {
 | 
			
		||||
			Log.w(TAG,"File not found");
 | 
			
		||||
			Log.w(TAG, e);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										226
									
								
								app/src/main/java/com/codebutler/farebot/Utils.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								app/src/main/java/com/codebutler/farebot/Utils.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,226 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Utils.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
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.app.AlertDialog
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import android.view.WindowManager
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireException
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireFileSettings
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireProtocol
 | 
			
		||||
import org.w3c.dom.Node
 | 
			
		||||
import java.io.StringWriter
 | 
			
		||||
import javax.xml.transform.OutputKeys
 | 
			
		||||
import javax.xml.transform.TransformerFactory
 | 
			
		||||
import javax.xml.transform.dom.DOMSource
 | 
			
		||||
import javax.xml.transform.stream.StreamResult
 | 
			
		||||
import kotlin.experimental.and
 | 
			
		||||
 | 
			
		||||
class Utils {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private val TAG = Utils::class.java.name
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun showError(activity: Activity, ex: Exception) {
 | 
			
		||||
            Log.e(activity.javaClass.name, ex.message, ex)
 | 
			
		||||
            AlertDialog.Builder(activity)
 | 
			
		||||
                .setMessage(getErrorMessage(ex))
 | 
			
		||||
                .show()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun showErrorAndFinish(activity: Activity, ex: Exception) {
 | 
			
		||||
            try {
 | 
			
		||||
                Log.e(activity.javaClass.name, getErrorMessage(ex))
 | 
			
		||||
                ex.printStackTrace()
 | 
			
		||||
 | 
			
		||||
                AlertDialog.Builder(activity)
 | 
			
		||||
                    .setMessage(getErrorMessage(ex))
 | 
			
		||||
                    .setCancelable(false)
 | 
			
		||||
                    .setPositiveButton(android.R.string.ok) { _, _ -> activity.finish() }
 | 
			
		||||
                    .show()
 | 
			
		||||
            } catch (unused: WindowManager.BadTokenException) {
 | 
			
		||||
                /* Ignore... happens if the activity was destroyed */
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Throws(Exception::class)
 | 
			
		||||
        fun getHexString(b: ByteArray): String {
 | 
			
		||||
            var result = ""
 | 
			
		||||
            for (i in b.indices) {
 | 
			
		||||
                result += ((b[i] and 0xff.toByte()) + 0x100).toString(16).substring(1)
 | 
			
		||||
            }
 | 
			
		||||
            return result
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun getHexString(b: ByteArray, defaultResult: String): String {
 | 
			
		||||
            return try {
 | 
			
		||||
                getHexString(b)
 | 
			
		||||
            } catch (ex: Exception) {
 | 
			
		||||
                defaultResult
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun hexStringToByteArray(s: String): ByteArray {
 | 
			
		||||
            if (s.length % 2 != 0) {
 | 
			
		||||
                throw IllegalArgumentException("Bad input string: $s")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val len = s.length
 | 
			
		||||
            val data = ByteArray(len / 2)
 | 
			
		||||
            var i = 0
 | 
			
		||||
            while (i < len) {
 | 
			
		||||
                data[i / 2] = ((Character.digit(s[i], 16) shl 4) + Character.digit(s[i + 1], 16)).toByte()
 | 
			
		||||
                i += 2
 | 
			
		||||
            }
 | 
			
		||||
            return data
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @JvmOverloads
 | 
			
		||||
        fun byteArrayToInt(b: ByteArray, offset: Int = 0, length: Int = b.size): Int {
 | 
			
		||||
            return byteArrayToLong(b, offset, length).toInt()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun byteArrayToLong(b: ByteArray, offset: Int, length: Int): Long {
 | 
			
		||||
            if (b.size < length)
 | 
			
		||||
                throw IllegalArgumentException("length must be less than or equal to b.length")
 | 
			
		||||
 | 
			
		||||
            var value: Long = 0
 | 
			
		||||
            for (i in 0 until length) {
 | 
			
		||||
                val shift = (length - 1 - i) * 8
 | 
			
		||||
                value += ((b[i + offset].toInt() and 0x000000FF).toLong() shl shift)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return value
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun byteArraySlice(b: ByteArray, offset: Int, length: Int): ByteArray {
 | 
			
		||||
            val ret = ByteArray(length)
 | 
			
		||||
            for (i in 0 until length)
 | 
			
		||||
                ret[i] = b[offset + i]
 | 
			
		||||
            return ret
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        @Throws(Exception::class)
 | 
			
		||||
        fun xmlNodeToString(node: Node): String {
 | 
			
		||||
            // The amount of code required to do simple things in Java is incredible.
 | 
			
		||||
            val source = DOMSource(node)
 | 
			
		||||
            val stringWriter = StringWriter()
 | 
			
		||||
            val result = StreamResult(stringWriter)
 | 
			
		||||
            val factory = TransformerFactory.newInstance()
 | 
			
		||||
            val transformer = factory.newTransformer()
 | 
			
		||||
            transformer.setOutputProperty(OutputKeys.INDENT, "yes")
 | 
			
		||||
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
 | 
			
		||||
            transformer.uriResolver = null
 | 
			
		||||
            transformer.transform(source, result)
 | 
			
		||||
            return stringWriter.buffer.toString()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getErrorMessage(ex: Throwable): String {
 | 
			
		||||
            var errorMessage: String? = ex.localizedMessage
 | 
			
		||||
            if (errorMessage == null)
 | 
			
		||||
                errorMessage = ex.message
 | 
			
		||||
            if (errorMessage == null)
 | 
			
		||||
                errorMessage = ex.toString()
 | 
			
		||||
 | 
			
		||||
            if (ex.cause != null) {
 | 
			
		||||
                var causeMessage: String? = ex.cause!!.localizedMessage
 | 
			
		||||
                if (causeMessage == null)
 | 
			
		||||
                    causeMessage = ex.cause!!.message
 | 
			
		||||
                if (causeMessage == null)
 | 
			
		||||
                    causeMessage = ex.cause.toString()
 | 
			
		||||
 | 
			
		||||
                errorMessage += ": $causeMessage"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return errorMessage
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun <T> findInList(list: List<T>, matcher: Matcher<T>): T? {
 | 
			
		||||
            for (item in list) {
 | 
			
		||||
                if (matcher.matches(item)) {
 | 
			
		||||
                    return item
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        interface Matcher<T> {
 | 
			
		||||
            fun matches(t: T): Boolean
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun selectAppFile(tag: DesfireProtocol, appID: Int, fileID: Int): DesfireFileSettings? {
 | 
			
		||||
            try {
 | 
			
		||||
                tag.selectApp(appID)
 | 
			
		||||
            } catch (e: DesfireException) {
 | 
			
		||||
                Log.w(TAG, "App not found")
 | 
			
		||||
                return null
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return try {
 | 
			
		||||
                tag.getFileSettings(fileID)
 | 
			
		||||
            } catch (e: DesfireException) {
 | 
			
		||||
                Log.w(TAG, "File not found")
 | 
			
		||||
                null
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun arrayContains(arr: IntArray, item: Int): Boolean {
 | 
			
		||||
            for (i in arr)
 | 
			
		||||
                if (i == item)
 | 
			
		||||
                    return true
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun containsAppFile(tag: DesfireProtocol, appID: Int, fileID: Int): Boolean {
 | 
			
		||||
            try {
 | 
			
		||||
                tag.selectApp(appID)
 | 
			
		||||
            } catch (e: DesfireException) {
 | 
			
		||||
                Log.w(TAG, "App not found")
 | 
			
		||||
                Log.w(TAG, e)
 | 
			
		||||
                return false
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return try {
 | 
			
		||||
                arrayContains(tag.fileList, fileID)
 | 
			
		||||
            } catch (e: DesfireException) {
 | 
			
		||||
                Log.w(TAG, "File not found")
 | 
			
		||||
                Log.w(TAG, e)
 | 
			
		||||
                false
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,77 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireApplication.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.desfire;
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel;
 | 
			
		||||
import android.os.Parcelable;
 | 
			
		||||
 | 
			
		||||
public class DesfireApplication implements Parcelable {
 | 
			
		||||
    private int           mId;
 | 
			
		||||
    private DesfireFile[] mFiles;
 | 
			
		||||
 | 
			
		||||
    public DesfireApplication (int id, DesfireFile[] files) {
 | 
			
		||||
        mId    = id;
 | 
			
		||||
        mFiles = files;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getId () {
 | 
			
		||||
        return mId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DesfireFile[] getFiles () {
 | 
			
		||||
        return mFiles;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DesfireFile getFile (int fileId) {
 | 
			
		||||
        for (DesfireFile file : mFiles) {
 | 
			
		||||
            if (file.getId() == fileId)
 | 
			
		||||
                return file;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static final Parcelable.Creator<DesfireApplication> CREATOR = new Parcelable.Creator<DesfireApplication>() {
 | 
			
		||||
        public DesfireApplication createFromParcel(Parcel source) {
 | 
			
		||||
            int id = source.readInt();
 | 
			
		||||
 | 
			
		||||
            DesfireFile[] files = new DesfireFile[source.readInt()];
 | 
			
		||||
            source.readTypedArray(files, DesfireFile.CREATOR);
 | 
			
		||||
 | 
			
		||||
            return new DesfireApplication(id, files);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DesfireApplication[] newArray (int size) {
 | 
			
		||||
            return new DesfireApplication[size];
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public void writeToParcel (Parcel parcel, int flags) {
 | 
			
		||||
        parcel.writeInt(mId);
 | 
			
		||||
        parcel.writeInt(mFiles.length);
 | 
			
		||||
        parcel.writeTypedArray(mFiles, flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int describeContents () {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }    
 | 
			
		||||
}
 | 
			
		||||
@ -1,13 +0,0 @@
 | 
			
		||||
package com.codebutler.farebot.card.desfire;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by Jakob Wenzel on 16.11.13.
 | 
			
		||||
 */
 | 
			
		||||
public class DesfireException extends Exception {
 | 
			
		||||
	public DesfireException(String message) {
 | 
			
		||||
		super(message);
 | 
			
		||||
	}
 | 
			
		||||
	public DesfireException(Throwable cause) {
 | 
			
		||||
		super(cause);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
package com.codebutler.farebot.card.desfire
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by Jakob Wenzel on 16.11.13.
 | 
			
		||||
 */
 | 
			
		||||
class DesfireException : Exception {
 | 
			
		||||
    constructor(message: String) : super(message) {}
 | 
			
		||||
    constructor(cause: Throwable) : super(cause) {}
 | 
			
		||||
}
 | 
			
		||||
@ -1,135 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireFile.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.desfire;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang3.ArrayUtils;
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel;
 | 
			
		||||
import android.os.Parcelable;
 | 
			
		||||
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireFileSettings.RecordDesfireFileSettings;
 | 
			
		||||
 | 
			
		||||
public class DesfireFile implements Parcelable {
 | 
			
		||||
    private int                 mId;
 | 
			
		||||
    private DesfireFileSettings mSettings;
 | 
			
		||||
    private byte[]              mData;
 | 
			
		||||
 | 
			
		||||
    public static DesfireFile create (int fileId, DesfireFileSettings fileSettings, byte[] fileData) {
 | 
			
		||||
        if (fileSettings instanceof RecordDesfireFileSettings)
 | 
			
		||||
            return new RecordDesfireFile(fileId, fileSettings, fileData);
 | 
			
		||||
        else
 | 
			
		||||
            return new DesfireFile(fileId, fileSettings, fileData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DesfireFile (int fileId, DesfireFileSettings fileSettings, byte[] fileData) {
 | 
			
		||||
        mId       = fileId;
 | 
			
		||||
        mSettings = fileSettings;
 | 
			
		||||
        mData     = fileData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DesfireFileSettings getFileSettings () {
 | 
			
		||||
        return mSettings;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getId () {
 | 
			
		||||
        return mId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] getData () {
 | 
			
		||||
        return mData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static final Parcelable.Creator<DesfireFile> CREATOR = new Parcelable.Creator<DesfireFile>() {
 | 
			
		||||
        public DesfireFile createFromParcel(Parcel source) {
 | 
			
		||||
            int fileId = source.readInt();
 | 
			
		||||
 | 
			
		||||
            boolean isError = (source.readInt() == 1);
 | 
			
		||||
 | 
			
		||||
            if (!isError) {
 | 
			
		||||
                DesfireFileSettings fileSettings = (DesfireFileSettings) source.readParcelable(DesfireFileSettings.class.getClassLoader());
 | 
			
		||||
                int    dataLength = source.readInt();
 | 
			
		||||
                byte[] fileData   = new byte[dataLength];
 | 
			
		||||
                source.readByteArray(fileData);
 | 
			
		||||
 | 
			
		||||
                return DesfireFile.create(fileId, fileSettings, fileData);
 | 
			
		||||
            } else {
 | 
			
		||||
                return new InvalidDesfireFile(fileId, source.readString());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DesfireFile[] newArray (int size) {
 | 
			
		||||
            return new DesfireFile[size];
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public void writeToParcel (Parcel parcel, int flags) {
 | 
			
		||||
        parcel.writeInt(mId);
 | 
			
		||||
        if (this instanceof InvalidDesfireFile) {
 | 
			
		||||
            parcel.writeInt(1);
 | 
			
		||||
            parcel.writeString(((InvalidDesfireFile)this).getErrorMessage());
 | 
			
		||||
        } else {
 | 
			
		||||
            parcel.writeInt(0);
 | 
			
		||||
            parcel.writeParcelable(mSettings, 0);
 | 
			
		||||
            parcel.writeInt(mData.length);
 | 
			
		||||
            parcel.writeByteArray(mData);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int describeContents () {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class RecordDesfireFile extends DesfireFile {
 | 
			
		||||
        private DesfireRecord[] mRecords;
 | 
			
		||||
 | 
			
		||||
        private RecordDesfireFile (int fileId, DesfireFileSettings fileSettings, byte[] fileData) {
 | 
			
		||||
            super(fileId, fileSettings, fileData);
 | 
			
		||||
 | 
			
		||||
            RecordDesfireFileSettings settings = (RecordDesfireFileSettings) fileSettings;
 | 
			
		||||
 | 
			
		||||
            DesfireRecord[] records = new DesfireRecord[settings.curRecords];
 | 
			
		||||
            for (int i = 0; i < settings.curRecords; i++) {
 | 
			
		||||
                int offset = settings.recordSize * i;
 | 
			
		||||
                records[i] = new DesfireRecord(ArrayUtils.subarray(getData(), offset, offset + settings.recordSize));
 | 
			
		||||
            }
 | 
			
		||||
            mRecords = records;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DesfireRecord[] getRecords () {
 | 
			
		||||
            return mRecords;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class InvalidDesfireFile extends DesfireFile {
 | 
			
		||||
        private String mErrorMessage;
 | 
			
		||||
 | 
			
		||||
        public InvalidDesfireFile (int fileId, String errorMessage) {
 | 
			
		||||
            super(fileId, null, new byte[0]);
 | 
			
		||||
            mErrorMessage = errorMessage;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public String getErrorMessage () {
 | 
			
		||||
            return mErrorMessage;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,103 @@
 | 
			
		||||
/**
 | 
			
		||||
 * DesfireFile.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.desfire
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import com.codebutler.farebot.card.desfire.DesfireFileSettings.RecordDesfireFileSettings
 | 
			
		||||
import org.apache.commons.lang3.ArrayUtils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
open class DesfireFile private constructor(val id: Int, private val fileSettings: DesfireFileSettings?, val data: ByteArray) :
 | 
			
		||||
    Parcelable {
 | 
			
		||||
 | 
			
		||||
    override fun writeToParcel(parcel: Parcel, flags: Int) {
 | 
			
		||||
        parcel.writeInt(id)
 | 
			
		||||
        if (this is InvalidDesfireFile) {
 | 
			
		||||
            parcel.writeInt(1)
 | 
			
		||||
            parcel.writeString(this.errorMessage)
 | 
			
		||||
        } else {
 | 
			
		||||
            parcel.writeInt(0)
 | 
			
		||||
            parcel.writeParcelable(fileSettings, 0)
 | 
			
		||||
            parcel.writeInt(data.size)
 | 
			
		||||
            parcel.writeByteArray(data)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun describeContents(): Int {
 | 
			
		||||
        return 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class RecordDesfireFile(fileId: Int, fileSettings: DesfireFileSettings, fileData: ByteArray) :
 | 
			
		||||
        DesfireFile(fileId, fileSettings, fileData) {
 | 
			
		||||
        private val records: Array<DesfireRecord?>
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
 | 
			
		||||
            val settings = fileSettings as RecordDesfireFileSettings
 | 
			
		||||
 | 
			
		||||
            val records = arrayOfNulls<DesfireRecord>(settings.curRecords)
 | 
			
		||||
            for (i in 0 until settings.curRecords) {
 | 
			
		||||
                val offset = settings.recordSize * i
 | 
			
		||||
                records[i] = DesfireRecord(ArrayUtils.subarray(data, offset, offset + settings.recordSize))
 | 
			
		||||
            }
 | 
			
		||||
            this.records = records
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class InvalidDesfireFile(fileId: Int, val errorMessage: String?) : DesfireFile(fileId, null, ByteArray(0))
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        fun create(fileId: Int, fileSettings: DesfireFileSettings, fileData: ByteArray): DesfireFile {
 | 
			
		||||
            return (fileSettings as? RecordDesfireFileSettings)?.let { RecordDesfireFile(fileId, it, fileData) }
 | 
			
		||||
                ?: DesfireFile(fileId, fileSettings, fileData)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        @JvmField
 | 
			
		||||
        val CREATOR: Parcelable.Creator<DesfireFile> = object : Parcelable.Creator<DesfireFile> {
 | 
			
		||||
            override fun createFromParcel(source: Parcel): DesfireFile {
 | 
			
		||||
                val fileId = source.readInt()
 | 
			
		||||
 | 
			
		||||
                val isError = source.readInt() == 1
 | 
			
		||||
 | 
			
		||||
                return if (!isError) {
 | 
			
		||||
                    val fileSettings =
 | 
			
		||||
                        source.readParcelable<Parcelable>(DesfireFileSettings::class.java.classLoader) as DesfireFileSettings
 | 
			
		||||
                    val dataLength = source.readInt()
 | 
			
		||||
                    val fileData = ByteArray(dataLength)
 | 
			
		||||
                    source.readByteArray(fileData)
 | 
			
		||||
 | 
			
		||||
                    create(fileId, fileSettings, fileData)
 | 
			
		||||
                } else {
 | 
			
		||||
                    InvalidDesfireFile(fileId, source.readString())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override fun newArray(size: Int): Array<DesfireFile?> {
 | 
			
		||||
                return arrayOfNulls(size)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,241 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireFileSettings.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.desfire;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang3.ArrayUtils;
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel;
 | 
			
		||||
import android.os.Parcelable;
 | 
			
		||||
 | 
			
		||||
import com.codebutler.farebot.Utils;
 | 
			
		||||
 | 
			
		||||
public abstract class DesfireFileSettings implements Parcelable {
 | 
			
		||||
    public final byte   fileType;
 | 
			
		||||
    public final byte   commSetting;
 | 
			
		||||
    public final byte[] accessRights;
 | 
			
		||||
 | 
			
		||||
    /* DesfireFile Types */
 | 
			
		||||
    static final byte STANDARD_DATA_FILE = (byte) 0x00;
 | 
			
		||||
    static final byte BACKUP_DATA_FILE   = (byte) 0x01;
 | 
			
		||||
    static final byte VALUE_FILE         = (byte) 0x02;
 | 
			
		||||
    static final byte LINEAR_RECORD_FILE = (byte) 0x03;
 | 
			
		||||
    static final byte CYCLIC_RECORD_FILE = (byte) 0x04;
 | 
			
		||||
    
 | 
			
		||||
    public static DesfireFileSettings Create (byte[] data) throws DesfireException {
 | 
			
		||||
        byte fileType = (byte) data[0];
 | 
			
		||||
 | 
			
		||||
        ByteArrayInputStream stream = new ByteArrayInputStream(data);
 | 
			
		||||
 | 
			
		||||
        if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE)
 | 
			
		||||
            return new StandardDesfireFileSettings(stream);
 | 
			
		||||
        else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE)
 | 
			
		||||
            return new RecordDesfireFileSettings(stream);
 | 
			
		||||
        else if (fileType == VALUE_FILE)
 | 
			
		||||
            return new ValueDesfireFileSettings(stream);
 | 
			
		||||
        else
 | 
			
		||||
            throw new DesfireException("Unknown file type: " + Integer.toHexString(fileType));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DesfireFileSettings (ByteArrayInputStream stream) {
 | 
			
		||||
        fileType    = (byte) stream.read();
 | 
			
		||||
        commSetting = (byte) stream.read();
 | 
			
		||||
 | 
			
		||||
        accessRights = new byte[2];
 | 
			
		||||
        stream.read(accessRights, 0, accessRights.length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights) {
 | 
			
		||||
        this.fileType     = fileType;
 | 
			
		||||
        this.commSetting  = commSetting;
 | 
			
		||||
        this.accessRights = accessRights;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getFileTypeName () {
 | 
			
		||||
        switch (fileType) {
 | 
			
		||||
            case STANDARD_DATA_FILE:
 | 
			
		||||
                return "Standard";
 | 
			
		||||
            case BACKUP_DATA_FILE:
 | 
			
		||||
                return "Backup";
 | 
			
		||||
            case VALUE_FILE:
 | 
			
		||||
                return "Value";
 | 
			
		||||
            case LINEAR_RECORD_FILE:
 | 
			
		||||
                return "Linear Record";
 | 
			
		||||
            case CYCLIC_RECORD_FILE:
 | 
			
		||||
                return "Cyclic Record";
 | 
			
		||||
            default:
 | 
			
		||||
                return "Unknown";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static final Parcelable.Creator<DesfireFileSettings> CREATOR = new Parcelable.Creator<DesfireFileSettings>() {
 | 
			
		||||
        public DesfireFileSettings createFromParcel(Parcel source) {
 | 
			
		||||
            byte fileType       = source.readByte();
 | 
			
		||||
            byte commSetting    = source.readByte();
 | 
			
		||||
            byte[] accessRights = new byte[source.readInt()];
 | 
			
		||||
            source.readByteArray(accessRights);
 | 
			
		||||
 | 
			
		||||
            if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) {
 | 
			
		||||
                int fileSize = source.readInt();
 | 
			
		||||
                return new StandardDesfireFileSettings(fileType, commSetting, accessRights, fileSize);
 | 
			
		||||
            } else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) {
 | 
			
		||||
                int recordSize = source.readInt();
 | 
			
		||||
                int maxRecords = source.readInt();
 | 
			
		||||
                int curRecords = source.readInt();
 | 
			
		||||
                return new RecordDesfireFileSettings(fileType, commSetting, accessRights, recordSize, maxRecords, curRecords);
 | 
			
		||||
            } else {
 | 
			
		||||
                return new UnsupportedDesfireFileSettings(fileType);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DesfireFileSettings[] newArray(int size) {
 | 
			
		||||
            return new DesfireFileSettings[size];
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public void writeToParcel (Parcel parcel, int flags) {
 | 
			
		||||
        parcel.writeByte(fileType);
 | 
			
		||||
        parcel.writeByte(commSetting);
 | 
			
		||||
        parcel.writeInt(accessRights.length);
 | 
			
		||||
        parcel.writeByteArray(accessRights);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int describeContents () {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class StandardDesfireFileSettings extends DesfireFileSettings {
 | 
			
		||||
        public final int fileSize;
 | 
			
		||||
 | 
			
		||||
        private StandardDesfireFileSettings (ByteArrayInputStream stream) {
 | 
			
		||||
            super(stream);
 | 
			
		||||
            byte[] buf = new byte[3];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            ArrayUtils.reverse(buf);
 | 
			
		||||
            fileSize = Utils.byteArrayToInt(buf);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        StandardDesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights, int fileSize) {
 | 
			
		||||
            super(fileType, commSetting, accessRights);
 | 
			
		||||
            this.fileSize = fileSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void writeToParcel (Parcel parcel, int flags) {
 | 
			
		||||
            super.writeToParcel(parcel, flags);
 | 
			
		||||
            parcel.writeInt(fileSize);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class RecordDesfireFileSettings extends DesfireFileSettings {
 | 
			
		||||
        public final int recordSize;
 | 
			
		||||
        public final int maxRecords;
 | 
			
		||||
        public final int curRecords;
 | 
			
		||||
 | 
			
		||||
        public RecordDesfireFileSettings(ByteArrayInputStream stream) {
 | 
			
		||||
            super(stream);
 | 
			
		||||
 | 
			
		||||
            byte[] buf = new byte[3];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            ArrayUtils.reverse(buf);
 | 
			
		||||
            recordSize = Utils.byteArrayToInt(buf);
 | 
			
		||||
 | 
			
		||||
            buf = new byte[3];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            ArrayUtils.reverse(buf);
 | 
			
		||||
            maxRecords = Utils.byteArrayToInt(buf);
 | 
			
		||||
 | 
			
		||||
            buf = new byte[3];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            ArrayUtils.reverse(buf);
 | 
			
		||||
            curRecords = Utils.byteArrayToInt(buf);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RecordDesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights, int recordSize, int maxRecords, int curRecords) {
 | 
			
		||||
            super(fileType, commSetting, accessRights);
 | 
			
		||||
            this.recordSize = recordSize;
 | 
			
		||||
            this.maxRecords = maxRecords;
 | 
			
		||||
            this.curRecords = curRecords;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void writeToParcel (Parcel parcel, int flags) {
 | 
			
		||||
            super.writeToParcel(parcel, flags);
 | 
			
		||||
            parcel.writeInt(recordSize);
 | 
			
		||||
            parcel.writeInt(maxRecords);
 | 
			
		||||
            parcel.writeInt(curRecords);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static class ValueDesfireFileSettings extends DesfireFileSettings {
 | 
			
		||||
        public final int lowerLimit;
 | 
			
		||||
        public final int upperLimit;
 | 
			
		||||
        public final int value;
 | 
			
		||||
        public final byte limitedCreditEnabled;
 | 
			
		||||
 | 
			
		||||
        public ValueDesfireFileSettings(ByteArrayInputStream stream) {
 | 
			
		||||
            super(stream);
 | 
			
		||||
 | 
			
		||||
            byte[] buf = new byte[4];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            ArrayUtils.reverse(buf);
 | 
			
		||||
            lowerLimit = Utils.byteArrayToInt(buf);
 | 
			
		||||
 | 
			
		||||
            buf = new byte[4];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            ArrayUtils.reverse(buf);
 | 
			
		||||
            upperLimit = Utils.byteArrayToInt(buf);
 | 
			
		||||
 | 
			
		||||
            buf = new byte[4];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            ArrayUtils.reverse(buf);
 | 
			
		||||
            value = Utils.byteArrayToInt(buf);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            buf = new byte[1];
 | 
			
		||||
            stream.read(buf, 0, buf.length);
 | 
			
		||||
            limitedCreditEnabled = buf[0];
 | 
			
		||||
            
 | 
			
		||||
            //http://www.skyetek.com/docs/m2/desfire.pdf
 | 
			
		||||
            //http://neteril.org/files/M075031_desfire.pdf
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void writeToParcel (Parcel parcel, int flags) {
 | 
			
		||||
            super.writeToParcel(parcel, flags);
 | 
			
		||||
            parcel.writeInt(lowerLimit);
 | 
			
		||||
            parcel.writeInt(upperLimit);
 | 
			
		||||
            parcel.writeInt(value);
 | 
			
		||||
            parcel.writeByte(limitedCreditEnabled);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public static class UnsupportedDesfireFileSettings extends DesfireFileSettings {
 | 
			
		||||
        public UnsupportedDesfireFileSettings(byte fileType) {
 | 
			
		||||
            super(fileType, Byte.MIN_VALUE, new byte[0]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,247 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireFileSettings.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.desfire
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import com.codebutler.farebot.Utils
 | 
			
		||||
import org.apache.commons.lang3.ArrayUtils
 | 
			
		||||
import java.io.ByteArrayInputStream
 | 
			
		||||
 | 
			
		||||
abstract class DesfireFileSettings : Parcelable {
 | 
			
		||||
    private val fileType: Byte
 | 
			
		||||
    private val commSetting: Byte
 | 
			
		||||
    private val accessRights: ByteArray
 | 
			
		||||
 | 
			
		||||
    @Suppress("unused")
 | 
			
		||||
    val fileTypeName: String
 | 
			
		||||
        get() {
 | 
			
		||||
            return when (fileType) {
 | 
			
		||||
                STANDARD_DATA_FILE -> "Standard"
 | 
			
		||||
                BACKUP_DATA_FILE -> "Backup"
 | 
			
		||||
                VALUE_FILE -> "Value"
 | 
			
		||||
                LINEAR_RECORD_FILE -> "Linear Record"
 | 
			
		||||
                CYCLIC_RECORD_FILE -> "Cyclic Record"
 | 
			
		||||
                else -> "Unknown"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private constructor(stream: ByteArrayInputStream) {
 | 
			
		||||
        fileType = stream.read().toByte()
 | 
			
		||||
        commSetting = stream.read().toByte()
 | 
			
		||||
 | 
			
		||||
        accessRights = ByteArray(2)
 | 
			
		||||
        stream.read(accessRights, 0, accessRights.size)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private constructor(fileType: Byte, commSetting: Byte, accessRights: ByteArray) {
 | 
			
		||||
        this.fileType = fileType
 | 
			
		||||
        this.commSetting = commSetting
 | 
			
		||||
        this.accessRights = accessRights
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun writeToParcel(parcel: Parcel, flags: Int) {
 | 
			
		||||
        parcel.writeByte(fileType)
 | 
			
		||||
        parcel.writeByte(commSetting)
 | 
			
		||||
        parcel.writeInt(accessRights.size)
 | 
			
		||||
        parcel.writeByteArray(accessRights)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun describeContents(): Int {
 | 
			
		||||
        return 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class StandardDesfireFileSettings : DesfireFileSettings {
 | 
			
		||||
        private val fileSize: Int
 | 
			
		||||
 | 
			
		||||
        internal constructor(stream: ByteArrayInputStream) : super(stream) {
 | 
			
		||||
            val buf = ByteArray(3)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            ArrayUtils.reverse(buf)
 | 
			
		||||
            fileSize = Utils.byteArrayToInt(buf)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal constructor(
 | 
			
		||||
            fileType: Byte,
 | 
			
		||||
            commSetting: Byte,
 | 
			
		||||
            accessRights: ByteArray,
 | 
			
		||||
            fileSize: Int
 | 
			
		||||
        ) : super(fileType, commSetting, accessRights) {
 | 
			
		||||
            this.fileSize = fileSize
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun writeToParcel(parcel: Parcel, flags: Int) {
 | 
			
		||||
            super.writeToParcel(parcel, flags)
 | 
			
		||||
            parcel.writeInt(fileSize)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class RecordDesfireFileSettings : DesfireFileSettings {
 | 
			
		||||
        private val maxRecords: Int
 | 
			
		||||
        val recordSize: Int
 | 
			
		||||
        val curRecords: Int
 | 
			
		||||
 | 
			
		||||
        constructor(stream: ByteArrayInputStream) : super(stream) {
 | 
			
		||||
 | 
			
		||||
            var buf = ByteArray(3)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            ArrayUtils.reverse(buf)
 | 
			
		||||
            recordSize = Utils.byteArrayToInt(buf)
 | 
			
		||||
 | 
			
		||||
            buf = ByteArray(3)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            ArrayUtils.reverse(buf)
 | 
			
		||||
            maxRecords = Utils.byteArrayToInt(buf)
 | 
			
		||||
 | 
			
		||||
            buf = ByteArray(3)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            ArrayUtils.reverse(buf)
 | 
			
		||||
            curRecords = Utils.byteArrayToInt(buf)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal constructor(
 | 
			
		||||
            fileType: Byte,
 | 
			
		||||
            commSetting: Byte,
 | 
			
		||||
            accessRights: ByteArray,
 | 
			
		||||
            recordSize: Int,
 | 
			
		||||
            maxRecords: Int,
 | 
			
		||||
            curRecords: Int
 | 
			
		||||
        ) : super(fileType, commSetting, accessRights) {
 | 
			
		||||
            this.recordSize = recordSize
 | 
			
		||||
            this.maxRecords = maxRecords
 | 
			
		||||
            this.curRecords = curRecords
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun writeToParcel(parcel: Parcel, flags: Int) {
 | 
			
		||||
            super.writeToParcel(parcel, flags)
 | 
			
		||||
            parcel.writeInt(recordSize)
 | 
			
		||||
            parcel.writeInt(maxRecords)
 | 
			
		||||
            parcel.writeInt(curRecords)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class ValueDesfireFileSettings(stream: ByteArrayInputStream) : DesfireFileSettings(stream) {
 | 
			
		||||
        private val lowerLimit: Int
 | 
			
		||||
        private val upperLimit: Int
 | 
			
		||||
        val value: Int
 | 
			
		||||
        private val limitedCreditEnabled: Byte
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
 | 
			
		||||
            var buf = ByteArray(4)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            ArrayUtils.reverse(buf)
 | 
			
		||||
            lowerLimit = Utils.byteArrayToInt(buf)
 | 
			
		||||
 | 
			
		||||
            buf = ByteArray(4)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            ArrayUtils.reverse(buf)
 | 
			
		||||
            upperLimit = Utils.byteArrayToInt(buf)
 | 
			
		||||
 | 
			
		||||
            buf = ByteArray(4)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            ArrayUtils.reverse(buf)
 | 
			
		||||
            value = Utils.byteArrayToInt(buf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            buf = ByteArray(1)
 | 
			
		||||
            stream.read(buf, 0, buf.size)
 | 
			
		||||
            limitedCreditEnabled = buf[0]
 | 
			
		||||
 | 
			
		||||
            //http://www.skyetek.com/docs/m2/desfire.pdf
 | 
			
		||||
            //http://neteril.org/files/M075031_desfire.pdf
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun writeToParcel(parcel: Parcel, flags: Int) {
 | 
			
		||||
            super.writeToParcel(parcel, flags)
 | 
			
		||||
            parcel.writeInt(lowerLimit)
 | 
			
		||||
            parcel.writeInt(upperLimit)
 | 
			
		||||
            parcel.writeInt(value)
 | 
			
		||||
            parcel.writeByte(limitedCreditEnabled)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class UnsupportedDesfireFileSettings(fileType: Byte) :
 | 
			
		||||
        DesfireFileSettings(fileType, java.lang.Byte.MIN_VALUE, ByteArray(0))
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        /* DesfireFile Types */
 | 
			
		||||
        internal const val STANDARD_DATA_FILE = 0x00.toByte()
 | 
			
		||||
        internal const val BACKUP_DATA_FILE = 0x01.toByte()
 | 
			
		||||
        internal const val VALUE_FILE = 0x02.toByte()
 | 
			
		||||
        internal const val LINEAR_RECORD_FILE = 0x03.toByte()
 | 
			
		||||
        internal const val CYCLIC_RECORD_FILE = 0x04.toByte()
 | 
			
		||||
 | 
			
		||||
        @Throws(DesfireException::class)
 | 
			
		||||
        fun create(data: ByteArray): DesfireFileSettings {
 | 
			
		||||
            val fileType = data[0]
 | 
			
		||||
 | 
			
		||||
            val stream = ByteArrayInputStream(data)
 | 
			
		||||
 | 
			
		||||
            return if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE)
 | 
			
		||||
                StandardDesfireFileSettings(stream)
 | 
			
		||||
            else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE)
 | 
			
		||||
                RecordDesfireFileSettings(stream)
 | 
			
		||||
            else if (fileType == VALUE_FILE)
 | 
			
		||||
                ValueDesfireFileSettings(stream)
 | 
			
		||||
            else
 | 
			
		||||
                throw DesfireException("Unknown file type: " + Integer.toHexString(fileType.toInt()))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        @JvmField
 | 
			
		||||
        val CREATOR: Parcelable.Creator<DesfireFileSettings> = object : Parcelable.Creator<DesfireFileSettings> {
 | 
			
		||||
            override fun createFromParcel(source: Parcel): DesfireFileSettings {
 | 
			
		||||
                val fileType = source.readByte()
 | 
			
		||||
                val commSetting = source.readByte()
 | 
			
		||||
                val accessRights = ByteArray(source.readInt())
 | 
			
		||||
                source.readByteArray(accessRights)
 | 
			
		||||
 | 
			
		||||
                if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) {
 | 
			
		||||
                    val fileSize = source.readInt()
 | 
			
		||||
                    return StandardDesfireFileSettings(fileType, commSetting, accessRights, fileSize)
 | 
			
		||||
                } else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) {
 | 
			
		||||
                    val recordSize = source.readInt()
 | 
			
		||||
                    val maxRecords = source.readInt()
 | 
			
		||||
                    val curRecords = source.readInt()
 | 
			
		||||
                    return RecordDesfireFileSettings(
 | 
			
		||||
                        fileType,
 | 
			
		||||
                        commSetting,
 | 
			
		||||
                        accessRights,
 | 
			
		||||
                        recordSize,
 | 
			
		||||
                        maxRecords,
 | 
			
		||||
                        curRecords
 | 
			
		||||
                    )
 | 
			
		||||
                } else {
 | 
			
		||||
                    return UnsupportedDesfireFileSettings(fileType)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override fun newArray(size: Int): Array<DesfireFileSettings?> {
 | 
			
		||||
                return arrayOfNulls(size)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,173 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireManufacturingData.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.desfire;
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel;
 | 
			
		||||
import android.os.Parcelable;
 | 
			
		||||
import com.codebutler.farebot.Utils;
 | 
			
		||||
import org.w3c.dom.Element;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
 | 
			
		||||
public class DesfireManufacturingData implements Parcelable {
 | 
			
		||||
    public final int hwVendorID;
 | 
			
		||||
    public final int hwType;
 | 
			
		||||
    public final int hwSubType;
 | 
			
		||||
    public final int hwMajorVersion;
 | 
			
		||||
    public final int hwMinorVersion;
 | 
			
		||||
    public final int hwStorageSize;
 | 
			
		||||
    public final int hwProtocol;
 | 
			
		||||
 | 
			
		||||
    public final int swVendorID;
 | 
			
		||||
    public final int swType;
 | 
			
		||||
    public final int swSubType;
 | 
			
		||||
    public final int swMajorVersion;
 | 
			
		||||
    public final int swMinorVersion;
 | 
			
		||||
    public final int swStorageSize;
 | 
			
		||||
    public final int swProtocol;
 | 
			
		||||
 | 
			
		||||
    public final int uid;
 | 
			
		||||
    public final int batchNo;
 | 
			
		||||
    public final int weekProd;
 | 
			
		||||
    public final int yearProd;
 | 
			
		||||
 | 
			
		||||
    public DesfireManufacturingData (byte[] data) {
 | 
			
		||||
        ByteArrayInputStream stream = new ByteArrayInputStream(data);
 | 
			
		||||
        hwVendorID     = stream.read();
 | 
			
		||||
        hwType         = stream.read();
 | 
			
		||||
        hwSubType      = stream.read();
 | 
			
		||||
        hwMajorVersion = stream.read();
 | 
			
		||||
        hwMinorVersion = stream.read();
 | 
			
		||||
        hwStorageSize  = stream.read();
 | 
			
		||||
        hwProtocol     = stream.read();
 | 
			
		||||
 | 
			
		||||
        swVendorID     = stream.read();
 | 
			
		||||
        swType         = stream.read();
 | 
			
		||||
        swSubType      = stream.read();
 | 
			
		||||
        swMajorVersion = stream.read();
 | 
			
		||||
        swMinorVersion = stream.read();
 | 
			
		||||
        swStorageSize  = stream.read();
 | 
			
		||||
        swProtocol     = stream.read();
 | 
			
		||||
 | 
			
		||||
        // FIXME: This has fewer digits than what's contained in EXTRA_ID, why?
 | 
			
		||||
        byte[] buf = new byte[7];
 | 
			
		||||
        stream.read(buf, 0, buf.length);
 | 
			
		||||
        uid = Utils.byteArrayToInt(buf);
 | 
			
		||||
 | 
			
		||||
        // FIXME: This is returning a negative number. Probably is unsigned.
 | 
			
		||||
        buf = new byte[5];
 | 
			
		||||
        stream.read(buf, 0, buf.length);
 | 
			
		||||
        batchNo = Utils.byteArrayToInt(buf);
 | 
			
		||||
 | 
			
		||||
        // FIXME: These numbers aren't making sense.
 | 
			
		||||
        weekProd = stream.read();
 | 
			
		||||
        yearProd = stream.read();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static DesfireManufacturingData fromXml (Element element) {
 | 
			
		||||
        return new DesfireManufacturingData(element);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DesfireManufacturingData (Element element) {
 | 
			
		||||
        hwVendorID     = Integer.parseInt(element.getElementsByTagName("hw-vendor-id").item(0).getTextContent());
 | 
			
		||||
        hwType         = Integer.parseInt(element.getElementsByTagName("hw-type").item(0).getTextContent());
 | 
			
		||||
        hwSubType      = Integer.parseInt(element.getElementsByTagName("hw-sub-type").item(0).getTextContent());
 | 
			
		||||
        hwMajorVersion = Integer.parseInt(element.getElementsByTagName("hw-major-version").item(0).getTextContent());
 | 
			
		||||
        hwMinorVersion = Integer.parseInt(element.getElementsByTagName("hw-minor-version").item(0).getTextContent());
 | 
			
		||||
        hwStorageSize  = Integer.parseInt(element.getElementsByTagName("hw-storage-size").item(0).getTextContent());
 | 
			
		||||
        hwProtocol     = Integer.parseInt(element.getElementsByTagName("hw-protocol").item(0).getTextContent());
 | 
			
		||||
 | 
			
		||||
        swVendorID     = Integer.parseInt(element.getElementsByTagName("sw-vendor-id").item(0).getTextContent());
 | 
			
		||||
        swType         = Integer.parseInt(element.getElementsByTagName("sw-type").item(0).getTextContent());
 | 
			
		||||
        swSubType      = Integer.parseInt(element.getElementsByTagName("sw-sub-type").item(0).getTextContent());
 | 
			
		||||
        swMajorVersion = Integer.parseInt(element.getElementsByTagName("sw-major-version").item(0).getTextContent());
 | 
			
		||||
        swMinorVersion = Integer.parseInt(element.getElementsByTagName("sw-minor-version").item(0).getTextContent());
 | 
			
		||||
        swStorageSize  = Integer.parseInt(element.getElementsByTagName("sw-storage-size").item(0).getTextContent());
 | 
			
		||||
        swProtocol     = Integer.parseInt(element.getElementsByTagName("sw-protocol").item(0).getTextContent());
 | 
			
		||||
 | 
			
		||||
        uid      = Integer.parseInt(element.getElementsByTagName("uid").item(0).getTextContent());
 | 
			
		||||
        batchNo  = Integer.parseInt(element.getElementsByTagName("batch-no").item(0).getTextContent());
 | 
			
		||||
        weekProd = Integer.parseInt(element.getElementsByTagName("week-prod").item(0).getTextContent());
 | 
			
		||||
        yearProd = Integer.parseInt(element.getElementsByTagName("year-prod").item(0).getTextContent());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DesfireManufacturingData (Parcel parcel) {
 | 
			
		||||
        hwVendorID     = parcel.readInt();
 | 
			
		||||
        hwType         = parcel.readInt();
 | 
			
		||||
        hwSubType      = parcel.readInt();
 | 
			
		||||
        hwMajorVersion = parcel.readInt();
 | 
			
		||||
        hwMinorVersion = parcel.readInt();
 | 
			
		||||
        hwStorageSize  = parcel.readInt();
 | 
			
		||||
        hwProtocol     = parcel.readInt();
 | 
			
		||||
 | 
			
		||||
        swVendorID     = parcel.readInt();
 | 
			
		||||
        swType         = parcel.readInt();
 | 
			
		||||
        swSubType      = parcel.readInt();
 | 
			
		||||
        swMajorVersion = parcel.readInt();
 | 
			
		||||
        swMinorVersion = parcel.readInt();
 | 
			
		||||
        swStorageSize  = parcel.readInt();
 | 
			
		||||
        swProtocol     = parcel.readInt();
 | 
			
		||||
 | 
			
		||||
        uid      = parcel.readInt();
 | 
			
		||||
        batchNo  = parcel.readInt();
 | 
			
		||||
        weekProd = parcel.readInt();
 | 
			
		||||
        yearProd = parcel.readInt();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void writeToParcel(Parcel parcel, int flags) {
 | 
			
		||||
        parcel.writeInt(hwVendorID);
 | 
			
		||||
        parcel.writeInt(hwType);
 | 
			
		||||
        parcel.writeInt(hwSubType);
 | 
			
		||||
        parcel.writeInt(hwMajorVersion);
 | 
			
		||||
        parcel.writeInt(hwMinorVersion);
 | 
			
		||||
        parcel.writeInt(hwStorageSize);
 | 
			
		||||
        parcel.writeInt(hwProtocol);
 | 
			
		||||
 | 
			
		||||
        parcel.writeInt(swVendorID);
 | 
			
		||||
        parcel.writeInt(swType);
 | 
			
		||||
        parcel.writeInt(swSubType);
 | 
			
		||||
        parcel.writeInt(swMajorVersion);
 | 
			
		||||
        parcel.writeInt(swMinorVersion);
 | 
			
		||||
        parcel.writeInt(swStorageSize);
 | 
			
		||||
        parcel.writeInt(swProtocol);
 | 
			
		||||
 | 
			
		||||
        parcel.writeInt(uid);
 | 
			
		||||
        parcel.writeInt(batchNo);
 | 
			
		||||
        parcel.writeInt(weekProd);
 | 
			
		||||
        parcel.writeInt(yearProd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int describeContents() {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static final Parcelable.Creator<DesfireManufacturingData> CREATOR = new Parcelable.Creator<DesfireManufacturingData>() {
 | 
			
		||||
        public DesfireManufacturingData createFromParcel(Parcel source) {
 | 
			
		||||
            return new DesfireManufacturingData(source);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DesfireManufacturingData[] newArray(int size) {
 | 
			
		||||
            return new DesfireManufacturingData[size];
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,180 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireManufacturingData.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.desfire
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import com.codebutler.farebot.Utils
 | 
			
		||||
import org.w3c.dom.Element
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayInputStream
 | 
			
		||||
 | 
			
		||||
class DesfireManufacturingData : Parcelable {
 | 
			
		||||
    private val hwVendorID: Int
 | 
			
		||||
    private val hwType: Int
 | 
			
		||||
    private val hwSubType: Int
 | 
			
		||||
    private val hwMajorVersion: Int
 | 
			
		||||
    private val hwMinorVersion: Int
 | 
			
		||||
    private val hwStorageSize: Int
 | 
			
		||||
    private val hwProtocol: Int
 | 
			
		||||
 | 
			
		||||
    private val swVendorID: Int
 | 
			
		||||
    private val swType: Int
 | 
			
		||||
    private val swSubType: Int
 | 
			
		||||
    private val swMajorVersion: Int
 | 
			
		||||
    private val swMinorVersion: Int
 | 
			
		||||
    private val swStorageSize: Int
 | 
			
		||||
    private val swProtocol: Int
 | 
			
		||||
 | 
			
		||||
    private val uid: Int
 | 
			
		||||
    private val batchNo: Int
 | 
			
		||||
    private val weekProd: Int
 | 
			
		||||
    private val yearProd: Int
 | 
			
		||||
 | 
			
		||||
    constructor(data: ByteArray) {
 | 
			
		||||
        val stream = ByteArrayInputStream(data)
 | 
			
		||||
        hwVendorID = stream.read()
 | 
			
		||||
        hwType = stream.read()
 | 
			
		||||
        hwSubType = stream.read()
 | 
			
		||||
        hwMajorVersion = stream.read()
 | 
			
		||||
        hwMinorVersion = stream.read()
 | 
			
		||||
        hwStorageSize = stream.read()
 | 
			
		||||
        hwProtocol = stream.read()
 | 
			
		||||
 | 
			
		||||
        swVendorID = stream.read()
 | 
			
		||||
        swType = stream.read()
 | 
			
		||||
        swSubType = stream.read()
 | 
			
		||||
        swMajorVersion = stream.read()
 | 
			
		||||
        swMinorVersion = stream.read()
 | 
			
		||||
        swStorageSize = stream.read()
 | 
			
		||||
        swProtocol = stream.read()
 | 
			
		||||
 | 
			
		||||
        // FIXME: This has fewer digits than what's contained in EXTRA_ID, why?
 | 
			
		||||
        var buf = ByteArray(7)
 | 
			
		||||
        stream.read(buf, 0, buf.size)
 | 
			
		||||
        uid = Utils.byteArrayToInt(buf)
 | 
			
		||||
 | 
			
		||||
        // FIXME: This is returning a negative number. Probably is unsigned.
 | 
			
		||||
        buf = ByteArray(5)
 | 
			
		||||
        stream.read(buf, 0, buf.size)
 | 
			
		||||
        batchNo = Utils.byteArrayToInt(buf)
 | 
			
		||||
 | 
			
		||||
        // FIXME: These numbers aren't making sense.
 | 
			
		||||
        weekProd = stream.read()
 | 
			
		||||
        yearProd = stream.read()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private constructor(element: Element) {
 | 
			
		||||
        hwVendorID = Integer.parseInt(element.getElementsByTagName("hw-vendor-id").item(0).textContent)
 | 
			
		||||
        hwType = Integer.parseInt(element.getElementsByTagName("hw-type").item(0).textContent)
 | 
			
		||||
        hwSubType = Integer.parseInt(element.getElementsByTagName("hw-sub-type").item(0).textContent)
 | 
			
		||||
        hwMajorVersion = Integer.parseInt(element.getElementsByTagName("hw-major-version").item(0).textContent)
 | 
			
		||||
        hwMinorVersion = Integer.parseInt(element.getElementsByTagName("hw-minor-version").item(0).textContent)
 | 
			
		||||
        hwStorageSize = Integer.parseInt(element.getElementsByTagName("hw-storage-size").item(0).textContent)
 | 
			
		||||
        hwProtocol = Integer.parseInt(element.getElementsByTagName("hw-protocol").item(0).textContent)
 | 
			
		||||
 | 
			
		||||
        swVendorID = Integer.parseInt(element.getElementsByTagName("sw-vendor-id").item(0).textContent)
 | 
			
		||||
        swType = Integer.parseInt(element.getElementsByTagName("sw-type").item(0).textContent)
 | 
			
		||||
        swSubType = Integer.parseInt(element.getElementsByTagName("sw-sub-type").item(0).textContent)
 | 
			
		||||
        swMajorVersion = Integer.parseInt(element.getElementsByTagName("sw-major-version").item(0).textContent)
 | 
			
		||||
        swMinorVersion = Integer.parseInt(element.getElementsByTagName("sw-minor-version").item(0).textContent)
 | 
			
		||||
        swStorageSize = Integer.parseInt(element.getElementsByTagName("sw-storage-size").item(0).textContent)
 | 
			
		||||
        swProtocol = Integer.parseInt(element.getElementsByTagName("sw-protocol").item(0).textContent)
 | 
			
		||||
 | 
			
		||||
        uid = Integer.parseInt(element.getElementsByTagName("uid").item(0).textContent)
 | 
			
		||||
        batchNo = Integer.parseInt(element.getElementsByTagName("batch-no").item(0).textContent)
 | 
			
		||||
        weekProd = Integer.parseInt(element.getElementsByTagName("week-prod").item(0).textContent)
 | 
			
		||||
        yearProd = Integer.parseInt(element.getElementsByTagName("year-prod").item(0).textContent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private constructor(parcel: Parcel) {
 | 
			
		||||
        hwVendorID = parcel.readInt()
 | 
			
		||||
        hwType = parcel.readInt()
 | 
			
		||||
        hwSubType = parcel.readInt()
 | 
			
		||||
        hwMajorVersion = parcel.readInt()
 | 
			
		||||
        hwMinorVersion = parcel.readInt()
 | 
			
		||||
        hwStorageSize = parcel.readInt()
 | 
			
		||||
        hwProtocol = parcel.readInt()
 | 
			
		||||
 | 
			
		||||
        swVendorID = parcel.readInt()
 | 
			
		||||
        swType = parcel.readInt()
 | 
			
		||||
        swSubType = parcel.readInt()
 | 
			
		||||
        swMajorVersion = parcel.readInt()
 | 
			
		||||
        swMinorVersion = parcel.readInt()
 | 
			
		||||
        swStorageSize = parcel.readInt()
 | 
			
		||||
        swProtocol = parcel.readInt()
 | 
			
		||||
 | 
			
		||||
        uid = parcel.readInt()
 | 
			
		||||
        batchNo = parcel.readInt()
 | 
			
		||||
        weekProd = parcel.readInt()
 | 
			
		||||
        yearProd = parcel.readInt()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun writeToParcel(parcel: Parcel, flags: Int) {
 | 
			
		||||
        parcel.writeInt(hwVendorID)
 | 
			
		||||
        parcel.writeInt(hwType)
 | 
			
		||||
        parcel.writeInt(hwSubType)
 | 
			
		||||
        parcel.writeInt(hwMajorVersion)
 | 
			
		||||
        parcel.writeInt(hwMinorVersion)
 | 
			
		||||
        parcel.writeInt(hwStorageSize)
 | 
			
		||||
        parcel.writeInt(hwProtocol)
 | 
			
		||||
 | 
			
		||||
        parcel.writeInt(swVendorID)
 | 
			
		||||
        parcel.writeInt(swType)
 | 
			
		||||
        parcel.writeInt(swSubType)
 | 
			
		||||
        parcel.writeInt(swMajorVersion)
 | 
			
		||||
        parcel.writeInt(swMinorVersion)
 | 
			
		||||
        parcel.writeInt(swStorageSize)
 | 
			
		||||
        parcel.writeInt(swProtocol)
 | 
			
		||||
 | 
			
		||||
        parcel.writeInt(uid)
 | 
			
		||||
        parcel.writeInt(batchNo)
 | 
			
		||||
        parcel.writeInt(weekProd)
 | 
			
		||||
        parcel.writeInt(yearProd)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun describeContents(): Int {
 | 
			
		||||
        return 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        fun fromXml(element: Element): DesfireManufacturingData {
 | 
			
		||||
            return DesfireManufacturingData(element)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Suppress("unused")
 | 
			
		||||
        @JvmField
 | 
			
		||||
        val CREATOR: Parcelable.Creator<DesfireManufacturingData> =
 | 
			
		||||
            object : Parcelable.Creator<DesfireManufacturingData> {
 | 
			
		||||
                override fun createFromParcel(source: Parcel): DesfireManufacturingData {
 | 
			
		||||
                    return DesfireManufacturingData(source)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                override fun newArray(size: Int): Array<DesfireManufacturingData?> {
 | 
			
		||||
                    return arrayOfNulls(size)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,188 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireProtocol.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.desfire;
 | 
			
		||||
 | 
			
		||||
import android.nfc.tech.IsoDep;
 | 
			
		||||
import com.codebutler.farebot.Utils;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang3.ArrayUtils;
 | 
			
		||||
 | 
			
		||||
public class DesfireProtocol {
 | 
			
		||||
    /* Commands */
 | 
			
		||||
    static final byte GET_MANUFACTURING_DATA    = (byte) 0x60;
 | 
			
		||||
    static final byte GET_APPLICATION_DIRECTORY = (byte) 0x6A;
 | 
			
		||||
    static final byte GET_ADDITIONAL_FRAME      = (byte) 0xAF;
 | 
			
		||||
    static final byte SELECT_APPLICATION        = (byte) 0x5A;
 | 
			
		||||
    static final byte READ_DATA                 = (byte) 0xBD;
 | 
			
		||||
    static final byte READ_RECORD               = (byte) 0xBB;
 | 
			
		||||
    static final byte READ_VALUE                = (byte) 0x6C;
 | 
			
		||||
    static final byte GET_FILES                 = (byte) 0x6F;
 | 
			
		||||
    static final byte GET_FILE_SETTINGS         = (byte) 0xF5;
 | 
			
		||||
 | 
			
		||||
    /* Status codes */
 | 
			
		||||
    static final byte OPERATION_OK      = (byte) 0x00;
 | 
			
		||||
    static final byte PERMISSION_DENIED = (byte) 0x9D;
 | 
			
		||||
    static final byte ADDITIONAL_FRAME  = (byte) 0xAF;
 | 
			
		||||
 | 
			
		||||
    private IsoDep mTagTech;
 | 
			
		||||
 | 
			
		||||
    public DesfireProtocol(IsoDep tagTech) {
 | 
			
		||||
        mTagTech = tagTech;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DesfireManufacturingData getManufacturingData() throws DesfireException {
 | 
			
		||||
        byte[] respBuffer = sendRequest(GET_MANUFACTURING_DATA);
 | 
			
		||||
        
 | 
			
		||||
        if (respBuffer.length != 28)
 | 
			
		||||
            throw new DesfireException("Invalid response");
 | 
			
		||||
 | 
			
		||||
        return new DesfireManufacturingData(respBuffer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int[] getAppList() throws DesfireException {
 | 
			
		||||
        byte[] appDirBuf = sendRequest(GET_APPLICATION_DIRECTORY);
 | 
			
		||||
 | 
			
		||||
        int[] appIds = new int[appDirBuf.length / 3];
 | 
			
		||||
 | 
			
		||||
        for (int app = 0; app < appDirBuf.length; app += 3) {
 | 
			
		||||
            byte[] appId = new byte[3];
 | 
			
		||||
            System.arraycopy(appDirBuf, app, appId, 0, 3);
 | 
			
		||||
 | 
			
		||||
            appIds[app / 3] = Utils.byteArrayToInt(appId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return appIds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void selectApp (int appId) throws DesfireException {
 | 
			
		||||
        byte[] appIdBuff = new byte[3];
 | 
			
		||||
        appIdBuff[0] = (byte) ((appId & 0xFF0000) >> 16);
 | 
			
		||||
        appIdBuff[1] = (byte) ((appId & 0xFF00) >> 8);
 | 
			
		||||
        appIdBuff[2] = (byte) (appId & 0xFF);
 | 
			
		||||
 | 
			
		||||
        sendRequest(SELECT_APPLICATION, appIdBuff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int[] getFileList() throws DesfireException {
 | 
			
		||||
        byte[] buf = sendRequest(GET_FILES);
 | 
			
		||||
        int[] fileIds = new int[buf.length];
 | 
			
		||||
        for (int x = 0; x < buf.length; x++) {
 | 
			
		||||
            fileIds[x] = (int)buf[x];
 | 
			
		||||
        }
 | 
			
		||||
        return fileIds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DesfireFileSettings getFileSettings (int fileNo) throws DesfireException {
 | 
			
		||||
		byte[] data = new byte[0];
 | 
			
		||||
		data = sendRequest(GET_FILE_SETTINGS, new byte[] { (byte) fileNo });
 | 
			
		||||
		return DesfireFileSettings.Create(data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] readFile (int fileNo) throws DesfireException {
 | 
			
		||||
        return sendRequest(READ_DATA, new byte[] {
 | 
			
		||||
            (byte) fileNo,
 | 
			
		||||
            (byte) 0x0, (byte) 0x0, (byte) 0x0,
 | 
			
		||||
            (byte) 0x0, (byte) 0x0, (byte) 0x0
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] readRecord (int fileNum) throws DesfireException {
 | 
			
		||||
        return sendRequest(READ_RECORD, new byte[]{
 | 
			
		||||
                (byte) fileNum,
 | 
			
		||||
                (byte) 0x0, (byte) 0x0, (byte) 0x0,
 | 
			
		||||
                (byte) 0x0, (byte) 0x0, (byte) 0x0
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	public int readValue(int fileNum) throws DesfireException  {
 | 
			
		||||
        byte[] buf = sendRequest(READ_VALUE, new byte[]{
 | 
			
		||||
                (byte) fileNum
 | 
			
		||||
        });
 | 
			
		||||
        ArrayUtils.reverse(buf);
 | 
			
		||||
        return Utils.byteArrayToInt(buf);
 | 
			
		||||
	}
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    private byte[] sendRequest (byte command) throws DesfireException {
 | 
			
		||||
        return sendRequest(command, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private byte[] sendRequest (byte command, byte[] parameters) throws DesfireException {
 | 
			
		||||
        ByteArrayOutputStream output = new ByteArrayOutputStream();
 | 
			
		||||
 | 
			
		||||
		byte[] recvBuffer = new byte[0];
 | 
			
		||||
		try {
 | 
			
		||||
			recvBuffer = mTagTech.transceive(wrapMessage(command, parameters));
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			throw new DesfireException(e);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		while (true) {
 | 
			
		||||
            if (recvBuffer[recvBuffer.length - 2] != (byte) 0x91)
 | 
			
		||||
                throw new DesfireException("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) {
 | 
			
		||||
				try {
 | 
			
		||||
					recvBuffer = mTagTech.transceive(wrapMessage(GET_ADDITIONAL_FRAME, null));
 | 
			
		||||
				} catch (IOException e) {
 | 
			
		||||
					throw new DesfireException(e);
 | 
			
		||||
				}
 | 
			
		||||
			} else if (status == PERMISSION_DENIED) {
 | 
			
		||||
                throw new DesfireException("Permission denied");
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new DesfireException("Unknown status code: " + Integer.toHexString(status & 0xFF));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return output.toByteArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private byte[] wrapMessage (byte command, byte[] parameters) throws DesfireException {
 | 
			
		||||
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
 | 
			
		||||
 | 
			
		||||
        stream.write((byte) 0x90);
 | 
			
		||||
        stream.write(command);
 | 
			
		||||
        stream.write((byte) 0x00);
 | 
			
		||||
        stream.write((byte) 0x00);
 | 
			
		||||
        if (parameters != null) {
 | 
			
		||||
            stream.write((byte) parameters.length);
 | 
			
		||||
			try {
 | 
			
		||||
				stream.write(parameters);
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
				throw new DesfireException(e);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
        stream.write((byte) 0x00);
 | 
			
		||||
 | 
			
		||||
        return stream.toByteArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,213 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DesfireProtocol.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.desfire
 | 
			
		||||
 | 
			
		||||
import android.nfc.tech.IsoDep
 | 
			
		||||
import com.codebutler.farebot.Utils
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang3.ArrayUtils
 | 
			
		||||
import kotlin.experimental.and
 | 
			
		||||
 | 
			
		||||
class DesfireProtocol(private val mTagTech: IsoDep) {
 | 
			
		||||
 | 
			
		||||
    @Suppress("unused")
 | 
			
		||||
    val manufacturingData: DesfireManufacturingData
 | 
			
		||||
        @Throws(DesfireException::class)
 | 
			
		||||
        get() {
 | 
			
		||||
            val respBuffer = sendRequest(GET_MANUFACTURING_DATA)
 | 
			
		||||
 | 
			
		||||
            if (respBuffer.size != 28)
 | 
			
		||||
                throw DesfireException("Invalid response")
 | 
			
		||||
 | 
			
		||||
            return DesfireManufacturingData(respBuffer)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @Suppress("unused")
 | 
			
		||||
    val appList: IntArray
 | 
			
		||||
        @Throws(DesfireException::class)
 | 
			
		||||
        get() {
 | 
			
		||||
            val appDirBuf = sendRequest(GET_APPLICATION_DIRECTORY)
 | 
			
		||||
 | 
			
		||||
            val appIds = IntArray(appDirBuf.size / 3)
 | 
			
		||||
 | 
			
		||||
            var app = 0
 | 
			
		||||
            while (app < appDirBuf.size) {
 | 
			
		||||
                val appId = ByteArray(3)
 | 
			
		||||
                System.arraycopy(appDirBuf, app, appId, 0, 3)
 | 
			
		||||
 | 
			
		||||
                appIds[app / 3] = Utils.byteArrayToInt(appId)
 | 
			
		||||
                app += 3
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return appIds
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    val fileList: IntArray
 | 
			
		||||
        @Throws(DesfireException::class)
 | 
			
		||||
        get() {
 | 
			
		||||
            val buf = sendRequest(GET_FILES)
 | 
			
		||||
            val fileIds = IntArray(buf.size)
 | 
			
		||||
            for (x in buf.indices) {
 | 
			
		||||
                fileIds[x] = buf[x].toInt()
 | 
			
		||||
            }
 | 
			
		||||
            return fileIds
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @Throws(DesfireException::class)
 | 
			
		||||
    fun selectApp(appId: Int) {
 | 
			
		||||
        val appIdBuff = ByteArray(3)
 | 
			
		||||
        appIdBuff[0] = (appId and 0xFF0000 shr 16).toByte()
 | 
			
		||||
        appIdBuff[1] = (appId and 0xFF00 shr 8).toByte()
 | 
			
		||||
        appIdBuff[2] = (appId and 0xFF).toByte()
 | 
			
		||||
 | 
			
		||||
        sendRequest(SELECT_APPLICATION, appIdBuff)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Throws(DesfireException::class)
 | 
			
		||||
    fun getFileSettings(fileNo: Int): DesfireFileSettings {
 | 
			
		||||
        val data = sendRequest(GET_FILE_SETTINGS, byteArrayOf(fileNo.toByte()))
 | 
			
		||||
        return DesfireFileSettings.create(data)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Suppress("unused")
 | 
			
		||||
    @Throws(DesfireException::class)
 | 
			
		||||
    fun readFile(fileNo: Int): ByteArray {
 | 
			
		||||
        return sendRequest(
 | 
			
		||||
            READ_DATA,
 | 
			
		||||
            byteArrayOf(
 | 
			
		||||
                fileNo.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte()
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Suppress("unused")
 | 
			
		||||
    @Throws(DesfireException::class)
 | 
			
		||||
    fun readRecord(fileNum: Int): ByteArray {
 | 
			
		||||
        return sendRequest(
 | 
			
		||||
            READ_RECORD,
 | 
			
		||||
            byteArrayOf(
 | 
			
		||||
                fileNum.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte(),
 | 
			
		||||
                0x0.toByte()
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Throws(DesfireException::class)
 | 
			
		||||
    fun readValue(fileNum: Int): Int {
 | 
			
		||||
        val buf = sendRequest(READ_VALUE, byteArrayOf(fileNum.toByte()))
 | 
			
		||||
        ArrayUtils.reverse(buf)
 | 
			
		||||
        return Utils.byteArrayToInt(buf)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Throws(DesfireException::class)
 | 
			
		||||
    private fun sendRequest(command: Byte, parameters: ByteArray? = null): ByteArray {
 | 
			
		||||
        val output = ByteArrayOutputStream()
 | 
			
		||||
 | 
			
		||||
        var recvBuffer: ByteArray
 | 
			
		||||
        try {
 | 
			
		||||
            recvBuffer = mTagTech.transceive(wrapMessage(command, parameters))
 | 
			
		||||
        } catch (e: IOException) {
 | 
			
		||||
            throw DesfireException(e)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
            if (recvBuffer[recvBuffer.size - 2] != 0x91.toByte())
 | 
			
		||||
                throw DesfireException("Invalid response")
 | 
			
		||||
 | 
			
		||||
            output.write(recvBuffer, 0, recvBuffer.size - 2)
 | 
			
		||||
 | 
			
		||||
            val status = recvBuffer[recvBuffer.size - 1]
 | 
			
		||||
            if (status == OPERATION_OK) {
 | 
			
		||||
                break
 | 
			
		||||
            } else if (status == ADDITIONAL_FRAME) {
 | 
			
		||||
                try {
 | 
			
		||||
                    recvBuffer = mTagTech.transceive(wrapMessage(GET_ADDITIONAL_FRAME, null))
 | 
			
		||||
                } catch (e: IOException) {
 | 
			
		||||
                    throw DesfireException(e)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            } else if (status == PERMISSION_DENIED) {
 | 
			
		||||
                throw DesfireException("Permission denied")
 | 
			
		||||
            } else {
 | 
			
		||||
                throw DesfireException("Unknown status code: " + Integer.toHexString((status and 0xFF.toByte()).toInt()))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return output.toByteArray()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Throws(DesfireException::class)
 | 
			
		||||
    private fun wrapMessage(command: Byte, parameters: ByteArray?): ByteArray {
 | 
			
		||||
        val stream = ByteArrayOutputStream()
 | 
			
		||||
 | 
			
		||||
        stream.write(0x90.toByte().toInt())
 | 
			
		||||
        stream.write(command.toInt())
 | 
			
		||||
        stream.write(0x00.toByte().toInt())
 | 
			
		||||
        stream.write(0x00.toByte().toInt())
 | 
			
		||||
        if (parameters != null) {
 | 
			
		||||
            stream.write(parameters.size.toByte().toInt())
 | 
			
		||||
            try {
 | 
			
		||||
                stream.write(parameters)
 | 
			
		||||
            } catch (e: IOException) {
 | 
			
		||||
                throw DesfireException(e)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        stream.write(0x00.toByte().toInt())
 | 
			
		||||
 | 
			
		||||
        return stream.toByteArray()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        /* Commands */
 | 
			
		||||
        internal const val GET_MANUFACTURING_DATA = 0x60.toByte()
 | 
			
		||||
        internal const val GET_APPLICATION_DIRECTORY = 0x6A.toByte()
 | 
			
		||||
        internal const val GET_ADDITIONAL_FRAME = 0xAF.toByte()
 | 
			
		||||
        internal const val SELECT_APPLICATION = 0x5A.toByte()
 | 
			
		||||
        internal const val READ_DATA = 0xBD.toByte()
 | 
			
		||||
        internal const val READ_RECORD = 0xBB.toByte()
 | 
			
		||||
        internal const val READ_VALUE = 0x6C.toByte()
 | 
			
		||||
        internal const val GET_FILES = 0x6F.toByte()
 | 
			
		||||
        internal const val GET_FILE_SETTINGS = 0xF5.toByte()
 | 
			
		||||
 | 
			
		||||
        /* Status codes */
 | 
			
		||||
        internal const val OPERATION_OK = 0x00.toByte()
 | 
			
		||||
        internal const val PERMISSION_DENIED = 0x9D.toByte()
 | 
			
		||||
        internal const val ADDITIONAL_FRAME = 0xAF.toByte()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -20,16 +20,6 @@
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.codebutler.farebot.card.desfire;
 | 
			
		||||
package com.codebutler.farebot.card.desfire
 | 
			
		||||
 | 
			
		||||
public class DesfireRecord {
 | 
			
		||||
    private byte[] mData;
 | 
			
		||||
 | 
			
		||||
    public DesfireRecord (byte[] data) {
 | 
			
		||||
        mData = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] getData () {
 | 
			
		||||
        return mData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
class DesfireRecord(val data: ByteArray)
 | 
			
		||||
		Reference in New Issue
	
	Block a user