├── .classpath
├── .gitattributes
├── .gitignore
├── .project
├── AndroidManifest.xml
├── README
├── project.properties
├── res
├── drawable-hdpi
│ ├── ic_icc_off.png
│ ├── ic_icc_on.png
│ ├── ic_icc_on_atr.png
│ ├── ic_main_icon.png
│ ├── ic_nfc_off.png
│ └── ic_nfc_on.png
├── drawable-ldpi
│ ├── ic_icc_off.png
│ ├── ic_icc_on.png
│ ├── ic_icc_on_atr.png
│ ├── ic_main_icon.png
│ ├── ic_nfc_off.png
│ └── ic_nfc_on.png
├── drawable-mdpi
│ ├── ic_icc_off.png
│ ├── ic_icc_on.png
│ ├── ic_icc_on_atr.png
│ ├── ic_main_icon.png
│ ├── ic_nfc_off.png
│ └── ic_nfc_on.png
├── drawable-xhdpi
│ ├── ic_icc_off.png
│ ├── ic_icc_on.png
│ ├── ic_icc_on_atr.png
│ ├── ic_main_icon.png
│ ├── ic_nfc_off.png
│ └── ic_nfc_on.png
├── layout
│ └── main.xml
├── values
│ └── strings.xml
└── xml
│ ├── hexkbd.xml
│ └── nfc_tech_filter.xml
└── src
└── com
└── jmarroyo
└── apdusendercontactless
├── ApduSenderContactLess.java
├── Ber_Tlv.java
├── EmvInterpreter.java
├── HexadecimalKbd.java
├── RetStatusWord.java
└── Util.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # =========================
18 | # Operating System Files
19 | # =========================
20 |
21 | # OSX
22 | # =========================
23 |
24 | .DS_Store
25 | .AppleDouble
26 | .LSOverride
27 |
28 | # Icon must ends with two \r.
29 | Icon
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ApduSenderContactLess
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | ApduSenderContactLess - Android software
2 | ---------------------------------------
3 |
4 | * Introduction:
5 |
6 | * Requirements:
7 | Android NFC phone
8 |
9 | * License: distributed under GPL version 3 (http://www.gnu.org/licenses/gpl.html)
10 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-14
12 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_icc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-hdpi/ic_icc_off.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_icc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-hdpi/ic_icc_on.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_icc_on_atr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-hdpi/ic_icc_on_atr.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_main_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-hdpi/ic_main_icon.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_nfc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-hdpi/ic_nfc_off.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_nfc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-hdpi/ic_nfc_on.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_icc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-ldpi/ic_icc_off.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_icc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-ldpi/ic_icc_on.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_icc_on_atr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-ldpi/ic_icc_on_atr.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_main_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-ldpi/ic_main_icon.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_nfc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-ldpi/ic_nfc_off.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_nfc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-ldpi/ic_nfc_on.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_icc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-mdpi/ic_icc_off.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_icc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-mdpi/ic_icc_on.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_icc_on_atr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-mdpi/ic_icc_on_atr.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_main_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-mdpi/ic_main_icon.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_nfc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-mdpi/ic_nfc_off.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_nfc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-mdpi/ic_nfc_on.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_icc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-xhdpi/ic_icc_off.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_icc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-xhdpi/ic_icc_on.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_icc_on_atr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-xhdpi/ic_icc_on_atr.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_main_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-xhdpi/ic_main_icon.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_nfc_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-xhdpi/ic_nfc_off.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_nfc_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jmarroyo/ApduSenderContactLess/d73ea1f498a816c09300fd904b3efeaf79cf777a/res/drawable-xhdpi/ic_nfc_on.png
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
14 |
19 |
25 |
26 |
27 |
28 |
32 |
37 |
43 |
44 |
45 |
46 |
50 |
54 |
60 |
61 |
70 |
71 |
72 |
76 |
82 |
83 |
92 |
93 |
94 |
98 |
104 |
105 |
114 |
115 |
116 |
120 |
126 |
127 |
136 |
137 |
138 |
139 |
143 |
149 |
150 |
159 |
165 |
166 |
175 |
176 |
177 |
183 |
184 |
193 |
197 |
198 |
199 |
204 |
205 |
211 |
212 |
218 |
219 |
220 |
221 |
226 |
233 |
234 |
235 |
240 |
241 |
248 |
255 |
262 |
263 |
264 |
269 |
273 |
274 |
275 |
276 |
285 |
286 |
287 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | APDU Sender Contactless
4 | NO READER DETECTED
5 | PLEASE TAP CARD
6 | CLA:
7 | INS:
8 | P1:
9 | P2:
10 | Lc:
11 | Le:
12 | Data:
13 | RAW C-APDU
14 | TLV Interpretation
15 | Send
16 | Clear Log
17 | Set NFC
18 | Paste
19 |
20 |
--------------------------------------------------------------------------------
/res/xml/hexkbd.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/res/xml/nfc_tech_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | android.nfc.tech.IsoDep
5 | android.nfc.tech.NfcA
6 | android.nfc.tech.NfcB
7 | android.nfc.tech.NfcF
8 | android.nfc.tech.NfcV
9 | android.nfc.tech.Ndef
10 | android.nfc.tech.NdefFormatable
11 | android.nfc.tech.MifareClassic
12 | android.nfc.tech.MifareUltralight
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/com/jmarroyo/apdusendercontactless/ApduSenderContactLess.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2014 Jose Maria ARROYO jm.arroyo.castejon@gmail.com
3 |
4 | APDUSenderContactLess is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by the
6 | Free Software Foundation, either version 3 of the License, or (at your option)
7 | any later version.
8 |
9 | APDUSenderContactLess is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
17 | */
18 |
19 | package com.jmarroyo.apdusendercontactless;
20 |
21 |
22 | import java.io.IOException;
23 | import java.util.Arrays;
24 |
25 | import android.app.Activity;
26 | import android.app.PendingIntent;
27 | import android.content.ClipboardManager;
28 | import android.content.Context;
29 | import android.content.Intent;
30 | import android.content.IntentFilter;
31 | import android.os.Bundle;
32 | import android.view.View;
33 | import android.view.View.OnClickListener;
34 | import android.widget.AdapterView;
35 | import android.widget.AdapterView.OnItemSelectedListener;
36 | import android.widget.ArrayAdapter;
37 | import android.widget.Button;
38 | import android.widget.CheckBox;
39 | import android.widget.CompoundButton;
40 | import android.widget.CompoundButton.OnCheckedChangeListener;
41 | import android.widget.EditText;
42 | import android.widget.ImageView;
43 | import android.widget.Spinner;
44 | import android.widget.TextView;
45 | import android.widget.Toast;
46 | import android.content.IntentFilter.MalformedMimeTypeException;
47 | import android.nfc.NfcAdapter;
48 | import android.nfc.Tag;
49 | import android.nfc.tech.IsoDep;
50 | import android.os.Parcelable;
51 |
52 | public class ApduSenderContactLess extends Activity
53 | {
54 |
55 | static byte[] byteAPDU=null;
56 | static byte[] respAPDU=null;
57 |
58 | static HexadecimalKbd mHexKbd;
59 |
60 | private static CheckBox mCheckRaw;
61 | private static CheckBox mCheckResp;
62 |
63 | private Button mSendAPDUButton;
64 | private Button mClearLogButton;
65 | private Button mSetNFCButton;
66 | private Button mPasteButton;
67 |
68 | static ImageView icoNfc;
69 | static ImageView icoCard;
70 |
71 | static TextView TextNfc;
72 | static TextView TextCard;
73 |
74 | static TextView txtCLA;
75 | static TextView txtINS;
76 | static TextView txtP1;
77 | static TextView txtP2;
78 | static TextView txtLc;
79 | static TextView txtDataIn;
80 | static TextView txtLe;
81 |
82 | static EditText editCLA;
83 | static EditText editINS;
84 | static EditText editP1;
85 | static EditText editP2;
86 | static EditText editLc;
87 | static EditText editDataIn;
88 | static EditText editLe;
89 |
90 | static TextView txtLog;
91 |
92 | private Spinner mCommandsSpinner;
93 |
94 | private NfcAdapter mAdapter=null;
95 | private PendingIntent mPendingIntent;
96 | private String[][] mTechLists;
97 | private IntentFilter[] mFilters;
98 | static IsoDep myTag;
99 | boolean mFirstDetected=false;
100 | boolean mShowAtr=false;
101 |
102 |
103 |
104 | @Override
105 | public void onCreate(Bundle savedInstanceState)
106 | {
107 | super.onCreate(savedInstanceState);
108 | setContentView(R.layout.main);
109 |
110 | byteAPDU=null;
111 | respAPDU=null;
112 |
113 | mHexKbd= new HexadecimalKbd(this, R.id.keyboardview, R.xml.hexkbd );
114 | mHexKbd.registerEditText(R.id.editCLA);
115 | mHexKbd.registerEditText(R.id.editINS);
116 | mHexKbd.registerEditText(R.id.editP1);
117 | mHexKbd.registerEditText(R.id.editP2);
118 | mHexKbd.registerEditText(R.id.editLc);
119 | mHexKbd.registerEditText(R.id.editDataIn);
120 | mHexKbd.registerEditText(R.id.editLe);
121 |
122 | txtLog = (TextView) findViewById(R.id.textLog);
123 | icoNfc = (ImageView) findViewById(R.id.imageNfc);
124 | icoNfc.setImageResource(R.drawable.ic_nfc_off);
125 | icoCard = (ImageView) findViewById(R.id.imageCard);
126 | icoCard.setImageResource(R.drawable.ic_icc_off);
127 | TextNfc = (TextView) findViewById(R.id.textNfc);
128 | TextCard = (TextView) findViewById(R.id.textCard);
129 |
130 | txtCLA = (TextView) findViewById(R.id.textCLA);
131 | txtINS = (TextView) findViewById(R.id.textINS);
132 | txtP1 = (TextView) findViewById(R.id.textP1);
133 | txtP2 = (TextView) findViewById(R.id.textP2);
134 | txtLc = (TextView) findViewById(R.id.textLc);
135 | txtDataIn = (TextView) findViewById(R.id.textDataIn);
136 | txtLe = (TextView) findViewById(R.id.textLe);
137 |
138 | editCLA = (EditText) findViewById(R.id.editCLA);
139 | editINS = (EditText) findViewById(R.id.editINS);
140 | editP1 = (EditText) findViewById(R.id.editP1);
141 | editP2 = (EditText) findViewById(R.id.editP2);
142 | editLc = (EditText) findViewById(R.id.editLc);
143 | editDataIn = (EditText) findViewById(R.id.editDataIn);
144 | editLe = (EditText) findViewById(R.id.editLe);
145 |
146 | mSendAPDUButton = (Button) findViewById(R.id.button_SendApdu);
147 | mSendAPDUButton.setOnClickListener(new OnClickListener()
148 | {
149 | @Override
150 | public void onClick(View v)
151 | {
152 | if( mFirstDetected==true && myTag.isConnected())
153 | {
154 | if(mShowAtr==true)
155 | {
156 | icoCard.setImageResource(R.drawable.ic_icc_on_atr);
157 | }
158 | else
159 | {
160 | icoCard.setImageResource(R.drawable.ic_icc_on);
161 | }
162 | clearlog();
163 | if(!bSendAPDU())
164 | {
165 | vShowErrorVaules();
166 | }
167 | }
168 | else
169 | {
170 | icoCard.setImageResource(R.drawable.ic_icc_off);
171 | clearlog();
172 | TextCard.setText("PLEASE TAP CARD");
173 | editCLA.setText("");
174 | editINS.setText("");
175 | editP1.setText("");
176 | editP2.setText("");
177 | editLc.setText("");
178 | editLe.setText("");
179 | editDataIn.setText("");
180 | editCLA.requestFocus();
181 | mSendAPDUButton.setEnabled(false);
182 | }
183 | }
184 | });
185 |
186 |
187 | mClearLogButton = (Button) findViewById(R.id.button_ClearLog);
188 | mClearLogButton.setOnClickListener(new OnClickListener()
189 | {
190 | @Override
191 | public void onClick(View v)
192 | {
193 | if( mFirstDetected==true && myTag.isConnected() )
194 | {
195 | if(mShowAtr==true)
196 | {
197 | icoCard.setImageResource(R.drawable.ic_icc_on_atr);
198 | }
199 | else
200 | {
201 | icoCard.setImageResource(R.drawable.ic_icc_on);
202 | }
203 | }
204 | else
205 | {
206 | icoCard.setImageResource(R.drawable.ic_icc_off);
207 | TextCard.setText("PLEASE TAP CARD");
208 | editCLA.setText("");
209 | editINS.setText("");
210 | editP1.setText("");
211 | editP2.setText("");
212 | editLc.setText("");
213 | editLe.setText("");
214 | editDataIn.setText("");
215 | editCLA.requestFocus();
216 | mSendAPDUButton.setEnabled(false);
217 | }
218 | clearlog();
219 | }
220 | });
221 |
222 | mSetNFCButton = (Button) findViewById(R.id.button_SetNFC);
223 | mSetNFCButton.setOnClickListener(new OnClickListener()
224 | {
225 | @Override
226 | public void onClick(View v)
227 | {
228 | if( mFirstDetected==true && myTag.isConnected() )
229 | {
230 | if(mShowAtr==true)
231 | {
232 | icoCard.setImageResource(R.drawable.ic_icc_on_atr);
233 | }
234 | else
235 | {
236 | icoCard.setImageResource(R.drawable.ic_icc_on);
237 | }
238 | }
239 | else
240 | {
241 | icoCard.setImageResource(R.drawable.ic_icc_off);
242 | clearlog();
243 | TextCard.setText("PLEASE TAP CARD");
244 | editCLA.setText("");
245 | editINS.setText("");
246 | editP1.setText("");
247 | editP2.setText("");
248 | editLc.setText("");
249 | editLe.setText("");
250 | editDataIn.setText("");
251 | editCLA.requestFocus();
252 | mSendAPDUButton.setEnabled(false);
253 | }
254 | clearlog();
255 | startActivityForResult(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS), 0);
256 | }
257 | });
258 |
259 | mPasteButton = (Button) findViewById(R.id.button_Paste);
260 | mPasteButton.setOnClickListener(new OnClickListener()
261 | {
262 | @Override
263 | public void onClick(View v)
264 | {
265 | ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
266 | String ClipBoardData = clipboard.getText().toString().toUpperCase();
267 |
268 | if(ClipBoardData.length() > 254)
269 | {
270 | vShowGeneralMesg("Max Length to Paste is 254 chars !");
271 | }
272 | else if(ClipBoardData.length() >= 8)
273 | {
274 | if( (ClipBoardData.length()%2)!=0)
275 | {
276 | vShowGeneralMesg("String Length must be Even !");
277 | }
278 | if (!ClipBoardData.matches("^[0-9A-F]+$"))
279 | {
280 | clearlog();
281 | print(ClipBoardData);
282 | vShowGeneralMesg("String should be '0'-'9' or 'A'-'F'");
283 | }
284 | else
285 | {
286 | vSetBuiltinCommand();
287 | editDataIn.setText(ClipBoardData);
288 | HideKbd();
289 | vShowGeneralMesg("Data Pasted Successfully");
290 | }
291 | }
292 | else
293 | {
294 | vShowGeneralMesg("Length must be greater than 8 chars !");
295 | }
296 | }
297 | });
298 |
299 | mCheckRaw = (CheckBox) findViewById(R.id.check_box_raw);
300 | mCheckRaw.setOnCheckedChangeListener(new OnCheckedChangeListener()
301 | {
302 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
303 | {
304 | if( mFirstDetected==true && myTag.isConnected() );
305 | else
306 | {
307 | icoCard.setImageResource(R.drawable.ic_icc_off);
308 | clearlog();
309 | mSendAPDUButton.setEnabled(false);
310 | TextCard.setText("PLEASE TAP CARD");
311 | }
312 | if( isChecked )
313 | {
314 | editCLA.setText("");
315 | editINS.setText("");
316 | editP1.setText("");
317 | editP2.setText("");
318 | editLc.setText("");
319 | editLe.setText("");
320 | editDataIn.setText("");
321 |
322 | txtCLA.setEnabled(false);
323 | txtINS.setEnabled(false);
324 | txtP1.setEnabled(false);
325 | txtP2.setEnabled(false);
326 | txtLc.setEnabled(false);
327 | txtLe.setEnabled(false);
328 | editCLA.setEnabled(false);
329 | editINS.setEnabled(false);
330 | editP1.setEnabled(false);
331 | editP2.setEnabled(false);
332 | editLc.setEnabled(false);
333 | editLe.setEnabled(false);
334 | editDataIn.setEnabled(true);
335 | txtDataIn.setEnabled(true);
336 | editDataIn.requestFocus();
337 | txtDataIn.setText("APDU:");
338 | }
339 | else
340 | {
341 | editCLA.setText("");
342 | editINS.setText("");
343 | editP1.setText("");
344 | editP2.setText("");
345 | editLc.setText("");
346 | editLe.setText("");
347 | editDataIn.setText("");
348 |
349 | txtCLA.setEnabled(true);
350 | txtINS.setEnabled(true);
351 | txtP1.setEnabled(true);
352 | txtP2.setEnabled(true);
353 | txtLc.setEnabled(true);
354 | txtDataIn.setEnabled(true);
355 | txtLe.setEnabled(true);
356 | txtLe.setEnabled(true);
357 | editCLA.setEnabled(true);
358 | editINS.setEnabled(true);
359 | editP1.setEnabled(true);
360 | editP2.setEnabled(true);
361 | editLc.setEnabled(true);
362 | editDataIn.setEnabled(true);
363 | txtDataIn.setText("Data:");
364 | editLe.setEnabled(true);
365 | mCommandsSpinner.setSelection(0);
366 | editCLA.requestFocus();
367 | }
368 | }
369 | });
370 |
371 | mCheckResp = (CheckBox) findViewById(R.id.check_box_resp);
372 | mCheckResp.setOnCheckedChangeListener(new OnCheckedChangeListener()
373 | {
374 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
375 | {
376 | if( (mFirstDetected==true) && (myTag.isConnected()) );
377 | else
378 | {
379 | icoCard.setImageResource(R.drawable.ic_icc_off);
380 | clearlog();
381 | mSendAPDUButton.setEnabled(false);
382 | TextCard.setText("PLEASE TAP CARD");
383 | return;
384 | }
385 | if( (byteAPDU==null)||(respAPDU==null) )
386 | {
387 | return;
388 | }
389 | if( isChecked )
390 | {
391 | clearlog();
392 | print("***COMMAND APDU***");
393 | print("");
394 | try
395 | {
396 | print("IFD - " + getHexString(byteAPDU));
397 | }
398 | catch (Exception e)
399 | {
400 | e.printStackTrace();
401 | }
402 | try
403 | {
404 | print("");
405 | print("ICC - " + getHexString(respAPDU));
406 | }
407 | catch (Exception e)
408 | {
409 | e.printStackTrace();
410 | }
411 | if(mCheckResp.isChecked())
412 | {
413 | try
414 | {
415 | vShowResponseInterpretation(respAPDU);
416 | }
417 | catch (Exception e)
418 | {
419 | clearlog();
420 | print("Response is not TLV format !!!");
421 | }
422 | }
423 |
424 | }
425 | else
426 | {
427 | clearlog();
428 | print("***COMMAND APDU***");
429 | print("");
430 | try
431 | {
432 | print("IFD - " + getHexString(byteAPDU));
433 | }
434 | catch (Exception e)
435 | {
436 | e.printStackTrace();
437 | }
438 | try
439 | {
440 | print("");
441 | print("ICC - " + getHexString(respAPDU));
442 | }
443 | catch (Exception e)
444 | {
445 | e.printStackTrace();
446 | }
447 | }
448 | }
449 | });
450 |
451 |
452 | final String[] commadsTableNames = {
453 | "Built-in APDUs...",
454 | "SELECT PSE",
455 | "SELECT PPSE",
456 | "SELECT VISA AID",
457 | "SELECT VISA ELECTRON AID",
458 | "SELECT MASTERCARD AID",
459 | "SELECT AMEX AID",
460 | "SELECT DINERS/DISCOVER AID",
461 | "SELECT INTERAC AID",
462 | "SELECT CUP AID",
463 | "READ RECORD SFI:01 R:01",
464 | "READ RECORD SFI:01 R:02",
465 | "READ RECORD SFI:02 R:01",
466 | "READ RECORD SFI:02 R:02",
467 | "GET ATC",
468 | "GET LAST ONLINE ATC",
469 | "GET PIN TRY COUNTER"
470 | };
471 | ArrayAdapter commadsTable = new ArrayAdapter(this,android.R.layout.simple_spinner_item, commadsTableNames);
472 | mCommandsSpinner = (Spinner) findViewById(R.id.APDU_spinner_table);
473 | mCommandsSpinner.setAdapter(commadsTable);
474 | mCommandsSpinner.setSelection(0);
475 | mCommandsSpinner.setOnItemSelectedListener(new OnItemSelectedListener()
476 | {
477 | @Override
478 | public void onItemSelected(AdapterView> arg0, View arg1,int arg2, long arg3)
479 | {
480 |
481 | int CommandAPDU = mCommandsSpinner.getSelectedItemPosition();
482 | switch (CommandAPDU)
483 | {
484 | case 0:
485 |
486 | mCheckRaw.setChecked(false);
487 | editCLA.setText("");
488 | editINS.setText("");
489 | editP1.setText("");
490 | editP2.setText("");
491 | editLc.setText("");
492 | editLe.setText("");
493 | editDataIn.setText("");
494 |
495 | txtCLA.setEnabled(true);
496 | txtINS.setEnabled(true);
497 | txtP1.setEnabled(true);
498 | txtP2.setEnabled(true);
499 | txtLc.setEnabled(true);
500 | txtDataIn.setEnabled(true);
501 | txtLe.setEnabled(true);
502 | txtLe.setEnabled(true);
503 | editCLA.setEnabled(true);
504 | editINS.setEnabled(true);
505 | editP1.setEnabled(true);
506 | editP2.setEnabled(true);
507 | editLc.setEnabled(true);
508 | editDataIn.setEnabled(true);
509 | txtDataIn.setText("Data:");
510 | editLe.setEnabled(true);
511 | editCLA.requestFocus();
512 |
513 | break;
514 | case 1: //SELECT PSE
515 | vSetBuiltinCommand();
516 | editDataIn.setText("00A404000E315041592E5359532E4444463031");
517 | HideKbd();
518 | vShowGeneralMesg("Payment System Environment");
519 | break;
520 | case 2: //SELECT PPSE
521 | vSetBuiltinCommand();
522 | editDataIn.setText("00A404000E325041592E5359532E4444463031");
523 | HideKbd();
524 | vShowGeneralMesg("Proximity Payment System Environment");
525 | break;
526 | case 3: //SELECT VISA AID
527 | vSetBuiltinCommand();
528 | editDataIn.setText("00A4040007A0000000031010");
529 | HideKbd();
530 | vShowGeneralMesg("Visa credit or debit");
531 | break;
532 | case 4: //SELECT VISA ELECTRON AID
533 | vSetBuiltinCommand();
534 | editDataIn.setText("00A4040007A0000000032010");
535 | HideKbd();
536 | vShowGeneralMesg("Visa Electron");
537 | break;
538 | case 5: //SELECT MASTERCARD AID
539 | vSetBuiltinCommand();
540 | editDataIn.setText("00A4040007A0000000041010");
541 | HideKbd();
542 | vShowGeneralMesg("MasterCard credit or debit");
543 | break;
544 | case 6: //SELECT AMEX AID
545 | vSetBuiltinCommand();
546 | editDataIn.setText("00A4040006A00000002501");
547 | HideKbd();
548 | vShowGeneralMesg("American Express");
549 | break;
550 | case 7: //SELECT DINERS/DISCOVER AID
551 | vSetBuiltinCommand();
552 | editDataIn.setText("00A4040007A0000001523010");
553 | HideKbd();
554 | vShowGeneralMesg("Diners Club/Discover");
555 | break;
556 | case 8: //SELECT INTERAC AID
557 | vSetBuiltinCommand();
558 | editDataIn.setText("00A4040007A0000002771010");
559 | HideKbd();
560 | vShowGeneralMesg("Interac Debit card");
561 | break;
562 | case 9: //SELECT CUP AID
563 | vSetBuiltinCommand();
564 | editDataIn.setText("00A4040008A000000333010101");
565 | HideKbd();
566 | vShowGeneralMesg("UnionPay Debit");
567 | break;
568 | case 10: //READRECORD SFI:01 R:01
569 | vSetBuiltinCommand();
570 | editDataIn.setText("00B2010C00");
571 | HideKbd();
572 | vShowGeneralMesg("SFI:01 R:01");
573 | break;
574 | case 11: //READRECORD SFI:01 R:02
575 | vSetBuiltinCommand();
576 | editDataIn.setText("00B2020C00");
577 | HideKbd();
578 | vShowGeneralMesg("SFI:01 R:02");
579 | break;
580 | case 12: //READRECORD SFI:02 R:01
581 | vSetBuiltinCommand();
582 | editDataIn.setText("00B2011400");
583 | HideKbd();
584 | vShowGeneralMesg("SFI:02 R:01");
585 | break;
586 | case 13: //READRECORD SFI:02 R:02
587 | vSetBuiltinCommand();
588 | editDataIn.setText("00B2021400");
589 | HideKbd();
590 | vShowGeneralMesg("SFI:02 R:02");
591 | break;
592 | case 14: //GET ATC
593 | vSetBuiltinCommand();
594 | editDataIn.setText("80CA9F3600");
595 | HideKbd();
596 | vShowGeneralMesg("Get Tag 9F36");
597 | break;
598 | case 15: //GET LAST ONLINE ATC
599 | vSetBuiltinCommand();
600 | editDataIn.setText("80CA9F1300");
601 | HideKbd();
602 | vShowGeneralMesg("Get Tag 9F13");
603 | break;
604 | case 16: //GET PIN TRY COUNTER
605 | vSetBuiltinCommand();
606 | editDataIn.setText("80CA9F1700");
607 | HideKbd();
608 | vShowGeneralMesg("Get Tag 9F17");
609 | break;
610 |
611 |
612 | default:
613 | break;
614 | }
615 | }
616 | @Override
617 | public void onNothingSelected(AdapterView> arg0)
618 | {
619 | return;
620 | }
621 | });
622 |
623 |
624 | mSendAPDUButton.setEnabled(false);
625 | mClearLogButton.setEnabled(true);
626 | mSetNFCButton.setEnabled(true);
627 | mCommandsSpinner.setEnabled(true);
628 |
629 | editCLA.setText("");
630 | editINS.setText("");
631 | editP1.setText("");
632 | editP2.setText("");
633 | editLc.setText("");
634 | editLe.setText("");
635 | editDataIn.setText("");
636 |
637 | txtCLA.setEnabled(true);
638 | txtINS.setEnabled(true);
639 | txtP1.setEnabled(true);
640 | txtP2.setEnabled(true);
641 | txtLc.setEnabled(true);
642 | txtDataIn.setEnabled(true);
643 | txtLe.setEnabled(true);
644 | txtLe.setEnabled(true);
645 | editCLA.setEnabled(true);
646 | editINS.setEnabled(true);
647 | editP1.setEnabled(true);
648 | editP2.setEnabled(true);
649 | editLc.setEnabled(true);
650 | editDataIn.setEnabled(true);
651 | txtDataIn.setText("Data:");
652 | editLe.setEnabled(true);
653 | editCLA.requestFocus();
654 |
655 | resolveIntent(getIntent());
656 |
657 | mAdapter = NfcAdapter.getDefaultAdapter(this);
658 | mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
659 | IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
660 | try
661 | {
662 | ndef.addDataType("*/*");
663 | }
664 | catch (MalformedMimeTypeException e)
665 | {
666 | throw new RuntimeException("fail", e);
667 | }
668 | mFilters = new IntentFilter[] { ndef, };
669 | mTechLists = new String[][] { new String[] { IsoDep.class.getName() } };
670 | }
671 |
672 |
673 | @Override
674 | public void onResume()
675 | {
676 | super.onResume();
677 |
678 | byteAPDU=null;
679 | respAPDU=null;
680 |
681 | if( (mFirstDetected==true) && (myTag.isConnected()) )
682 | {
683 | if(mShowAtr==true)
684 | {
685 | icoCard.setImageResource(R.drawable.ic_icc_on_atr);
686 | }
687 | else
688 | {
689 | icoCard.setImageResource(R.drawable.ic_icc_on);
690 | }
691 | }
692 | else
693 | {
694 | icoCard.setImageResource(R.drawable.ic_icc_off);
695 | }
696 | if( (mAdapter == null) || (!mAdapter.isEnabled()) )
697 | {
698 | if (mAdapter == null)
699 | {
700 | clearlog();
701 | TextCard.setText("PLEASE TAP CARD");
702 | mSendAPDUButton.setEnabled(false);
703 | mSetNFCButton.setEnabled(false);
704 | editCLA.setText("");
705 | editINS.setText("");
706 | editP1.setText("");
707 | editP2.setText("");
708 | editLc.setText("");
709 | editLe.setText("");
710 | editDataIn.setText("");
711 | editCLA.requestFocus();
712 | print(" No NFC hardware found.");
713 | print(" Program will NOT function.");
714 | }
715 | else if(mAdapter.isEnabled())
716 | {
717 | clearlog();
718 | TextNfc.setText("NFC ENABLED");
719 | }
720 | else
721 | {
722 | clearlog();
723 | TextCard.setText("PLEASE TAP CARD");
724 | editCLA.setText("");
725 | editINS.setText("");
726 | editP1.setText("");
727 | editP2.setText("");
728 | editLc.setText("");
729 | editLe.setText("");
730 | editDataIn.setText("");
731 | editCLA.requestFocus();
732 | mSendAPDUButton.setEnabled(false);
733 | print(" NFC hardware has been disabled.");
734 | print(" Please enable it first.");
735 | mSetNFCButton.setEnabled(true);
736 | icoNfc.setImageResource(R.drawable.ic_nfc_off);
737 | TextNfc.setText("NO READER DETECTED");
738 | }
739 | }
740 | if (mAdapter != null)
741 | {
742 | if (mAdapter.isEnabled())
743 | {
744 | clearlog();
745 | TextNfc.setText("NFC ENABLED");
746 | icoNfc.setImageResource(R.drawable.ic_nfc_on);
747 | print("This program is distributed in the hope that it will be useful for educational purposes. Enjoy! ");
748 | }
749 | mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
750 | }
751 | else
752 | {
753 | clearlog();
754 | icoNfc.setImageResource(R.drawable.ic_nfc_off);
755 | TextNfc.setText("NO READER DETECTED");
756 | TextCard.setText("PLEASE TAP CARD");
757 | mSendAPDUButton.setEnabled(false);
758 | mSetNFCButton.setEnabled(false);
759 | editCLA.setText("");
760 | editINS.setText("");
761 | editP1.setText("");
762 | editP2.setText("");
763 | editLc.setText("");
764 | editLe.setText("");
765 | editDataIn.setText("");
766 | editCLA.requestFocus();
767 | print(" No NFC hardware found.");
768 | print(" Program will NOT function.");
769 | }
770 | }
771 |
772 | @Override
773 | public void onPause()
774 | {
775 | super.onPause();
776 |
777 | byteAPDU=null;
778 | respAPDU=null;
779 |
780 | if( (mFirstDetected==true) && (myTag.isConnected()) )
781 | {
782 | if(mShowAtr==true)
783 | {
784 | icoCard.setImageResource(R.drawable.ic_icc_on_atr);
785 | }
786 | else
787 | {
788 | icoCard.setImageResource(R.drawable.ic_icc_on);
789 | }
790 | }
791 | else
792 | {
793 | icoCard.setImageResource(R.drawable.ic_icc_off);
794 | }
795 | mAdapter.disableForegroundDispatch(this);
796 | }
797 |
798 | @Override
799 | protected void onNewIntent(Intent intent)
800 | {
801 | setIntent(intent);
802 | resolveIntent(intent);
803 | }
804 |
805 | @Override public void onBackPressed()
806 | {
807 | if( mHexKbd.isCustomKeyboardVisible() ) mHexKbd.hideCustomKeyboard(); else this.finish();
808 | }
809 |
810 | public static void HideKbd()
811 | {
812 | if( mHexKbd.isCustomKeyboardVisible() ) mHexKbd.hideCustomKeyboard();
813 | }
814 |
815 | private static void clearlog()
816 | {
817 | txtLog.setText("");
818 | }
819 |
820 | private static void print(String s)
821 | {
822 | txtLog.append(s);
823 | txtLog.append("\r\n");
824 | return;
825 | }
826 |
827 | private static byte[] transceives (byte[] data)
828 | {
829 | byte[] ra = null;
830 |
831 | try
832 | {
833 | print("***COMMAND APDU***");
834 | print("");
835 | print("IFD - " + getHexString(data));
836 | }
837 | catch (Exception e1)
838 | {
839 | e1.printStackTrace();
840 | }
841 |
842 | try
843 | {
844 | ra = myTag.transceive(data);
845 | }
846 | catch (IOException e)
847 | {
848 |
849 | print("************************************");
850 | print(" NO CARD RESPONSE");
851 | print("************************************");
852 |
853 | }
854 | try
855 | {
856 | print("");
857 | print("ICC - " + getHexString(ra));
858 | }
859 | catch (Exception e1)
860 | {
861 | e1.printStackTrace();
862 | }
863 |
864 | return (ra);
865 | }
866 |
867 | private static boolean bSendAPDU()
868 | {
869 | HideKbd();
870 |
871 | String StringAPDU = null;
872 |
873 | String StringCLA = editCLA.getText().toString();
874 | String StringINS = editINS.getText().toString();
875 | String StringP1 = editP1.getText().toString();
876 | String StringP2 = editP2.getText().toString();
877 | String StringLc = editLc.getText().toString();
878 | String StringDataIn = editDataIn.getText().toString();
879 | String StringLe = editLe.getText().toString();
880 |
881 | if (!mCheckRaw.isChecked())
882 | {
883 | if ( (StringCLA.length()==0)||(StringINS.length()==0)||(StringP1.length()==0)||(StringP2.length()==0)||( (StringDataIn.length()%2)!=0 ) )
884 | {
885 | return false;
886 | }
887 | if(!StringLc.contentEquals(""))
888 | {
889 | if( StringDataIn.length() != (((int) Long.parseLong(StringLc, 16))*2) )
890 | {
891 | return false;
892 | }
893 | }
894 | if ( StringLe.length() == 1 )
895 | {
896 | StringLe = "0"+ StringLe;
897 | editLe.setText(StringLe);
898 | }
899 | if ( StringLc.length() == 1 )
900 | {
901 | StringLc = "0"+ StringLc;
902 | editLc.setText(StringLc);
903 | }
904 | if ( StringP2.length() == 1 )
905 | {
906 | StringP2 = "0"+ StringP2;
907 | editP2.setText(StringP2);
908 | }
909 | if ( StringP1.length() == 1 )
910 | {
911 | StringP1 = "0"+ StringP1;
912 | editP1.setText(StringP1);
913 | }
914 | if ( StringINS.length() == 1 )
915 | {
916 | StringINS = "0"+ StringINS;
917 | editINS.setText(StringINS);
918 | }
919 | if ( StringCLA.length() == 1 )
920 | {
921 | StringCLA = "0"+ StringCLA;
922 | editCLA.setText(StringCLA);
923 | }
924 | }
925 |
926 | if (mCheckRaw.isChecked())
927 | {
928 | StringAPDU = editDataIn.getText().toString();
929 | if ( ((StringAPDU.length()%2)!=0)|| (StringAPDU.length()<1) )
930 | {
931 | return false;
932 | }
933 | }
934 | else
935 | {
936 | StringAPDU = StringCLA + StringINS + StringP1 + StringP2 + StringLc + StringDataIn + StringLe;
937 | }
938 |
939 | byteAPDU = atohex(StringAPDU);
940 | respAPDU = transceives(byteAPDU);
941 |
942 | if(mCheckResp.isChecked())
943 | {
944 | try
945 | {
946 | vShowResponseInterpretation(respAPDU);
947 | }
948 | catch (Exception e)
949 | {
950 | clearlog();
951 | print("Response is not TLV format !!!");
952 | }
953 |
954 | }
955 |
956 | return true;
957 | }
958 |
959 | private void resolveIntent(Intent intent)
960 | {
961 | String action = intent.getAction();
962 | clearlog();
963 | if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))
964 | {
965 | Parcelable tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
966 | final Tag t = (Tag) tag;
967 | myTag = IsoDep.get(t);
968 | mFirstDetected=true;
969 | if( !myTag.isConnected() )
970 | {
971 | try
972 | {
973 | myTag.connect();
974 | myTag.setTimeout(5000);
975 | }
976 | catch (IOException e)
977 | {
978 | e.printStackTrace();
979 | return;
980 | }
981 | }
982 | if( myTag.isConnected() )
983 | {
984 | if(mShowAtr==true)
985 | {
986 | icoCard.setImageResource(R.drawable.ic_icc_on_atr);
987 | }
988 | else
989 | {
990 | icoCard.setImageResource(R.drawable.ic_icc_on);
991 | }
992 | vShowCardRemovalInfo();
993 | String szATR = null;
994 | try
995 | {
996 | mShowAtr=true;
997 | szATR =" 3B " + getATRLeString(myTag.getHistoricalBytes())+ "80 01 " + getHexString(myTag.getHistoricalBytes())+""+ getATRXorString(myTag.getHistoricalBytes());
998 | }
999 | catch (Exception e)
1000 | {
1001 | mShowAtr=false;
1002 | szATR = "CARD DETECTED ";
1003 | }
1004 | TextCard.setText(szATR);
1005 |
1006 | mSendAPDUButton.setEnabled(true);
1007 | clearlog();
1008 | txtCLA.setEnabled(true);
1009 | txtINS.setEnabled(true);
1010 | txtP1.setEnabled(true);
1011 | txtP2.setEnabled(true);
1012 | txtLc.setEnabled(true);
1013 | txtDataIn.setEnabled(true);
1014 | txtLe.setEnabled(true);
1015 |
1016 | editCLA.setEnabled(true);
1017 | editINS.setEnabled(true);
1018 | editP1.setEnabled(true);
1019 | editP2.setEnabled(true);
1020 | editLc.setEnabled(true);
1021 | editDataIn.setEnabled(true);
1022 | editLe.setEnabled(true);
1023 | editCLA.setText("");
1024 | editINS.setText("");
1025 | editP1.setText("");
1026 | editP2.setText("");
1027 | editLc.setText("");
1028 | editLe.setText("");
1029 | editDataIn.setText("");
1030 | mCheckRaw.setChecked(false);
1031 | mCheckResp.setChecked(false);
1032 | editCLA.requestFocus();
1033 | }
1034 | else
1035 | {
1036 | icoCard.setImageResource(R.drawable.ic_icc_off);
1037 | }
1038 | }
1039 | if( mFirstDetected==true && myTag.isConnected() )
1040 | {
1041 | if(mShowAtr==true)
1042 | {
1043 | icoCard.setImageResource(R.drawable.ic_icc_on_atr);
1044 | }
1045 | else
1046 | {
1047 | icoCard.setImageResource(R.drawable.ic_icc_on);
1048 | }
1049 | }
1050 | else
1051 | {
1052 | icoCard.setImageResource(R.drawable.ic_icc_off);
1053 | }
1054 | }
1055 |
1056 | private void vSetBuiltinCommand()
1057 | {
1058 | clearlog();
1059 |
1060 | editCLA.setText("");
1061 | editINS.setText("");
1062 | editP1.setText("");
1063 | editP2.setText("");
1064 | editLc.setText("");
1065 | editLe.setText("");
1066 | editDataIn.setText("");
1067 |
1068 | txtCLA.setEnabled(false);
1069 | txtINS.setEnabled(false);
1070 | txtP1.setEnabled(false);
1071 | txtP2.setEnabled(false);
1072 | txtLc.setEnabled(false);
1073 | txtLe.setEnabled(false);
1074 | editCLA.setEnabled(false);
1075 | editINS.setEnabled(false);
1076 | editP1.setEnabled(false);
1077 | editP2.setEnabled(false);
1078 | editLc.setEnabled(false);
1079 | editLe.setEnabled(false);
1080 | editDataIn.setEnabled(true);
1081 | txtDataIn.setEnabled(true);
1082 | txtDataIn.setText("APDU:");
1083 | mCheckRaw.setChecked(true);
1084 |
1085 | return;
1086 | }
1087 |
1088 | private static void vShowResponseInterpretation(byte[] data)
1089 | {
1090 |
1091 | print("");
1092 | print("====================================");
1093 | print("RESPONSE INTERPRETATION:");
1094 |
1095 |
1096 | if (data.length > 2)
1097 | {
1098 | byte[] sw12 = new byte[2];
1099 | System.arraycopy(data, data.length-2, sw12, 0, 2);
1100 | byte[] payload = Arrays.copyOf(data, (data.length)-2 );
1101 | try
1102 | {
1103 | print("SW1-SW2 " + getHexString(sw12) + RetStatusWord.getSWDescription(Util.szByteHex2String(sw12[0]) + Util.szByteHex2String(sw12[1])));
1104 | }
1105 | catch (Exception e)
1106 | {
1107 | e.printStackTrace();
1108 | }
1109 | print(EmvInterpreter.ShowEMV_Interpretation(payload));
1110 |
1111 | }
1112 | else if (data.length == 2)
1113 | {
1114 | byte[] sw12 = new byte[2];
1115 | System.arraycopy(data, data.length-2, sw12, 0, 2);
1116 | try
1117 | {
1118 | print("SW1-SW2 " + getHexString(sw12) );
1119 | print(RetStatusWord.getSWDescription(Util.szByteHex2String(sw12[0]) + Util.szByteHex2String(sw12[1])));
1120 | }
1121 | catch (Exception e)
1122 | {
1123 | e.printStackTrace();
1124 | }
1125 | }
1126 | print("====================================");
1127 | return;
1128 | }
1129 |
1130 | private void vShowCardRemovalInfo()
1131 | {
1132 | Context context = getApplicationContext();
1133 | CharSequence text = "Card Removal will NOT be detected";
1134 | int duration = Toast.LENGTH_LONG;
1135 | HideKbd();
1136 | Toast toast = Toast.makeText(context, text, duration);
1137 | toast.show();
1138 | }
1139 |
1140 | private void vShowGeneralMesg(String szText)
1141 | {
1142 | Context context = getApplicationContext();
1143 | int duration = Toast.LENGTH_LONG;
1144 | Toast toast = Toast.makeText(context, szText, duration);
1145 | toast.show();
1146 | }
1147 |
1148 | private void vShowErrorVaules()
1149 | {
1150 | Context context = getApplicationContext();
1151 | CharSequence text = "C-APDU values ERROR";
1152 | int duration = Toast.LENGTH_LONG;
1153 | HideKbd();
1154 | Toast toast = Toast.makeText(context, text, duration);
1155 | toast.show();
1156 | }
1157 |
1158 | private static String getHexString(byte[] data) throws Exception
1159 | {
1160 | String szDataStr = "";
1161 | for (int ii=0; ii < data.length; ii++)
1162 | {
1163 | szDataStr += String.format("%02X ", data[ii] & 0xFF);
1164 | }
1165 | return szDataStr;
1166 | }
1167 |
1168 | private static String getATRLeString(byte[] data) throws Exception
1169 | {
1170 | return String.format("%02X ", data.length | 0x80);
1171 | }
1172 |
1173 | private static String getATRXorString(byte[] b) throws Exception
1174 | {
1175 | int Lrc=0x00;
1176 | Lrc = b.length | 0x80;
1177 | Lrc = Lrc^0x81;
1178 | for (int i=0; i < b.length; i++)
1179 | {
1180 | Lrc = Lrc^(b[i] & 0xFF);
1181 | }
1182 | return String.format("%02X ", Lrc);
1183 | }
1184 |
1185 | private static byte[] atohex(String data)
1186 | {
1187 | String hexchars = "0123456789abcdef";
1188 |
1189 | data = data.replaceAll(" ","").toLowerCase();
1190 | if (data == null)
1191 | {
1192 | return null;
1193 | }
1194 | byte[] hex = new byte[data.length() / 2];
1195 |
1196 | for (int ii = 0; ii < data.length(); ii += 2)
1197 | {
1198 | int i1 = hexchars.indexOf(data.charAt(ii));
1199 | int i2 = hexchars.indexOf(data.charAt(ii + 1));
1200 | hex[ii/2] = (byte)((i1 << 4) | i2);
1201 | }
1202 | return hex;
1203 | }
1204 |
1205 | }
1206 |
1207 |
--------------------------------------------------------------------------------
/src/com/jmarroyo/apdusendercontactless/Ber_Tlv.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2014 Jose Maria ARROYO jm.arroyo.castejon@gmail.com
3 |
4 | APDUSenderContactLess is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by the
6 | Free Software Foundation, either version 3 of the License, or (at your option)
7 | any later version.
8 |
9 | APDUSenderContactLess is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
17 | */
18 |
19 | package com.jmarroyo.apdusendercontactless;
20 |
21 |
22 | import java.io.ByteArrayInputStream;
23 | import java.io.ByteArrayOutputStream;
24 |
25 | public class Ber_Tlv
26 | {
27 |
28 | private byte[] TagHex;
29 | private byte[] rawEncodedLengthBytes;
30 | private byte[] valueBytes;
31 |
32 |
33 | private static boolean isBitSet(byte data, int offset)
34 | {
35 | if ((data >> (offset - 1) & 0x01) == 1)
36 | {
37 | return true;
38 | }
39 | return false;
40 | }
41 |
42 | private static int iatoi(byte[] data, int length)
43 | {
44 | int atoi = 0;
45 | for (int ii = 0; ii < length; ii++)
46 | {
47 | atoi += ((data[ii] & 0xFF) << 8 * (data.length - ii - 1));
48 | }
49 | return atoi;
50 | }
51 |
52 | private static byte[] get_TLV_Tag(ByteArrayInputStream stream)
53 | {
54 | ByteArrayOutputStream tagBAOS = new ByteArrayOutputStream();
55 | byte tagFirstOctet = (byte) stream.read();
56 | byte MASK = (byte)0x1F;
57 |
58 | tagBAOS.write(tagFirstOctet);
59 | if ((tagFirstOctet & MASK) == MASK)
60 | {
61 | do
62 | {
63 | int nextOctet = stream.read();
64 | if(nextOctet < 0)
65 | {
66 | break;
67 | }
68 | byte tlvIdNextOctet = (byte) nextOctet;
69 | tagBAOS.write(tlvIdNextOctet);
70 | if (!isBitSet(tlvIdNextOctet, 8))
71 | {
72 | break;
73 | }
74 | }while (true);
75 | }
76 | return tagBAOS.toByteArray();
77 | }
78 |
79 | private static int get_TLV_Length(ByteArrayInputStream stream)
80 | {
81 | int length;
82 | int length_aux = stream.read();
83 |
84 | if (length_aux <= 128)
85 | {
86 | length = length_aux;
87 | }
88 | else
89 | {
90 | int numberOfLengthOctets = length_aux & 127;
91 | length_aux = 0;
92 | for (int i = 0; i < numberOfLengthOctets; i++)
93 | {
94 | int nextLengthOctet = stream.read();
95 | length_aux <<= 8;
96 | length_aux |= nextLengthOctet;
97 | }
98 | length = length_aux;
99 | }
100 | return length;
101 | }
102 |
103 | private Ber_Tlv(byte[] TagHex,int length, byte[] rawEncodedLengthBytes, byte[] valueBytes)
104 | {
105 | if(valueBytes!=null)
106 | {
107 | this.valueBytes = valueBytes;
108 | }
109 | this.rawEncodedLengthBytes = rawEncodedLengthBytes;
110 | this.TagHex = TagHex;
111 | }
112 |
113 | public static Ber_Tlv getNextTLV(ByteArrayInputStream stream, boolean bDOL)
114 | {
115 | stream.mark(0);
116 | int peekInt = stream.read();
117 | byte peekByte = (byte) peekInt;
118 | while( (peekInt!=-1) && ( (peekByte==(byte)0xFF) || (peekByte == (byte)0x00) ) )
119 | {
120 | stream.mark(0);
121 | peekInt = stream.read();
122 | peekByte = (byte)peekInt;
123 | }
124 | stream.reset();
125 |
126 | // TAG
127 | byte[] tagIdBytes = get_TLV_Tag(stream);
128 |
129 | stream.mark(0);
130 | int posBefore = stream.available();
131 |
132 | // LENGTH
133 | int length = get_TLV_Length(stream);
134 | int posAfter = stream.available();
135 | stream.reset();
136 | byte[] lengthBytes = new byte[posBefore - posAfter];
137 | stream.read(lengthBytes, 0, lengthBytes.length);
138 |
139 | int rawLength = iatoi(lengthBytes,lengthBytes.length);
140 | byte[] valueBytes = null;
141 |
142 | if(bDOL==true)
143 | {
144 | Ber_Tlv tlv = new Ber_Tlv(tagIdBytes, length, lengthBytes, null);
145 | return tlv;
146 | }
147 |
148 | // VALUE
149 | if (rawLength == 128)
150 | {
151 | stream.mark(0);
152 | int prevOctet = 1;
153 | int curOctet = 0;
154 | int len = 0;
155 | while(true)
156 | {
157 | len++;
158 | curOctet = stream.read();
159 | if( (prevOctet == 0) && (curOctet == 0) )
160 | {
161 | break;
162 | }
163 | prevOctet = curOctet;
164 | }
165 | len -= 2;
166 | valueBytes = new byte[len];
167 | stream.reset();
168 | stream.read(valueBytes, 0, len);
169 | length = len;
170 | }
171 | else
172 | {
173 | valueBytes = new byte[length];
174 | stream.read(valueBytes, 0, length);
175 | }
176 | stream.mark(0);
177 | peekInt = stream.read();
178 | peekByte = (byte) peekInt;
179 | while (peekInt != -1 && (peekByte == (byte) 0xFF || peekByte == (byte) 0x00))
180 | {
181 | stream.mark(0);
182 | peekInt = stream.read();
183 | peekByte = (byte) peekInt;
184 | }
185 | stream.reset();
186 |
187 | Ber_Tlv tlv = new Ber_Tlv(tagIdBytes, length, lengthBytes, valueBytes);
188 | return tlv;
189 | }
190 |
191 | public byte[] getTagBytes()
192 | {
193 | return TagHex;
194 | }
195 |
196 | public byte[] getRawEncodedLengthBytes()
197 | {
198 | return rawEncodedLengthBytes;
199 | }
200 |
201 | public byte[] getValueBytes()
202 | {
203 | return valueBytes;
204 | }
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/src/com/jmarroyo/apdusendercontactless/EmvInterpreter.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2014 Jose Maria ARROYO jm.arroyo.castejon@gmail.com
3 |
4 | APDUSenderContactLess is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by the
6 | Free Software Foundation, either version 3 of the License, or (at your option)
7 | any later version.
8 |
9 | APDUSenderContactLess is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
17 | */
18 |
19 | package com.jmarroyo.apdusendercontactless;
20 |
21 |
22 |
23 | import java.io.ByteArrayInputStream;
24 |
25 |
26 | public class EmvInterpreter
27 | {
28 |
29 | private enum EMVDescription
30 | {
31 | TAG001("0042","Issuer Identification Number (IIN)"),
32 | TAG002("004F","Application Identifier (AID)"),
33 | TAG003("0050","Application Label"),
34 | TAG004("0051","ISO-7816 Path"),
35 | TAG005("0057","Track 2 Equivalent Data"),
36 | TAG006("005A","Application PAN"),
37 | TAG007("0061","Application Template"),
38 | TAG008("006F","FCI Template"),
39 | TAG009("0070","Record Template (EMV Proprietary)"),
40 | TAG010("0071","Issuer Script Template 1"),
41 | TAG011("0072","Issuer Script Template 2"),
42 | TAG012("0073","Directory Discretionary Template"),
43 | TAG013("0077","Response Message Template Format 2"),
44 | TAG014("0080","Response Message Template Format 1"),
45 | TAG015("0081","Amount, Authorised (Binary)"),
46 | TAG016("0082","Application Interchange Profile"),
47 | TAG017("0083","Command Template"),
48 | TAG018("0084","Dedicated File (DF) Name"),
49 | TAG019("0086","Issuer Script Command"),
50 | TAG020("0087","Application Priority Indicator"),
51 | TAG021("0088","Short File Identifier (SFI)"),
52 | TAG022("0089","Authorisation Code"),
53 | TAG023("008A","Authorisation Response Code"),
54 | TAG024("008C","CDOL1"),
55 | TAG025("008D","CDOL2"),
56 | TAG026("008E","CVM List"),
57 | TAG027("008F","CA Public Key Index"),
58 | TAG028("0090","Issuer Public Key Certificate"),
59 | TAG029("0091","Issuer Authentication Data"),
60 | TAG030("0092","Issuer Public Key Remainder"),
61 | TAG031("0093","Signed Static Application Data"),
62 | TAG032("0094","Application File Locator (AFL)"),
63 | TAG033("0095","Terminal Verification Results (TVR)"),
64 | TAG034("0097","TDOL"),
65 | TAG035("0098","TC Hash Value"),
66 | TAG036("0099","Transaction PIN Data"),
67 | TAG037("009A","Transaction Date"),
68 | TAG038("009B","Transaction Status Information"),
69 | TAG039("009C","Transaction Type"),
70 | TAG040("009D","DDF Name"),
71 | TAG041("00A5", "FCI Proprietary Template"),
72 | TAG0101("5F20","Cardholder Name"),
73 | TAG0102("5F24","Application Expiration Date"),
74 | TAG0103("5F25","Application Effective Date"),
75 | TAG0104("5F28","Issuer Country Code"),
76 | TAG0105("5F2A","Transaction Currency Code"),
77 | TAG0106("5F2D","Language Preference"),
78 | TAG0107("5F30","Service Code"),
79 | TAG0108("5F34","PAN Sequence Number"),
80 | TAG0109("5F36","Transaction Currency Exponent"),
81 | TAG0110("5F50","Issuer URL"),
82 | TAG0111("5F53","IBAN"),
83 | TAG0112("5F54","Bank Identifier Code (BIC)"),
84 | TAG0113("5F55","Issuer Country Code (alpha2 format)"),
85 | TAG0114("5F56","Issuer Country Code (alpha3 format)"),
86 | TAG0115("9F01","Acquirer Identifier"),
87 | TAG0116("9F02","Amount, Authorised (Numeric)"),
88 | TAG0117("9F03","Amount, Other (Numeric)"),
89 | TAG0118("9F04","Amount, Other (Binary)"),
90 | TAG0119("9F05","Application Discretionary Data"),
91 | TAG0120("9F06","Application Identifier (AID)"),
92 | TAG0121("9F07","Application Usage Control"),
93 | TAG0122("9F08","Application Version Number"),
94 | TAG0123("9F09","Application Version Number"),
95 | TAG0124("9F0B","Cardholder Name Extended"),
96 | TAG0125("9F0D","Issuer Action Code - Default"),
97 | TAG0126("9F0E","Issuer Action Code - Denial"),
98 | TAG0127("9F0F","Issuer Action Code - Online"),
99 | TAG0128("9F10","Issuer Application Data"),
100 | TAG0129("9F11","Issuer Code Table Index"),
101 | TAG0130("9F12","Application Preferred Name"),
102 | TAG0131("9F13","Last Online ATC Register"),
103 | TAG0132("9F14","Lower Consecutive Offline Limit"),
104 | TAG0133("9F15","Merchant Category Code"),
105 | TAG0134("9F16","Merchant Identifier"),
106 | TAG0135("9F17","PIN Try Counter"),
107 | TAG0136("9F18","Issuer Script Identifier"),
108 | TAG0137("9F1A","Terminal Country Code"),
109 | TAG0138("9F1B","Terminal Floor Limit"),
110 | TAG0139("9F1C","Terminal Identification"),
111 | TAG0140("9F1D","Terminal Risk Management Data"),
112 | TAG0141("9F1E","Interface Device (IFD) Serial Number"),
113 | TAG0142("9F1F","Track 1 Discretionary Data"),
114 | TAG0143("9F20","Track 2 Discretionary Data"),
115 | TAG0144("9F21","Transaction Time (HHMMSS)"),
116 | TAG0145("9F22","Certification Authority Public Key Index"),
117 | TAG0146("9F23","Upper Consecutive Offline Limit"),
118 | TAG0147("9F26","Application Cryptogram"),
119 | TAG0148("9F27","Cryptogram Information Data"),
120 | TAG0149("9F2D","ICC PIN Encipherment Pub Key Certificate"),
121 | TAG0150("9F2E","ICC PIN Encipherment Pub Key Exponent"),
122 | TAG0151("9F2F","ICC PIN Encipherment Pub Key Remainder"),
123 | TAG0152("9F32","Issuer Public Key Exponent"),
124 | TAG0153("9F33","Terminal Capabilities"),
125 | TAG0154("9F34","Cardholder Verification (CVM) Results"),
126 | TAG0155("9F35","Terminal Type"),
127 | TAG0156("9F36","Application Transaction Counter (ATC)"),
128 | TAG0157("9F37","Unpredictable Number"),
129 | TAG0158("9F38","Processing Options (PDOL)"),
130 | TAG0159("9F39","Point-of-Service (POS) Entry Mode"),
131 | TAG0160("9F3A","Amount, Reference Currency"),
132 | TAG0161("9F3B","Application Reference Currency"),
133 | TAG0162("9F3C","Transaction Reference Currency Code"),
134 | TAG0163("9F3D","Transaction Reference Currency Exponent"),
135 | TAG0164("9F40","Additional Terminal Capabilities"),
136 | TAG0165("9F41","Transaction Sequence Counter"),
137 | TAG0166("9F42","Application Currency Code"),
138 | TAG0167("9F43","Application Reference Currency Exponent"),
139 | TAG0168("9F44","Application Currency Exponent"),
140 | TAG0169("9F45","Data Authentication Code"),
141 | TAG0170("9F46","ICC Public Key Certificate"),
142 | TAG0171("9F47","ICC Public Key Exponent"),
143 | TAG0172("9F48","ICC Public Key Remainder"),
144 | TAG0173("9F49","Dynamic Data Authentication (DDOL)"),
145 | TAG0174("9F4A","Static Data Authentication Tag List"),
146 | TAG0175("9F4B","Signed Dynamic Application Data"),
147 | TAG0176("9F4C","ICC Dynamic Number"),
148 | TAG0177("9F4D","Log Entry"),
149 | TAG0178("9F4E","Merchant Name and Location"),
150 | TAG0179("9F4F","Log Format"),
151 | TAG0180("9F66","Terminal Transaction Qualifiers"),
152 | TAG0181("BF0C","FCI Issuer Discretionary Data");
153 |
154 |
155 | private String szEmvTagStr;
156 | private String szDescriptionStr;
157 |
158 | private EMVDescription(String szEmvTagStr, String szDescriptionStr)
159 | {
160 | this.szEmvTagStr = szEmvTagStr;
161 | this.szDescriptionStr = szDescriptionStr;
162 | }
163 |
164 | private String getEMVCodeAsString()
165 | {
166 | return szEmvTagStr;
167 | }
168 |
169 | private String getDescription()
170 | {
171 | return szDescriptionStr;
172 | }
173 |
174 | private static String getEMVDescription(byte emv1, byte emv2)
175 | {
176 | return getEMVDescription(Util.szByteHex2String(emv1)+Util.szByteHex2String(emv2));
177 | }
178 |
179 | private static String getEMVDescription(String EmvStr)
180 | {
181 | EMVDescription[] EmvDescs = EMVDescription.values();
182 | for (int ii = 0; ii < EmvDescs.length; ii++)
183 | {
184 | EMVDescription EmvDesc = EmvDescs[ii];
185 | if (EmvDesc.getEMVCodeAsString().equalsIgnoreCase(EmvStr))
186 | {
187 | return EmvDesc.getDescription();
188 | }
189 | }
190 | return "(UNRECOGNIZED)";
191 | }
192 | }
193 |
194 | private static String getTagDescription(byte[] tagBytes)
195 | {
196 | if(tagBytes.length>1)
197 | {
198 | return(EMVDescription.getEMVDescription((byte)tagBytes[0],(byte)tagBytes[1]));
199 | }
200 | else
201 | {
202 | return(EMVDescription.getEMVDescription((byte)0x00,(byte)tagBytes[0]));
203 | }
204 | }
205 |
206 | private static String PrintValue(byte[] byteArray)
207 | {
208 | int length = byteArray.length;
209 |
210 | StringBuilder buf = new StringBuilder();
211 | for (int ii=0; ii= (byte) 0x20 && byteArray[ii] < (byte) 0x7F)
214 | {
215 | buf.append((char) byteArray[ii]);
216 | }
217 | else
218 | {
219 | StringBuilder buf_no = new StringBuilder();
220 | return buf_no.toString();
221 | }
222 | }
223 | return buf.toString();
224 | }
225 |
226 | private static String getTagValueAsString(byte[] value)
227 | {
228 | StringBuilder buf = new StringBuilder();
229 | buf.append("\n");
230 | buf.append(" >Inter ");
231 | String ValueStr = PrintValue(value);
232 | buf.append(ValueStr);
233 | if (ValueStr.length()>1)
234 | {
235 | return buf.toString();
236 | }
237 | return "";
238 | }
239 |
240 | private static String AddSpace(String in)
241 | {
242 | StringBuilder buf = new StringBuilder();
243 | for (int ii=0; ii 0)
279 | {
280 | if (firstLine)
281 | {
282 | firstLine = false;
283 | buf.append("DOL Interpretation:\n");
284 | }
285 | else
286 | {
287 | buf.append("\n");
288 | }
289 |
290 | Ber_Tlv tlv = Ber_Tlv.getNextTLV(stream,true);
291 | byte[] tagBytes = tlv.getTagBytes();
292 | byte[] lengthBytes = tlv.getRawEncodedLengthBytes();
293 | buf.append(" *Tag ");
294 | buf.append(AddSpace(byteArrayToHexString(tagBytes)));
295 | buf.append("\n");
296 | buf.append(" *Len ");
297 | buf.append(AddSpace(byteArrayToHexString(lengthBytes)));
298 | buf.append("\n");
299 | buf.append(" * ");
300 | buf.append(getTagDescription(tagBytes));
301 | }
302 | return buf.toString();
303 | }
304 |
305 |
306 | private static boolean isDOL(byte[] tagBytes)
307 | {
308 | if( tagBytes[0]==(byte)0x9F )
309 | {
310 | if( (tagBytes[1]==(byte)0x38)||( tagBytes[1]==(byte)0x49)||(tagBytes[1]==(byte)0x4F) )
311 | {
312 | return true;
313 | }
314 | }
315 | if( (tagBytes[0]==(byte)0x8C)||(tagBytes[0]==(byte)0x8D) )
316 | {
317 | return true;
318 | }
319 | return false;
320 | }
321 |
322 | public static String ShowEMV_Interpretation(byte[] data)
323 | {
324 | StringBuilder buf = new StringBuilder();
325 | ByteArrayInputStream stream = new ByteArrayInputStream(data);
326 |
327 | while (stream.available() > 0)
328 | {
329 | buf.append("\n");
330 | Ber_Tlv tlv = Ber_Tlv.getNextTLV(stream,false);
331 | byte[] tagBytes = tlv.getTagBytes();
332 | byte[] lengthBytes = tlv.getRawEncodedLengthBytes();
333 | byte[] valueBytes = tlv.getValueBytes();
334 |
335 | buf.append("Tag ");
336 | buf.append(AddSpace(byteArrayToHexString(tagBytes)));
337 | buf.append(" ");
338 | buf.append(getTagDescription(tagBytes));
339 | buf.append("\n");
340 | buf.append(" >Len ");
341 | buf.append(AddSpace(byteArrayToHexString(lengthBytes)));
342 |
343 | if (((byte)tagBytes[0] >> 5 & 0x01) == 1)
344 | {
345 | buf.append(" ( Constructed Data Object )");
346 | buf.append(ShowEMV_Interpretation(valueBytes));
347 | }
348 | else
349 | {
350 | buf.append("\n");
351 | if (isDOL(tagBytes)==true)
352 | {
353 | buf.append(" >Value");
354 | buf.append(" ");
355 | String ValueStr = AddSpace(byteArrayToHexString(valueBytes));
356 | if (ValueStr.length()>34)
357 | {
358 | buf.append(":");
359 | buf.append("\n");
360 | }
361 | buf.append(ValueStr);
362 | buf.append("\n");
363 | buf.append(ShowDOLInterpretation(valueBytes));
364 | }
365 | else
366 | {
367 | buf.append(" >Value");
368 | buf.append(" ");
369 | String ValueStr = AddSpace(byteArrayToHexString(valueBytes));
370 | if (ValueStr.length()>34)
371 | {
372 | buf.append(":");
373 | buf.append("\n");
374 | }
375 | buf.append(ValueStr);
376 | buf.append(getTagValueAsString(valueBytes));
377 | }
378 | }
379 | }
380 | return buf.toString();
381 | }
382 |
383 | }
384 |
385 |
386 |
--------------------------------------------------------------------------------
/src/com/jmarroyo/apdusendercontactless/HexadecimalKbd.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2014 Jose Maria ARROYO jm.arroyo.castejon@gmail.com
3 |
4 | APDUSenderContactLess is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by the
6 | Free Software Foundation, either version 3 of the License, or (at your option)
7 | any later version.
8 |
9 | APDUSenderContactLess is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
17 | */
18 |
19 | package com.jmarroyo.apdusendercontactless;
20 |
21 | import android.app.Activity;
22 | import android.inputmethodservice.Keyboard;
23 | import android.inputmethodservice.KeyboardView;
24 | import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
25 | import android.text.Editable;
26 | import android.text.InputType;
27 | import android.view.MotionEvent;
28 | import android.view.View;
29 | import android.view.View.OnClickListener;
30 | import android.view.View.OnFocusChangeListener;
31 | import android.view.View.OnTouchListener;
32 | import android.view.WindowManager;
33 | import android.view.inputmethod.InputMethodManager;
34 | import android.widget.EditText;
35 |
36 |
37 | class HexadecimalKbd
38 | {
39 | private boolean bFirstIteration=false;
40 | private KeyboardView mKeyboardView;
41 | private Activity mHostActivity;
42 |
43 | private OnKeyboardActionListener mOnKeyboardActionListener = new OnKeyboardActionListener()
44 | {
45 | public final static int CodeDelete = -5; //DELETE
46 | public final static int CodeLeft = 55002;
47 | public final static int CodeRight = 55003;
48 | public final static int CodeClear = 55006;
49 |
50 | @Override public void onKey(int primaryCode, int[] keyCodes)
51 | {
52 | View focusCurrent = mHostActivity.getWindow().getCurrentFocus();
53 | if( focusCurrent==null || (focusCurrent.getClass()!=EditText.class) )
54 | {
55 | return;
56 | }
57 | EditText edittext = (EditText) focusCurrent;
58 | Editable editable = edittext.getText();
59 | int start = edittext.getSelectionStart();
60 | if( primaryCode==CodeDelete )
61 | {
62 | if( (editable!=null) && (start>0) )
63 | {
64 | editable.delete(start - 1, start);
65 | }
66 | }
67 | else if( primaryCode==CodeClear )
68 | {
69 | if( editable!=null )
70 | {
71 | editable.clear();
72 | }
73 | }
74 | else if( primaryCode==CodeLeft )
75 | {
76 | if( start>0 )
77 | {
78 | edittext.setSelection(start - 1);
79 | }
80 | }
81 | else if( primaryCode==CodeRight )
82 | {
83 | if (start < edittext.length())
84 | {
85 | edittext.setSelection(start + 1);
86 | }
87 | }
88 | else
89 | {
90 | editable.insert(start, Character.toString((char) primaryCode));
91 | }
92 | }
93 | @Override public void onPress(int arg0)
94 | {
95 | }
96 |
97 | @Override public void onRelease(int primaryCode)
98 | {
99 | }
100 |
101 | @Override public void onText(CharSequence text)
102 | {
103 | }
104 |
105 | @Override public void swipeDown()
106 | {
107 | }
108 |
109 | @Override public void swipeLeft()
110 | {
111 | }
112 |
113 | @Override public void swipeRight()
114 | {
115 | }
116 |
117 | @Override public void swipeUp()
118 | {
119 | }
120 |
121 | };
122 |
123 |
124 | public HexadecimalKbd(Activity host, int viewid, int layoutid)
125 | {
126 | mHostActivity= host;
127 | mKeyboardView= (KeyboardView)mHostActivity.findViewById(viewid);
128 | mKeyboardView.setKeyboard(new Keyboard(mHostActivity, layoutid));
129 | mKeyboardView.setPreviewEnabled(false);
130 | mKeyboardView.setOnKeyboardActionListener(mOnKeyboardActionListener);
131 | mHostActivity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
132 | }
133 |
134 | public boolean isCustomKeyboardVisible()
135 | {
136 | return (mKeyboardView.getVisibility()==View.VISIBLE);
137 | }
138 |
139 | public void showCustomKeyboard( View v )
140 | {
141 | mKeyboardView.setVisibility(View.VISIBLE);
142 | mKeyboardView.setEnabled(true);
143 | if( v!=null )
144 | {
145 | ((InputMethodManager)mHostActivity.getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0);
146 | }
147 | }
148 |
149 | public void hideCustomKeyboard()
150 | {
151 | mKeyboardView.setVisibility(View.GONE);
152 | mKeyboardView.setEnabled(false);
153 | }
154 |
155 | public void registerEditText(int resid)
156 | {
157 | EditText edittext= (EditText)mHostActivity.findViewById(resid);
158 | edittext.setOnFocusChangeListener(new OnFocusChangeListener()
159 | {
160 | @Override public void onFocusChange(View v, boolean hasFocus)
161 | {
162 | if(bFirstIteration)
163 | {
164 | if( hasFocus )
165 | {
166 | showCustomKeyboard(v);
167 | }
168 | else
169 | {
170 | hideCustomKeyboard();
171 | }
172 | }
173 | else
174 | {
175 | hideCustomKeyboard();
176 | bFirstIteration=true;
177 | }
178 | }
179 | });
180 |
181 | edittext.setOnClickListener(new OnClickListener()
182 | {
183 | @Override public void onClick(View v)
184 | {
185 | showCustomKeyboard(v);
186 | }
187 | });
188 |
189 | edittext.setOnTouchListener(new OnTouchListener()
190 | {
191 | @Override public boolean onTouch(View v, MotionEvent event)
192 | {
193 | EditText edittext = (EditText) v;
194 | int inType = edittext.getInputType();
195 | edittext.setInputType(InputType.TYPE_NULL);
196 | edittext.onTouchEvent(event);
197 | edittext.setInputType(inType);
198 | return true;
199 | }
200 | });
201 | edittext.setInputType(edittext.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/src/com/jmarroyo/apdusendercontactless/RetStatusWord.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2014 Jose Maria ARROYO jm.arroyo.castejon@gmail.com
3 |
4 | APDUSenderContactLess is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by the
6 | Free Software Foundation, either version 3 of the License, or (at your option)
7 | any later version.
8 |
9 | APDUSenderContactLess is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
17 | */
18 |
19 | package com.jmarroyo.apdusendercontactless;
20 |
21 |
22 | public enum RetStatusWord
23 | {
24 | SW001("9000", "Successful"),
25 | SW002("6200","No information given"),
26 | SW003("6281","Part of returned data may be corrupted"),
27 | SW004("6282","End of file/record reached before reading Le bytes"),
28 | SW005("6283","Selected file invalidated"),
29 | SW006("6284","FCI not formatted according to 1.1.5"),
30 | SW007("6300","Authentication failed"),
31 | SW008("6381","File filled up by the last write"),
32 | SW009("6400","State of nonvolatile memory unchanged"),
33 | SW010("6500","No information given"),
34 | SW011("6581","Memory failure"),
35 | SW012("6700","Wrong length"),
36 | SW013("6800","No information given"),
37 | SW014("6881","Logical channel not supported"),
38 | SW015("6882","Secure messaging not supported"),
39 | SW016("6900","No information given"),
40 | SW017("6981","Command incompatible with file structure"),
41 | SW018("6982","Security status not satisfied"),
42 | SW019("6983","Authentication method blocked"),
43 | SW020("6984","Reference data invalidated"),
44 | SW021("6985","Conditions of use not satisfied"),
45 | SW022("6986","Command not allowed"),
46 | SW023("6987","Secure messaging data object missing"),
47 | SW024("6988","Secure messaging data object incorrect"),
48 | SW025("6A00","No information given"),
49 | SW026("6A80","Incorrect parameters in the data field"),
50 | SW027("6A81","Function not supported"),
51 | SW028("6A82","File not found"),
52 | SW029("6A83","Record not found"),
53 | SW030("6A84","Not enough memory space in file"),
54 | SW031("6A85","Lc inconsistent with TLV structure"),
55 | SW032("6A86","Incorrect parameters P1 P2"),
56 | SW033("6A87","Referenced data not found"),
57 | SW034("6D00","Instruction code not supported or invalid"),
58 | SW035("6E00","Class not supported"),
59 | SW036("6F00","No precise diagnostics");
60 |
61 |
62 | private String szSWStr;
63 | private String szDescriptionStr;
64 |
65 |
66 | private RetStatusWord(String szSWStr, String szDescriptionStr)
67 | {
68 | this.szSWStr = szSWStr;
69 | this.szDescriptionStr = szDescriptionStr;
70 |
71 | }
72 |
73 | private String getDescriptionStr()
74 | {
75 | return szDescriptionStr;
76 | }
77 |
78 |
79 | private String getSWStr()
80 | {
81 | return szSWStr;
82 | }
83 |
84 | public static String getSWDescription(String swStr)
85 | {
86 | RetStatusWord[] retStatusWords = RetStatusWord.values();
87 | for (int ii = 0; ii < retStatusWords.length; ii++)
88 | {
89 | RetStatusWord retStatusWord=retStatusWords[ii];
90 | if (retStatusWord.getSWStr().equalsIgnoreCase(swStr))
91 | {
92 | return retStatusWord.getDescriptionStr();
93 | }
94 | }
95 | return "";
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/com/jmarroyo/apdusendercontactless/Util.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2014 Jose Maria ARROYO jm.arroyo.castejon@gmail.com
3 |
4 | APDUSenderContactLess is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by the
6 | Free Software Foundation, either version 3 of the License, or (at your option)
7 | any later version.
8 |
9 | APDUSenderContactLess is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
17 | */
18 |
19 | package com.jmarroyo.apdusendercontactless;
20 |
21 | public class Util
22 | {
23 |
24 | public static String szByteHex2String(byte datain)
25 | {
26 | String[] CHARS0F = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
27 |
28 | int idata = datain & 0xFF;
29 | int nibble_1 = (idata >> 0x04) & 0x0F;
30 | int nibble_2 = idata & 0x0F;
31 |
32 | return CHARS0F[nibble_1] + CHARS0F[nibble_2];
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------