implements EMVReader.CardReader{
197 |
198 | private String LOG_TAG = CardHandler.class.getSimpleName();
199 | private byte[] adfInfo;
200 | private IsoDep isoDep;
201 |
202 | /**
203 | * Override this method to perform a computation on a background thread. The
204 | * specified parameters are the parameters passed to {@link #execute}
205 | * by the caller of this task.
206 | *
207 | * This method can call {@link #publishProgress} to publish updates
208 | * on the UI thread.
209 | *
210 | * @param params The parameters of the task.
211 | * @return A result, defined by the subclass of this task.
212 | * @see #onPreExecute()
213 | * @see #onPostExecute
214 | * @see #publishProgress
215 | */
216 | @Override
217 | protected CardHandler doInBackground(IsoDep... params) {
218 | Log.i(LOG_TAG, "doInBackground()..executing...");
219 | try{
220 |
221 | isoDep = params[0];
222 | isoDep.connect();
223 | byte[] response = transceive(EMVReader.SELECT_PPSE);
224 | CardHandler card = new CardHandler(response);
225 | card.isoDep = isoDep;
226 | return card;
227 |
228 | // if ((response.length == 2) && (response[0] == (byte) 0x61))
229 | // {
230 | // byte[] getData = new byte[]
231 | // {
232 | // 0x00, (byte) 0xC0, 0x00, 0x00, response[1]
233 | // };
234 | //
235 | // response = isoDep.transceive(getData);
236 | // if (response != null)
237 | // {
238 | // if ((response.length >= 2)
239 | // && (response[response.length - 2] == (byte) 0x90)
240 | // && (response[response.length - 1] == (byte) 0x00))
241 | // {
242 | // CardHandler card = new CardHandler(response);
243 | // Log.i(LOG_TAG, "doInBackground()..returns CardHandler object...");
244 | // return card;
245 | // }
246 | // }
247 | // }
248 | // }
249 | }
250 | catch (Exception ex){
251 | Log.e(LOG_TAG, "Error: " + ex.getMessage() );
252 | }
253 | Log.i(LOG_TAG, "doInBackground()..returns null...");
254 | return null;
255 | }
256 |
257 |
258 | /**
259 | * Runs on the UI thread after {@link #doInBackground}. The
260 | * specified result is the value returned by {@link #doInBackground}.
261 | *
262 | * This method won't be invoked if the task was cancelled.
263 | *
264 | * @param card The result of the operation computed by {@link #doInBackground}.
265 | * @see #onPreExecute
266 | * @see #doInBackground
267 | * @see #onCancelled(Object)
268 | */
269 | @Override
270 | protected void onPostExecute(CardHandler card) {
271 | Log.i(LOG_TAG, "onPostExecute()..executing...");
272 | if(card !=null) {
273 | try{
274 | EMVReader reader = new EMVReader(card,null /*EMVReader.AID_PPSE*/,card.getAdfInfo());
275 | reader.doTrace=true;
276 | reader.read();
277 |
278 | //Welcome Message updated.
279 | TextView welcomeMessageTextView = (TextView) findViewById(R.id.welcome_message_textView);
280 | welcomeMessageTextView.setText(getString(R.string.welcome_message_2));
281 |
282 | //PAN number
283 | TextView cardPanTextView = (TextView) findViewById(R.id.card_pan_textView);
284 | if(reader.pan!=null){
285 | cardPanTextView.setText(getString(R.string.card_pan_title) + " " + reader.pan);
286 | }
287 | else
288 | {
289 | cardPanTextView.setText(getString(R.string.card_pan_title) + " " + getString(R.string.not_available));
290 | }
291 |
292 | //Expiry Month
293 | TextView cardExpiryMonthTextView = (TextView) findViewById(R.id.card_expiry_month_textView);
294 | if(reader.expiryMonth !=null){
295 | cardExpiryMonthTextView.setText(getString(R.string.expiry_month_title) + " " + reader.expiryMonth);
296 | }
297 | else
298 | {
299 | cardExpiryMonthTextView.setText(getString(R.string.expiry_month_title) + " " + getString(R.string.not_available));
300 | }
301 |
302 | //Expiry Year
303 | TextView cardExpiryYearTextView = (TextView)findViewById(R.id.card_expiry_yr_textView);
304 | if(reader.expiryYear != null){
305 | cardExpiryYearTextView.setText(getString(R.string.expiry_yr_title) + " " + reader.expiryYear);
306 | }
307 | else
308 | {
309 | cardExpiryYearTextView.setText(getString(R.string.expiry_yr_title) + " " + getString(R.string.not_available));
310 | }
311 |
312 | //Issuer
313 | TextView cardIssuerTextView = (TextView) findViewById(R.id.card_issuer_textView);
314 | if(reader.issuer!=null){
315 | cardIssuerTextView.setText(getString(R.string.card_issuer_title) + " " + reader.issuer);
316 | }
317 | else
318 | {
319 | cardIssuerTextView.setText(getString(R.string.card_issuer_title) + " " + getString(R.string.not_available));
320 | }
321 |
322 | }
323 | catch (Exception ex){
324 | Log.e(LOG_TAG, "Error: " + ex.getMessage());
325 | }
326 | }
327 | }
328 |
329 | @Override
330 | public byte[] transceive(byte[] apdu) throws IOException {
331 | Log.i(LOG_TAG, "inside : transceive().. executed..");
332 | return isoDep.transceive(apdu);
333 | }
334 |
335 | /**
336 | * public CardHandler default constructor.
337 | */
338 | public CardHandler(){
339 | }
340 |
341 | /**
342 | * private CardHandler constructor with parameter.
343 | * @param rb
344 | */
345 | private CardHandler(byte[] rb){
346 | adfInfo = rb;
347 | }
348 |
349 | /**
350 | * adfInfo byte[]
351 | *
352 | * @return
353 | */
354 | public byte[] getAdfInfo() {
355 | return adfInfo;
356 | }
357 | }
358 |
359 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gauravtproject/android/emvreader/app/emvtools/BinaryTools.java:
--------------------------------------------------------------------------------
1 | /**************************************************************************
2 | *
3 | * Copyright 2014, Roger Brown
4 | *
5 | * This file is part of Roger Brown's Toolkit.
6 | *
7 | * This program is free software: you can redistribute it and/or modify it
8 | * under the terms of the GNU Lesser General Public License as published by the
9 | * Free Software Foundation, either version 3 of the License, or (at your
10 | * option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful, but WITHOUT
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 | * more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with this program. If not, see
19 | *
20 | */
21 |
22 | /*
23 | * $Id: BinaryTools.java 1 2014-06-07 22:37:15Z rhubarb-geek-nz $
24 | */
25 |
26 | package com.gauravtproject.android.emvreader.app.emvtools;
27 |
28 | /**
29 | * Re-Created by Gaurav Tandon on 8/03/2015.
30 | */
31 | /**
32 | * Binary tools for byte array manipulation
33 | * @author rogerb
34 | * @version $Id: BinaryTools.java 1 2014-06-07 22:37:15Z rhubarb-geek-nz $
35 | */
36 |
37 | public class BinaryTools
38 | {
39 | /**
40 | * convert a series of bytes into a long value
41 | * @param b buffer
42 | * @param off offset
43 | * @param len length
44 | * @return long value
45 | */
46 | public static long readLong(byte[]b,int off,int len)
47 | {
48 | long val=0;
49 |
50 | while (0!=(len--))
51 | {
52 | int x=(0xff & b[off++]);
53 | val=(val << 8)|x;
54 | }
55 |
56 | return val;
57 | }
58 |
59 | /**
60 | * convert a series of bytes into an int value
61 | * @param b buffer
62 | * @param off offset
63 | * @param len length
64 | * @return int value
65 | */
66 | public static int readInt(byte[]b,int off,int len)
67 | {
68 | return (int)readLong(b,off,len);
69 | }
70 |
71 | /**
72 | * get a subarray of bytes
73 | * @param data parent array
74 | * @param from offset
75 | * @param len number of bytes
76 | * @return subarray
77 | */
78 | public static byte[] bytesFrom(byte [] data,int from,int len)
79 | {
80 | byte [] res=new byte[len];
81 | System.arraycopy(data, from, res,0, len);
82 | return res;
83 | }
84 |
85 | /**
86 | * compare two subarrays of bytes
87 | * @param mac array one
88 | * @param x offset one
89 | * @param mac2 array two
90 | * @param y offset two
91 | * @param len number of bytes
92 | * @return true of match
93 | */
94 | public static boolean compareBytes(byte[] mac,int x,byte[] mac2,int y,int len)
95 | {
96 | while (0!=(len--))
97 | {
98 | if (mac[x++]!=mac2[y++])
99 | {
100 | return false;
101 | }
102 | }
103 |
104 | return true;
105 | }
106 |
107 | /**
108 | * concatenate a series of arrays
109 | * @param a array of arrays
110 | * @return single combined array
111 | */
112 |
113 | public static byte [] catenate(byte [][] a)
114 | {
115 | int i=0,j=0;
116 |
117 | while (i < a.length)
118 | {
119 | j+=a[i++].length;
120 | }
121 |
122 | byte [] r=new byte[j];
123 |
124 | i=0;
125 | j=0;
126 |
127 | while (i < a.length)
128 | {
129 | byte []b=a[i++];
130 | System.arraycopy(b,0,r,j,b.length);
131 | j+=b.length;
132 | }
133 |
134 | return r;
135 | }
136 |
137 | /**
138 | * write an int byte into a binary byte array
139 | * @param ba byte array
140 | * @param offset offset
141 | * @param length length of data
142 | * @param value value to write
143 | */
144 | public static void writeInt(byte[] ba, int offset, int length, int value)
145 | {
146 | offset+=length;
147 |
148 | while (0!=length--)
149 | {
150 | ba[--offset]=(byte)value;
151 | value>>=8;
152 | }
153 | }
154 |
155 | /**
156 | * write a long byte into a binary byte array
157 | * @param ba byte array
158 | * @param offset offset
159 | * @param length length of data
160 | * @param value value to write
161 | */
162 | public static void writeLong(byte[] ba, int offset, int length, long value)
163 | {
164 | offset+=length;
165 |
166 | while (0!=length--)
167 | {
168 | ba[--offset]=(byte)value;
169 | value>>=8;
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/app/src/main/java/com/gauravtproject/android/emvreader/app/emvtools/EMVReader.java:
--------------------------------------------------------------------------------
1 | /**************************************************************************
2 | *
3 | * Copyright 2014, Roger Brown
4 | *
5 | * This file is part of Roger Brown's Toolkit.
6 | *
7 | * This program is free software: you can redistribute it and/or modify it
8 | * under the terms of the GNU Lesser General Public License as published by the
9 | * Free Software Foundation, either version 3 of the License, or (at your
10 | * option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful, but WITHOUT
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 | * more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with this program. If not, see
19 | *
20 | */
21 |
22 | /*
23 | * $Id: EMVReader.java 1 2014-06-07 22:37:15Z rhubarb-geek-nz $
24 | */
25 |
26 | package com.gauravtproject.android.emvreader.app.emvtools;
27 |
28 | /**
29 | * Re-Created by Gaurav Tandon on 8/03/2015.
30 | */
31 |
32 | import java.io.IOException;
33 | import java.io.UnsupportedEncodingException;
34 | import java.util.Random;
35 | import java.util.logging.Level;
36 | import java.util.logging.Logger;
37 |
38 | /**
39 | *
40 | * @author rogerb
41 | */
42 | public class EMVReader
43 | {
44 | public boolean doTrace=false;
45 | final static String UTF8="utf-8";
46 | public static final byte [] SELECT_PPSE={
47 | 0x00,(byte)0xA4,0x04,0x00,0x0E,
48 | '2','P','A','Y','.','S','Y','S','.','D','D','F','0','1',0x00
49 | };
50 | public static final byte [] AID_PPSE={
51 | '2','P','A','Y','.','S','Y','S','.','D','D','F','0','1'
52 | };
53 | public static final byte [] SELECT_PSE={
54 | 0x00,(byte)0xA4,0x04,0x00,0x0E,
55 | '1','P','A','Y','.','S','Y','S','.','D','D','F','0','1'
56 | };
57 | public static final byte [] AID_PSE={
58 | '1','P','A','Y','.','S','Y','S','.','D','D','F','0','1'
59 | };
60 |
61 | private int getTagLen(byte[] data, int offset, int len)
62 | {
63 | int r=1;
64 | if ((data[offset]&0x1f)==0x1f)
65 | {
66 | r=2;
67 | }
68 | return r;
69 | }
70 |
71 | private int getTag(byte[] data, int offset, int tagLen)
72 | {
73 | return BinaryTools.readInt(data, offset, tagLen);
74 | }
75 |
76 | private int getLenLen(byte[] data, int offset, int len)
77 | {
78 | int r=0;
79 | int c=(0xff & data[offset]);
80 | if (c < 0x80)
81 | {
82 | r=1;
83 | }
84 | else
85 | {
86 | switch (c)
87 | {
88 | case 0x81:
89 | r=2;
90 | break;
91 | case 0x82:
92 | r=3;
93 | break;
94 | }
95 | }
96 | return r;
97 | }
98 |
99 | private int getLen(byte[] data, int offset, int lenLen)
100 | {
101 | int r=0;
102 |
103 | switch (lenLen)
104 | {
105 | case 1:
106 | r=(0x7f & data[offset]);
107 | break;
108 | case 2:
109 | r=(0xff & data[offset+1]);
110 | break;
111 | case 3:
112 | r=BinaryTools.readInt(data,offset+1, 2);
113 | break;
114 | }
115 | return r;
116 | }
117 |
118 | public class TLV
119 | {
120 | public int type,length;
121 | public byte [] value;
122 | }
123 |
124 | final CardReader reader;
125 | final byte [] adf;
126 | public Integer expiryMonth,expiryYear;
127 | public String pan;
128 |
129 | public byte [] aid;
130 | public String issuer;
131 | byte [] PDOL;
132 |
133 | public interface CardReader
134 | {
135 | public byte [] transceive(byte [] apdu) throws IOException;
136 | }
137 |
138 | public EMVReader(CardReader r,byte [] b,byte [] a)
139 | {
140 | reader=r;
141 | aid=b;
142 | adf=a;
143 | }
144 |
145 | interface EnumCallback
146 | {
147 | public boolean found(int tag,int len,byte [] data,int offset) throws IOException;
148 | }
149 |
150 | class ReadPDOData implements EnumCallback
151 | {
152 | @Override
153 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
154 | {
155 | boolean result=true;
156 |
157 | // System.out.println(String.format("PDO %04X,%d,",tag,len)+Hex.encode(data, offset, len));
158 |
159 | switch (tag)
160 | {
161 | case 0x57:
162 | result=readTrack2Equivalent(data,offset,len);
163 | break;
164 | case 0x94:
165 | result=readAFL(data,offset,len);
166 | break;
167 | }
168 |
169 | return result;
170 | }
171 | }
172 |
173 | class ReadPDO implements EnumCallback
174 | {
175 | @Override
176 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
177 | {
178 | boolean result=true;
179 |
180 | // System.out.println(String.format("PDO %04X,%d,",tag,len)+Hex.encode(data, offset, len));
181 |
182 | switch (tag)
183 | {
184 | case 0x70:
185 | case 0x77:
186 | result=parse(new ReadPDOData(),data,offset,len);
187 | break;
188 | case 0x80:
189 | result=readAFL(data,offset+2,len-2);
190 | break;
191 | }
192 |
193 | return result;
194 | }
195 | }
196 |
197 | class ReadAppFCI implements EnumCallback
198 | {
199 | @Override
200 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
201 | {
202 | boolean result=true;
203 |
204 | // System.out.println(String.format("appFCI %04X,%d,",tag,len)+Hex.encode(data, offset, len));
205 |
206 | switch (tag)
207 | {
208 | case 0x50:
209 | try
210 | {
211 | issuer=new String(data,offset,len,UTF8);
212 | }
213 | catch (UnsupportedEncodingException ex)
214 | {
215 | Logger.getLogger(EMVReader.class.getName()).log(Level.SEVERE, null, ex);
216 | }
217 | break;
218 | case 0x9F38:
219 | PDOL=BinaryTools.bytesFrom(data,offset, len);
220 | break;
221 | }
222 | return result;
223 | }
224 |
225 | private boolean read() throws IOException
226 | {
227 | boolean result=true;
228 | byte [] pdolData=null;
229 |
230 | if (PDOL!=null)
231 | {
232 | int pdolLen=getPDOLlength(PDOL,0,PDOL.length);
233 |
234 | pdolData=new byte[pdolLen+2];
235 | pdolData[0]=(byte)0x83;
236 | pdolData[1]=(byte)pdolLen;
237 |
238 | fillPDOL(PDOL,0,PDOL.length,pdolData,2);
239 | }
240 |
241 | if (pdolData==null)
242 | {
243 | pdolData=new byte[]{(byte)0x83,0x00};
244 | }
245 |
246 | byte [] apdu=BinaryTools.catenate(new byte[][]{
247 | new byte[]{(byte)0x80,(byte)0xa8,0x00,0x00,(byte)pdolData.length},
248 | pdolData,
249 | new byte[]{0}
250 | });
251 |
252 | byte [] resp=reader.transceive(apdu);
253 |
254 | if ((resp!=null)&&(resp.length>2))
255 | {
256 | ReadPDO pdo=new ReadPDO();
257 |
258 | result=parse(pdo,resp,0,resp.length-2);
259 | }
260 |
261 | return result;
262 | }
263 | }
264 |
265 | class ReadApplicationDataFileRecord implements EnumCallback
266 | {
267 | @Override
268 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
269 | {
270 | boolean result=true;
271 |
272 | // System.out.println(String.format("app %04X,%d,",tag,len)+Hex.encode(data, offset, len));
273 |
274 | switch (tag)
275 | {
276 | case 0xA5:
277 | {
278 | ReadAppFCI app = new ReadAppFCI();
279 | result=parse(app,data,offset,len);
280 |
281 | if (result)
282 | {
283 | result=app.read();
284 | }
285 | }
286 | break;
287 | }
288 |
289 | return result;
290 | }
291 | }
292 |
293 | class ReadApplicationDataFile implements EnumCallback
294 | {
295 | @Override
296 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
297 | {
298 | boolean result=true;
299 |
300 | // System.out.println(String.format("app %04X,%d,",tag,len)+Hex.encode(data, offset, len));
301 |
302 | switch (tag)
303 | {
304 | case 0x6F:
305 | case 0x70:
306 | result=parse(new ReadApplicationDataFileRecord(),data,offset,len);
307 | break;
308 | }
309 |
310 | return result;
311 | }
312 | }
313 |
314 | class ReadApplicationTemplate implements EnumCallback
315 | {
316 | @Override
317 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
318 | {
319 | boolean result=true;
320 |
321 | // System.out.println(String.format("appTemp %04X,%d,",tag,len)+Hex.encode(data, offset, len));
322 |
323 | switch (tag)
324 | {
325 | case 0x4F:
326 | aid=BinaryTools.bytesFrom(data,offset, len);
327 | break;
328 | case 0x50:
329 | try
330 | {
331 | issuer=new String(data,offset,len,UTF8);
332 | }
333 | catch (UnsupportedEncodingException ex)
334 | {
335 | Logger.getLogger(EMVReader.class.getName()).log(Level.SEVERE, null, ex);
336 | }
337 | break;
338 | }
339 |
340 | return result;
341 | }
342 |
343 | boolean read() throws IOException
344 | {
345 | boolean result=true;
346 | byte [] apdu=BinaryTools.catenate(new byte[][]{
347 | BinaryTools.bytesFrom(SELECT_PPSE,0,4),
348 | new byte[]{(byte)aid.length},
349 | aid,
350 | new byte[]{0}
351 | });
352 | byte [] resp=reader.transceive(apdu);
353 |
354 | if ((resp!=null)&&(resp.length>2))
355 | {
356 | result=parse(new ReadApplicationDataFile(),resp,0,resp.length-2);
357 | }
358 |
359 | return result;
360 | }
361 | }
362 |
363 | class ReadFCIIssuerDiscretionaryData implements EnumCallback
364 | {
365 | @Override
366 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
367 | {
368 | boolean result=true;
369 |
370 | // System.out.println(String.format("FCIidd %04X,%d,",tag,len)+Hex.encode(data, offset, len));
371 |
372 | switch (tag)
373 | {
374 | case 0x61:
375 | {
376 | ReadApplicationTemplate app = new ReadApplicationTemplate();
377 | result=parse(app,data,offset,len);
378 |
379 | if (result)
380 | {
381 | result=app.read();
382 | }
383 | }
384 | break;
385 | }
386 |
387 | return result;
388 | }
389 | }
390 |
391 | class ReadFCIPropTemplate implements EnumCallback
392 | {
393 | @Override
394 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
395 | {
396 | boolean result=true;
397 |
398 | // System.out.println(String.format("FCIpt %04X,%d,",tag,len)+Hex.encode(data, offset, len));
399 |
400 | switch (tag)
401 | {
402 | case 0xBF0C:
403 | result=parse(new ReadFCIIssuerDiscretionaryData(),data,offset,len);
404 | break;
405 | case 0x88:
406 | if (len==1)
407 | {
408 | result=readPSERecord(data[offset]);
409 | }
410 | break;
411 | }
412 |
413 | return result;
414 | }
415 | }
416 |
417 | class ReadPPSErecord implements EnumCallback
418 | {
419 | @Override
420 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
421 | {
422 | boolean result=true;
423 |
424 | // System.out.println(String.format("PPSE %04X,%d,",tag,len)+Hex.encode(data, offset, len));
425 |
426 | switch (tag)
427 | {
428 | case 0xA5:
429 | result=parse(new ReadFCIPropTemplate(),data,offset,len);
430 | break;
431 | case 0x84:
432 | if (false)
433 | {
434 | byte [] aid2=BinaryTools.bytesFrom(data,offset, len);
435 |
436 | if (!matchBytes(aid2,aid))
437 | {
438 | aid=aid2;
439 |
440 | result=new ReadApplicationTemplate().read();
441 | }
442 | }
443 | break;
444 | }
445 |
446 | return result;
447 | }
448 | }
449 |
450 | class ReadPPSE implements EnumCallback
451 | {
452 | @Override
453 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
454 | {
455 | boolean result=true;
456 |
457 | // System.out.println(String.format("PPSE %04X,%d,",tag,len)+Hex.encode(data, offset, len));
458 |
459 | switch (tag)
460 | {
461 | case 0x6f:
462 | result=parse(new ReadPPSErecord(),data,offset,len);
463 | break;
464 | }
465 |
466 | return result;
467 | }
468 | }
469 |
470 | boolean parse(EnumCallback c,byte [] data,int offset,int len) throws IOException
471 | {
472 | boolean b=true;
473 |
474 | if (doTrace)
475 | {
476 | System.err.println("parse "+c.getClass().getSimpleName());
477 | System.err.println(Hex.encode(data, offset, len));
478 | }
479 |
480 | while (b && (len > 0))
481 | {
482 | int tagLen=getTagLen(data,offset,len);
483 |
484 | if (tagLen < 1) break;
485 |
486 | int tag=getTag(data,offset,tagLen);
487 |
488 | offset+=tagLen;
489 | len-=tagLen;
490 |
491 | int lenLen=getLenLen(data,offset,len);
492 |
493 | int dlen=getLen(data,offset,lenLen);
494 |
495 | offset+=lenLen; len-=lenLen;
496 |
497 | if (doTrace)
498 | {
499 | System.err.println("parse: "+String.format("%04X,%d:", tag,dlen)+Hex.encode(data,offset,dlen));
500 | }
501 |
502 | b=c.found(tag, dlen, data, offset);
503 |
504 | len-=dlen;
505 | offset+=dlen;
506 | }
507 |
508 | return b;
509 | }
510 |
511 | public void read() throws IOException
512 | {
513 | byte [] ppse=adf;
514 |
515 | if (ppse==null)
516 | {
517 | ppse=reader.transceive(SELECT_PPSE);
518 | }
519 |
520 | if ((ppse!=null)&&(ppse.length>2))
521 | {
522 | parse(new ReadPPSE(),ppse,0,ppse.length-2);
523 | }
524 | }
525 |
526 | int getPDOLlength(byte[] PDOL, int offset, int len)
527 | {
528 | int tot=0;
529 |
530 | while (len > 0)
531 | {
532 | int tagLen=getTagLen(PDOL,offset,len);
533 | int tag=getTag(PDOL,offset,tagLen);
534 | offset+=tagLen;
535 | len-=tagLen;
536 | int optLen=getLenLen(PDOL,offset,len);
537 | int actLen=getLen(PDOL,offset,optLen);
538 |
539 | offset+=optLen;
540 | len-=optLen;
541 |
542 | // System.out.println(String.format("PDOL %04X,%d",tag,actLen));
543 |
544 | tot+=actLen;
545 | }
546 |
547 | return tot;
548 | }
549 |
550 | void fillPDOL(byte[] PDOL, int offset, int len, byte[] pdolData, int i)
551 | {
552 | while (len > 0)
553 | {
554 | int tagLen=getTagLen(PDOL,offset,len);
555 | int tag=getTag(PDOL,offset,tagLen);
556 | offset+=tagLen;
557 | len-=tagLen;
558 | int optLen=getLenLen(PDOL,offset,len);
559 | int actLen=getLen(PDOL,offset,optLen);
560 |
561 | offset+=optLen;
562 | len-=optLen;
563 |
564 | // System.out.println(String.format("PDOL %04X,%d",tag,actLen));
565 |
566 | switch (tag)
567 | {
568 | case 0x9F1A: /* country code */
569 | if (actLen==2)
570 | {
571 | pdolData[i]=0x05;
572 | pdolData[i+1]=0x54;
573 | }
574 | break;
575 | case 0x5F2A: /* currency */
576 | if (actLen==2)
577 | {
578 | pdolData[i]=0x05;
579 | pdolData[i+1]=0x54;
580 | }
581 | break;
582 | case 0x9F66:
583 | switch (actLen)
584 | {
585 | case 4: /* kernel 3 */
586 | pdolData[i]=0x30;
587 | pdolData[i+1]=0x00;
588 | pdolData[i+2]=0x00;
589 | pdolData[i+3]=0x00;
590 | break;
591 | }
592 | break;
593 | case 0x9F37: /* random number */
594 | if (actLen > 0)
595 | {
596 | Random r=new Random();
597 | byte []m=new byte[actLen];
598 | r.nextBytes(m);
599 | System.arraycopy(m,0,pdolData,i,actLen);
600 | }
601 | break;
602 | }
603 |
604 | i+=actLen;
605 | }
606 | }
607 |
608 | boolean readTrack2Equivalent(byte [] data,int offset,int len)
609 | {
610 | boolean result=true;
611 | String cards=Hex.encode(data, offset, len);
612 | int i=cards.indexOf('D');
613 | if (i > 0)
614 | {
615 | pan=cards.substring(0, i);
616 | expiryYear=Integer.parseInt(cards.substring(i+1,i+3));
617 | expiryMonth=Integer.parseInt(cards.substring(i+3,i+5));
618 | result=false;
619 | }
620 | return result;
621 | }
622 |
623 | boolean readAFL(byte [] data,int offset,int len) throws IOException
624 | {
625 | boolean result=true;
626 |
627 | while (result && (len > 0))
628 | {
629 | byte sfi=data[offset++];
630 | byte firstRec=data[offset++];
631 | byte lastRec=data[offset++];
632 | byte authNum=data[offset++];
633 |
634 | while (result && (firstRec <= lastRec))
635 | {
636 | result=readRecord((byte)(0x1f & (sfi>>3)),firstRec);
637 |
638 | firstRec++;
639 | }
640 |
641 | len-=4;
642 | }
643 |
644 | return result;
645 | }
646 |
647 | class ReadRecordData implements EnumCallback
648 | {
649 | @Override
650 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
651 | {
652 | boolean result=true;
653 |
654 | switch (tag)
655 | {
656 | case 0x57:
657 | case 0x9f6b:
658 | result=readTrack2Equivalent(data,offset,len);
659 | break;
660 | case 0x5A:
661 | pan=Hex.encode(data, offset, len);
662 | result=(expiryMonth==null);
663 | break;
664 | case 0x5F24:
665 | expiryMonth=Integer.parseInt(String.format("%x",data[offset+1]));
666 | expiryYear=Integer.parseInt(String.format("%x",data[offset]));
667 | result=(pan==null);
668 | break;
669 | }
670 |
671 | return result;
672 | }
673 | }
674 |
675 | class ReadRecord implements EnumCallback
676 | {
677 | @Override
678 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
679 | {
680 | boolean result=true;
681 |
682 | // System.out.println(String.format("RR %04X,%d,",tag,len)+Hex.encode(data, offset, len));
683 |
684 | switch (tag)
685 | {
686 | case 0x70:
687 | result=parse(new ReadRecordData(),data,offset,len);
688 | break;
689 | }
690 |
691 | return result;
692 | }
693 | }
694 |
695 | class ReadPSERecord implements EnumCallback
696 | {
697 | @Override
698 | public boolean found(int tag, int len, byte[] data, int offset) throws IOException
699 | {
700 | boolean result=true;
701 |
702 | switch (tag)
703 | {
704 | case 0x70:
705 | result=parse(new ReadFCIIssuerDiscretionaryData(),data,offset,len);
706 | break;
707 | }
708 |
709 | return result;
710 | }
711 | }
712 |
713 | boolean readPSERecord(byte sfi) throws IOException
714 | {
715 | byte num=1;
716 | boolean result=true;
717 | byte []apdu={0x00,(byte)0xB2,num,(byte)((sfi<<3)+4),0x00 };
718 | byte []data=reader.transceive(apdu);
719 |
720 | if ((data!=null)&&(data.length==2)&&(data[0]==0x6c))
721 | {
722 | byte []apduLen={0x00,(byte)0xB2,num,(byte)((sfi<<3)+4),data[1]};
723 | data=reader.transceive(apduLen);
724 | }
725 |
726 | if ((data!=null)&&(data.length>2))
727 | {
728 | result=parse(new ReadPSERecord(),data,0,data.length-2);
729 | }
730 |
731 | return result;
732 | }
733 |
734 | boolean readRecord(byte sfi, byte num) throws IOException
735 | {
736 | boolean result=true;
737 | byte []apdu={0x00,(byte)0xB2,num,(byte)((sfi<<3)+4),0x00 };
738 | byte []data=reader.transceive(apdu);
739 |
740 | if ((data!=null)&&(data.length==2)&&(data[0]==0x6c))
741 | {
742 | byte []apduLen={0x00,(byte)0xB2,num,(byte)((sfi<<3)+4),data[1]};
743 | data=reader.transceive(apduLen);
744 | }
745 |
746 | if ((data!=null)&&(data.length>2))
747 | {
748 | result=parse(new ReadRecord(),data,0,data.length-2);
749 | }
750 |
751 | return result;
752 | }
753 |
754 | static boolean matchBytes(byte[] aid2, byte[] aid)
755 | {
756 | boolean match=(aid==null)&&(aid2==null);
757 |
758 | if (!match)
759 | {
760 | if ((aid!=null)&&(aid2!=null))
761 | {
762 | if (aid.length==aid2.length)
763 | {
764 | int i=aid.length;
765 |
766 | while (0!=i--)
767 | {
768 | if (aid[i]!=aid2[i])
769 | {
770 | break;
771 | }
772 | }
773 |
774 | match=(i < 0);
775 | }
776 | }
777 | }
778 |
779 | return match;
780 | }
781 | }
782 |
--------------------------------------------------------------------------------
/app/src/main/java/com/gauravtproject/android/emvreader/app/emvtools/Hex.java:
--------------------------------------------------------------------------------
1 | /**************************************************************************
2 | *
3 | * Copyright 2014, Roger Brown
4 | *
5 | * This file is part of Roger Brown's Toolkit.
6 | *
7 | * This program is free software: you can redistribute it and/or modify it
8 | * under the terms of the GNU Lesser General Public License as published by the
9 | * Free Software Foundation, either version 3 of the License, or (at your
10 | * option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful, but WITHOUT
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 | * more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with this program. If not, see
19 | *
20 | */
21 |
22 | /*
23 | * $Id: Hex.java 1 2014-06-07 22:37:15Z rhubarb-geek-nz $
24 | */
25 |
26 | package com.gauravtproject.android.emvreader.app.emvtools;
27 |
28 | /**
29 | * Re-Created by Gaurav Tandon on 8/03/2015.
30 | */
31 | /**
32 | * hex conversion routines
33 | * @author rogerb
34 | * @version $Id: Hex.java 1 2014-06-07 22:37:15Z rhubarb-geek-nz $
35 | */
36 | public class Hex
37 | {
38 | static final char [] map={
39 | '0','1','2','3',
40 | '4','5','6','7',
41 | '8','9','A','B',
42 | 'C','D','E','F'
43 | };
44 |
45 | public static String encode(byte[] a, int offset, int length)
46 | {
47 | char []sb= new char[length * 2];
48 | int i=0;
49 |
50 | while (0 != length--)
51 | {
52 | byte b = a[offset++];
53 |
54 | sb[i++]=map[(b >> 4) & 0xf];
55 | sb[i++]=map[b & 0xf];
56 | }
57 |
58 | return String.valueOf(sb,0,i);
59 | }
60 |
61 | public static byte[] decode(String s)
62 | {
63 | byte[] a = new byte[s.length() >> 1];
64 | int i = 0;
65 | int o = 0;
66 |
67 | while (i < s.length())
68 | {
69 | byte b = nybble(s.charAt(i++));
70 | if (b >= 0)
71 | {
72 | if (0==(o & 1))
73 | {
74 | a[o>>1]|=(byte)(b<<4);
75 | }
76 | else
77 | {
78 | a[o>>1]|=b;
79 | }
80 | o++;
81 | }
82 | }
83 |
84 | if (0==(o & 1))
85 | {
86 | o>>=1;
87 |
88 | if (o != a.length)
89 | {
90 | byte[]c=new byte[o];
91 | System.arraycopy(a,0,c,0,o);
92 | return c;
93 | }
94 |
95 | return a;
96 | }
97 |
98 | return null;
99 | }
100 |
101 | static byte nybble(char c)
102 | {
103 | if (c > 'F')
104 | {
105 | c -= 32;
106 | }
107 |
108 | if (c > '9')
109 | {
110 | c -= 7;
111 | }
112 |
113 | if ((c >= '0') && (c < ('0' + 16)))
114 | {
115 | return (byte)(c-'0');
116 | }
117 |
118 | return -1;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/emv_reader_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gaurav2Github/EMVReader/6ff6c14f8d6e7eb09b13e38b4a76ed6ddbe51d16/app/src/main/res/drawable/emv_reader_image.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/card_viewer.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
16 |
23 |
30 |
37 |
38 |
45 |
46 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_card_viewer.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gaurav2Github/EMVReader/6ff6c14f8d6e7eb09b13e38b4a76ed6ddbe51d16/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gaurav2Github/EMVReader/6ff6c14f8d6e7eb09b13e38b4a76ed6ddbe51d16/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gaurav2Github/EMVReader/6ff6c14f8d6e7eb09b13e38b4a76ed6ddbe51d16/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gaurav2Github/EMVReader/6ff6c14f8d6e7eb09b13e38b4a76ed6ddbe51d16/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gaurav2Github/EMVReader/6ff6c14f8d6e7eb09b13e38b4a76ed6ddbe51d16/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EMVReader
3 |
4 | Hello world!
5 | Settings
6 |
7 | PAN:
8 | Issuer:
9 | Expiry Month:
10 | Expiry Year:
11 |
12 | Not Available
13 |
14 | NFC is not enabled. Please go to the wireless settings to enable it.
15 | Error
16 | No NFC found on this device
17 | Welcome to EMV Reader. \nPlease scan your payPass/payWave card with the back of this phone.
18 | Details of the scanned card is as below:
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/nfc_tech_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | android.nfc.tech.IsoDep
5 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.1.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 |
14 | }
15 |
16 |
17 | allprojects {
18 | repositories {
19 | jcenter()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gaurav2Github/EMVReader/6ff6c14f8d6e7eb09b13e38b4a76ed6ddbe51d16/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------