release 0.5.0 #35
@ -25,6 +25,9 @@ android {
 | 
				
			|||||||
            shrinkResources false
 | 
					            shrinkResources false
 | 
				
			||||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
					            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        debug {
 | 
				
			||||||
 | 
					            versionNameSuffix "-debug"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    compileOptions {
 | 
					    compileOptions {
 | 
				
			||||||
@ -48,6 +51,7 @@ dependencies {
 | 
				
			|||||||
    implementation 'com.afollestad.material-dialogs:color:3.1.0'
 | 
					    implementation 'com.afollestad.material-dialogs:color:3.1.0'
 | 
				
			||||||
    implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
 | 
					    implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    implementation 'org.apache.commons:commons-lang3:3.9'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    testImplementation 'junit:junit:4.12'
 | 
					    testImplementation 'junit:junit:4.12'
 | 
				
			||||||
    androidTestImplementation 'androidx.test:runner:1.2.0'
 | 
					    androidTestImplementation 'androidx.test:runner:1.2.0'
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										268
									
								
								app/src/main/java/com/codebutler/farebot/Utils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								app/src/main/java/com/codebutler/farebot/Utils.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,268 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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;
 | 
				
			||||||
 | 
					    }    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					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,135 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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,241 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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,173 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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,188 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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,35 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * DesfireRecord.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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DesfireRecord {
 | 
				
			||||||
 | 
					    private byte[] mData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DesfireRecord (byte[] data) {
 | 
				
			||||||
 | 
					        mData = data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getData () {
 | 
				
			||||||
 | 
					        return mData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -22,9 +22,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package org.mosad.seil0.projectlaogai
 | 
					package org.mosad.seil0.projectlaogai
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Intent
 | 
				
			||||||
import android.graphics.Color
 | 
					import android.graphics.Color
 | 
				
			||||||
 | 
					import android.nfc.NdefMessage
 | 
				
			||||||
import android.nfc.NfcAdapter
 | 
					import android.nfc.NfcAdapter
 | 
				
			||||||
import android.nfc.Tag
 | 
					import android.nfc.Tag
 | 
				
			||||||
 | 
					import android.nfc.tech.IsoDep
 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import android.view.Menu
 | 
					import android.view.Menu
 | 
				
			||||||
import android.view.MenuItem
 | 
					import android.view.MenuItem
 | 
				
			||||||
@ -35,6 +38,7 @@ import androidx.fragment.app.Fragment
 | 
				
			|||||||
import androidx.fragment.app.FragmentTransaction
 | 
					import androidx.fragment.app.FragmentTransaction
 | 
				
			||||||
import com.afollestad.aesthetic.Aesthetic
 | 
					import com.afollestad.aesthetic.Aesthetic
 | 
				
			||||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
					import com.afollestad.materialdialogs.MaterialDialog
 | 
				
			||||||
 | 
					import com.codebutler.farebot.card.desfire.DesfireProtocol
 | 
				
			||||||
import com.google.android.material.navigation.NavigationView
 | 
					import com.google.android.material.navigation.NavigationView
 | 
				
			||||||
import kotlinx.android.synthetic.main.activity_main.*
 | 
					import kotlinx.android.synthetic.main.activity_main.*
 | 
				
			||||||
import kotlinx.android.synthetic.main.app_bar_main.*
 | 
					import kotlinx.android.synthetic.main.app_bar_main.*
 | 
				
			||||||
@ -44,6 +48,10 @@ import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.
 | 
				
			|||||||
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
 | 
					import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
 | 
				
			||||||
import org.mosad.seil0.projectlaogai.fragments.*
 | 
					import org.mosad.seil0.projectlaogai.fragments.*
 | 
				
			||||||
import kotlin.system.measureTimeMillis
 | 
					import kotlin.system.measureTimeMillis
 | 
				
			||||||
 | 
					import com.codebutler.farebot.Utils.selectAppFile
 | 
				
			||||||
 | 
					import com.codebutler.farebot.card.desfire.DesfireFileSettings
 | 
				
			||||||
 | 
					import java.lang.Exception
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO save the current fragment to show it when the app is restarted
 | 
					// TODO save the current fragment to show it when the app is restarted
 | 
				
			||||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
 | 
					class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
 | 
				
			||||||
@ -74,15 +82,49 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
				
			|||||||
        nav_view.setNavigationItemSelectedListener(this)
 | 
					        nav_view.setNavigationItemSelectedListener(this)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO nfc stuff, needs to move to it's own function
 | 
					        // TODO nfc stuff, needs to move to it's own function
 | 
				
			||||||
        if (intent.action == NfcAdapter.ACTION_TECH_DISCOVERED) {
 | 
					        if (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action) {
 | 
				
			||||||
 | 
					            val appId = 0x5F8415
 | 
				
			||||||
 | 
					            val fileId = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
 | 
					            val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val isoDep = IsoDep.get(tag)
 | 
				
			||||||
 | 
					            isoDep.connect()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val card = DesfireProtocol(isoDep)
 | 
				
			||||||
 | 
					            val settings = selectAppFile(card, appId, fileId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (settings is DesfireFileSettings.ValueDesfireFileSettings) {
 | 
				
			||||||
 | 
					                val data = try {
 | 
				
			||||||
 | 
					                    card.readValue(fileId)
 | 
				
			||||||
 | 
					                } catch (ex: Exception) { 0 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                MaterialDialog(this)
 | 
					                MaterialDialog(this)
 | 
				
			||||||
                .title(text = "nfc tag detected")
 | 
					                    .title(text = "Mensa balance")
 | 
				
			||||||
                .message(text = tag.toString())
 | 
					                    .message(text = "current: ${data / 1000}.${(data % 1000) / 10}€\n" +
 | 
				
			||||||
 | 
					                            "latest:    ${settings.value / 1000}.${(settings.value % 1000) / 10}€")
 | 
				
			||||||
                    .show()
 | 
					                    .show()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onNewIntent(intent: Intent) {
 | 
				
			||||||
 | 
					        super.onNewIntent(intent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action) {
 | 
				
			||||||
 | 
					            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_TAG)?.also { rawMessages ->
 | 
				
			||||||
 | 
					                val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }
 | 
				
			||||||
 | 
					                // Process the messages array.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                MaterialDialog(this)
 | 
				
			||||||
 | 
					                    .title(text = "nfc tag detected (onNewIntent)")
 | 
				
			||||||
 | 
					                    .message(text = messages[0].toString())
 | 
				
			||||||
 | 
					                    .show()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onResume() {
 | 
					    override fun onResume() {
 | 
				
			||||||
        super.onResume()
 | 
					        super.onResume()
 | 
				
			||||||
 | 
				
			|||||||
@ -25,10 +25,10 @@
 | 
				
			|||||||
        <license>Apache Software License 2.0</license>
 | 
					        <license>Apache Software License 2.0</license>
 | 
				
			||||||
    </notice>
 | 
					    </notice>
 | 
				
			||||||
    <notice>
 | 
					    <notice>
 | 
				
			||||||
        <name>Android Sliding Up Panel</name>
 | 
					        <name>farebot part for desfire cards</name>
 | 
				
			||||||
        <url>https://github.com/umano/AndroidSlidingUpPanel</url>
 | 
					        <url>https://github.com/codebutler/farebot</url>
 | 
				
			||||||
        <copyright />
 | 
					        <copyright />
 | 
				
			||||||
        <license>Apache Software License 2.0</license>
 | 
					        <license>GNU General Public License v3.0</license>
 | 
				
			||||||
    </notice>
 | 
					    </notice>
 | 
				
			||||||
    <notice>
 | 
					    <notice>
 | 
				
			||||||
        <name>Android Support Libraries</name>
 | 
					        <name>Android Support Libraries</name>
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user