├── .gitignore
├── .idea
├── caches
│ ├── build_file_checksums.ser
│ └── gradle_models.ser
├── codeStyles
│ └── Project.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── android-hostcardemulation-sample.iml
├── apdu_exchange_tester
├── README.md
└── apdu_tag_test.c
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── justinribeiro
│ │ └── demo
│ │ └── apps
│ │ └── hostcardemulation
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── justinribeiro
│ │ └── demo
│ │ └── apps
│ │ └── hostcardemulation
│ │ ├── myHostApduService.java
│ │ ├── myStartActivity.java
│ │ └── utils.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── layout-v21
│ └── activity_start.xml
│ ├── layout
│ └── activity_start.xml
│ ├── menu
│ └── start.xml
│ ├── values-v21
│ └── styles.xml
│ ├── values-w820dp
│ └── dimens.xml
│ ├── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ └── apduservice.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── hostcardemulation.iml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 |
8 | ### Android ###
9 | # Built application files
10 | *.apk
11 | *.ap_
12 |
13 | # Files for the Dalvik VM
14 | *.dex
15 |
16 | # Java class files
17 | *.class
18 |
19 | # Generated files
20 | bin/
21 | gen/
22 |
23 | # Gradle files
24 | .gradle/
25 | build/
26 |
27 | # Local configuration file (sdk path, etc)
28 | local.properties
29 |
30 | # Proguard folder generated by Eclipse
31 | proguard/
32 |
33 | # Log Files
34 | *.log
35 |
36 |
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justinribeiro/android-hostcardemulation-sample/a078d90ddd59b4ba4ad7cb52ce6cc30f21780405/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/.idea/caches/gradle_models.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justinribeiro/android-hostcardemulation-sample/a078d90ddd59b4ba4ad7cb52ce6cc30f21780405/.idea/caches/gradle_models.ser
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android Host Card Emulation of a NFC Forum Type-4 tag
2 |
3 | This example emulates a NFC Forum Type-4 tag with an a single NDEF record (RTD_TEXT). The project uses aid-filter F0394148148100 (which is an example that is defined in the Android documentation) for the APDU SELECT command.
4 |
5 | ## Whoa there what?
6 |
7 | The NFC Forum Type 4 Tag Operation Specification 3.0 outlines how to interact with tags. Using Host Card Emulation in Android, we can do something pretty nifty:
8 |
9 | 1. We can write an application on the card reader side that sends the proper APDU SELECT and associated commands and interact with a "card".
10 | 2. That "card" in this case is emulated in our Android application.
11 |
12 | ## Where can I get the tag operational spec?
13 |
14 | There are various copies on the Internet, but the place to go is to sign the specification license and download direct from the [NFC Forum document library](http://members.nfc-forum.org/specs/spec_license/document_form/).
15 |
16 | ## How do I interact with the Android application through a card reader?
17 |
18 | You have to follow the commands in your client application. You can also compile and run the sample in the apdu_exchange_tester directory that is based on the APDU examples from libNFC (for detailed instructions, see the readme in that folder).
19 |
20 | ## Where can I learn more about Host Card Emulation on Android?
21 |
22 | You'll need API 19 (aka Android 4.4) to use HCE. See [Host-based Card Emulation @ developers.android.com](https://developer.android.com/guide/topics/connectivity/nfc/hce.html) for all the details.
23 |
24 | ## What if I want to interact with or read a Type-4 tag?
25 |
26 | This is a different sort of thing and you'll want to look into [ISO-DEP](http://developer.android.com/reference/android/nfc/tech/IsoDep.html).
--------------------------------------------------------------------------------
/android-hostcardemulation-sample.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/apdu_exchange_tester/README.md:
--------------------------------------------------------------------------------
1 | # Testing the Android Host Card Emulation of a NFC Forum Type-4 tag
2 |
3 | The following program takes a command by command approach to testing our ISO14443-4 emulated tag. It is an extended version of the existing [libnfc apdu example](http://nfc-tools.org/index.php?title=Libnfc:APDU_example).
4 |
5 | ## What do I need to run this?
6 |
7 | You'll need a couple of things:
8 |
9 | 1. An NFC Reader. I've used this with the [ACR122U USB NFC Reader](http://www.acs.com.hk/en/products/3/acr122u-usb-nfc-reader) without issue.
10 | 2. [Libnfc](http://nfc-tools.org/index.php?title=Libnfc). You'll likely be able to install from your package manager of choice.
11 |
12 | ## Compile
13 |
14 | ```
15 | gcc -o apdu_tag_test apdu_tag_test.c -lnfc
16 | ```
17 |
18 | ## Run
19 |
20 | ```
21 | ./apdu_tag_test
22 | ```
23 |
24 | ## Sample Output
25 |
26 | ```
27 | $ ./apdu_tag_test
28 |
29 | Running checks...
30 | ./apdu_tag_test uses libnfc 1.7.1
31 | NFC reader: ACS / ACR122U PICC Interface opened
32 | Polling for target...
33 | Target detected! Running command set...
34 |
35 | Sending ADPU SELECT...
36 | => 00 a4 04 00 07 f0 39 41 48 14 81 00 00
37 | <= 90 00
38 | Application selected!
39 |
40 | Sending CC SELECT...
41 | => 00 a4 00 0c 02 e1 03
42 | <= 90 00
43 | Capability Container selected!
44 |
45 | Sending ReadBinary from CC...
46 | => 00 b0 00 00 0f
47 | <= 00 0f 20 00 3b 00 34 04 06 e1 04 00 32 00 00 90 00
48 |
49 | Capability Container header:
50 | 00 0f 20 00 3b 00 34 04 06 e1 04 00 32 00 00
51 |
52 | Sending NDEF Select...
53 | => 00 a4 00 0c 02 e1 04
54 | <= 90 00
55 |
56 | Sending ReadBinary NLEN...
57 | => 00 b0 00 00 02
58 | <= 00 13 90 00
59 |
60 | Sending ReadBinary, get NDEF data...
61 | => 00 b0 00 00 0f
62 | <= 00 13 d9 01 0c 02 54 e1 04 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 90 00
63 |
64 | Wrapping up, closing session.
65 | ```
66 |
67 | ## Things this doesn't do
68 | It's not nearly as strict to the spec as it should be, but for simple testing it works well.
69 |
--------------------------------------------------------------------------------
/apdu_exchange_tester/apdu_tag_test.c:
--------------------------------------------------------------------------------
1 | /**
2 | Modified from original example @ Libnfc:
3 | http://nfc-tools.org/index.php?title=Libnfc:APDU_example
4 |
5 | Allows testing of Host-based Card Emulatio Android example at
6 | https://github.com/justinribeiro/android-hostcardemulation-sample
7 |
8 | Requires Libnfc:
9 | http://nfc-tools.org/index.php?title=Libnfc
10 |
11 | To compile:
12 | gcc -o apdu_tag_test apdu_tag_test.c -lnfc
13 | */
14 | #include
15 | #include
16 | #include
17 | int
18 | CardTransmit(nfc_device *pnd, uint8_t * capdu, size_t capdulen, uint8_t * rapdu, size_t * rapdulen)
19 | {
20 | int res;
21 | size_t szPos;
22 | printf("=> ");
23 | for (szPos = 0; szPos < capdulen; szPos++) {
24 | printf("%02x ", capdu[szPos]);
25 | }
26 | printf("\n");
27 | if ((res = nfc_initiator_transceive_bytes(pnd, capdu, capdulen, rapdu, *rapdulen, 500)) < 0) {
28 | return -1;
29 | } else {
30 | *rapdulen = (size_t) res;
31 | printf("<= ");
32 | for (szPos = 0; szPos < *rapdulen; szPos++) {
33 | printf("%02x ", rapdu[szPos]);
34 | }
35 | printf("\n");
36 | return 0;
37 | }
38 | }
39 | int
40 | main(int argc, const char *argv[])
41 | {
42 | nfc_device *pnd;
43 | nfc_target nt;
44 | nfc_context *context;
45 | nfc_init(&context);
46 |
47 | printf("\nRunning checks...\n");
48 |
49 | if (context == NULL) {
50 | printf("Unable to init libnfc (malloc)\n");
51 | exit(EXIT_FAILURE);
52 | }
53 |
54 | const char *acLibnfcVersion = nfc_version();
55 | (void)argc;
56 | printf("%s uses libnfc %s\n", argv[0], acLibnfcVersion);
57 |
58 | pnd = nfc_open(context, NULL);
59 |
60 | if (pnd == NULL) {
61 | printf("ERROR: %s", "Unable to open NFC device.");
62 | exit(EXIT_FAILURE);
63 | }
64 | if (nfc_initiator_init(pnd) < 0) {
65 | nfc_perror(pnd, "nfc_initiator_init");
66 | exit(EXIT_FAILURE);
67 | }
68 |
69 | printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));
70 |
71 | const nfc_modulation nmMifare = {
72 | .nmt = NMT_ISO14443A,
73 | .nbr = NBR_106,
74 | };
75 | // nfc_set_property_bool(pnd, NP_AUTO_ISO14443_4, true);
76 | printf("Polling for target...\n");
77 | while (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0);
78 | printf("Target detected! Running command set...\n\n");
79 | uint8_t capdu[264];
80 | size_t capdulen;
81 | uint8_t rapdu[264];
82 | size_t rapdulen;
83 | // Select application
84 | memcpy(capdu, "\x00\xA4\x04\x00\x07\xF0\x39\x41\x48\x14\x81\x00\x00", 13);
85 | capdulen=13;
86 | rapdulen=sizeof(rapdu);
87 |
88 | printf("Sending ADPU SELECT...\n");
89 | if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0) {
90 | exit(EXIT_FAILURE);
91 | }
92 | if (rapdulen < 2 || rapdu[rapdulen-2] != 0x90 || rapdu[rapdulen-1] != 0x00) {
93 | exit(EXIT_FAILURE);
94 | }
95 | printf("Application selected!\n\n");
96 |
97 | // Select Capability Container
98 | memcpy(capdu, "\x00\xa4\x00\x0c\x02\xe1\x03", 7);
99 | capdulen=7;
100 | rapdulen=sizeof(rapdu);
101 |
102 | printf("Sending CC SELECT...\n");
103 | if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0)
104 | exit(EXIT_FAILURE);
105 | if (rapdulen < 2 || rapdu[rapdulen-2] != 0x90 || rapdu[rapdulen-1] != 0x00) {
106 | capdu[3]='\x00'; // Maybe an older Tag4 ?
107 | if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0)
108 | exit(EXIT_FAILURE);
109 | }
110 | printf("Capability Container selected!\n\n");
111 |
112 | // Read Capability Container
113 | memcpy(capdu, "\x00\xb0\x00\x00\x0f", 5);
114 | capdulen=5;
115 | rapdulen=sizeof(rapdu);
116 |
117 | printf("Sending ReadBinary from CC...\n");
118 | if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0)
119 | exit(EXIT_FAILURE);
120 | if (rapdulen < 2 || rapdu[rapdulen-2] != 0x90 || rapdu[rapdulen-1] != 0x00)
121 | exit(EXIT_FAILURE);
122 | printf("\nCapability Container header:\n");
123 | size_t szPos;
124 | for (szPos = 0; szPos < rapdulen-2; szPos++) {
125 | printf("%02x ", rapdu[szPos]);
126 | }
127 | printf("\n\n");
128 |
129 | // NDEF Select
130 | memcpy(capdu, "\x00\xa4\x00\x0C\x02\xE1\x04", 7);
131 | capdulen=7;
132 | rapdulen=sizeof(rapdu);
133 | printf("Sending NDEF Select...\n");
134 | if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0)
135 | exit(EXIT_FAILURE);
136 | if (rapdulen < 2 || rapdu[rapdulen-2] != 0x90 || rapdu[rapdulen-1] != 0x00)
137 | exit(EXIT_FAILURE);
138 | printf("\n");
139 |
140 | // ReadBinary
141 | memcpy(capdu, "\x00\xb0\x00\x00\x02", 5);
142 | capdulen=5;
143 | rapdulen=sizeof(rapdu);
144 | printf("Sending ReadBinary NLEN...\n");
145 | if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0)
146 | exit(EXIT_FAILURE);
147 | if (rapdulen < 2 || rapdu[rapdulen-2] != 0x90 || rapdu[rapdulen-1] != 0x00)
148 | exit(EXIT_FAILURE);
149 | printf("\n");
150 |
151 | // ReadBinary - Get NDEF data
152 | memcpy(capdu, "\x00\xb0\x00\x00\x0f", 5);
153 | capdulen=5;
154 | rapdulen=sizeof(rapdu);
155 | printf("Sending ReadBinary, get NDEF data...\n");
156 | if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0)
157 | exit(EXIT_FAILURE);
158 | if (rapdulen < 2 || rapdu[rapdulen-2] != 0x90 || rapdu[rapdulen-1] != 0x00)
159 | exit(EXIT_FAILURE);
160 | printf("\n");
161 |
162 | printf("Wrapping up, closing session.\n\n");
163 |
164 | nfc_close(pnd);
165 | nfc_exit(context);
166 | exit(EXIT_SUCCESS);
167 | }
168 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | generateDebugSources
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion '29.0.2'
6 |
7 | defaultConfig {
8 | applicationId "com.justinribeiro.demo.apps.hostcardemulation"
9 | minSdkVersion 19
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled true
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation 'androidx.appcompat:appcompat:1.0.0'
24 | }
25 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Android\android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/justinribeiro/demo/apps/hostcardemulation/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.justinribeiro.demo.apps.hostcardemulation;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/justinribeiro/demo/apps/hostcardemulation/myHostApduService.java:
--------------------------------------------------------------------------------
1 | package com.justinribeiro.demo.apps.hostcardemulation;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.nfc.NdefRecord;
6 | import android.nfc.cardemulation.HostApduService;
7 | import android.os.Bundle;
8 | import android.util.Log;
9 | import android.view.Gravity;
10 | import android.widget.Toast;
11 |
12 | import java.math.BigInteger;
13 | import java.nio.charset.Charset;
14 | import java.util.Arrays;
15 |
16 | /**
17 | * Created by justin.ribeiro on 10/27/2014.
18 | *
19 | * The following definitions are based on two things:
20 | * 1. NFC Forum Type 4 Tag Operation Technical Specification, version 3.0 2014-07-30
21 | * 2. APDU example in libnfc: http://nfc-tools.org/index.php?title=Libnfc:APDU_example
22 | *
23 | */
24 | public class myHostApduService extends HostApduService {
25 |
26 | private static final String TAG = "JDR HostApduService";
27 |
28 | //
29 | // We use the default AID from the HCE Android documentation
30 | // https://developer.android.com/guide/topics/connectivity/nfc/hce.html
31 | //
32 | // Ala...
33 | //
34 | private static final byte[] APDU_SELECT = {
35 | (byte)0x00, // CLA - Class - Class of instruction
36 | (byte)0xA4, // INS - Instruction - Instruction code
37 | (byte)0x04, // P1 - Parameter 1 - Instruction parameter 1
38 | (byte)0x00, // P2 - Parameter 2 - Instruction parameter 2
39 | (byte)0x07, // Lc field - Number of bytes present in the data field of the command
40 | (byte)0xF0, (byte)0x39, (byte)0x41, (byte)0x48, (byte)0x14, (byte)0x81, (byte)0x00, // NDEF Tag Application name
41 | (byte)0x00 // Le field - Maximum number of bytes expected in the data field of the response to the command
42 | };
43 |
44 | private static final byte[] CAPABILITY_CONTAINER = {
45 | (byte)0x00, // CLA - Class - Class of instruction
46 | (byte)0xa4, // INS - Instruction - Instruction code
47 | (byte)0x00, // P1 - Parameter 1 - Instruction parameter 1
48 | (byte)0x0c, // P2 - Parameter 2 - Instruction parameter 2
49 | (byte)0x02, // Lc field - Number of bytes present in the data field of the command
50 | (byte)0xe1, (byte)0x03 // file identifier of the CC file
51 | };
52 |
53 | private static final byte[] READ_CAPABILITY_CONTAINER = {
54 | (byte)0x00, // CLA - Class - Class of instruction
55 | (byte)0xb0, // INS - Instruction - Instruction code
56 | (byte)0x00, // P1 - Parameter 1 - Instruction parameter 1
57 | (byte)0x00, // P2 - Parameter 2 - Instruction parameter 2
58 | (byte)0x0f // Lc field - Number of bytes present in the data field of the command
59 | };
60 |
61 | // In the scenario that we have done a CC read, the same byte[] match
62 | // for ReadBinary would trigger and we don't want that in succession
63 | private boolean READ_CAPABILITY_CONTAINER_CHECK = false;
64 |
65 | private static final byte[] READ_CAPABILITY_CONTAINER_RESPONSE = {
66 | (byte)0x00, (byte)0x0F, // CCLEN length of the CC file
67 | (byte)0x20, // Mapping Version 2.0
68 | (byte)0x00, (byte)0x3B, // MLe maximum 59 bytes R-APDU data size
69 | (byte)0x00, (byte)0x34, // MLc maximum 52 bytes C-APDU data size
70 | (byte)0x04, // T field of the NDEF File Control TLV
71 | (byte)0x06, // L field of the NDEF File Control TLV
72 | (byte)0xE1, (byte)0x04, // File Identifier of NDEF file
73 | (byte)0x00, (byte)0x32, // Maximum NDEF file size of 50 bytes
74 | (byte)0x00, // Read access without any security
75 | (byte)0x00, // Write access without any security
76 | (byte)0x90, (byte)0x00 // A_OKAY
77 | };
78 |
79 | private static final byte[] NDEF_SELECT = {
80 | (byte)0x00, // CLA - Class - Class of instruction
81 | (byte)0xa4, // Instruction byte (INS) for Select command
82 | (byte)0x00, // Parameter byte (P1), select by identifier
83 | (byte)0x0c, // Parameter byte (P1), select by identifier
84 | (byte)0x02, // Lc field - Number of bytes present in the data field of the command
85 | (byte)0xE1, (byte)0x04 // file identifier of the NDEF file retrieved from the CC file
86 | };
87 |
88 | private static final byte[] NDEF_READ_BINARY_NLEN = {
89 | (byte)0x00, // Class byte (CLA)
90 | (byte)0xb0, // Instruction byte (INS) for ReadBinary command
91 | (byte)0x00, (byte)0x00, // Parameter byte (P1, P2), offset inside the CC file
92 | (byte)0x02 // Le field
93 | };
94 |
95 | private static final byte[] NDEF_READ_BINARY_GET_NDEF = {
96 | (byte)0x00, // Class byte (CLA)
97 | (byte)0xb0, // Instruction byte (INS) for ReadBinary command
98 | (byte)0x00, (byte)0x00, // Parameter byte (P1, P2), offset inside the CC file
99 | (byte)0x0f // Le field
100 | };
101 |
102 | private static final byte[] A_OKAY = {
103 | (byte)0x90, // SW1 Status byte 1 - Command processing status
104 | (byte)0x00 // SW2 Status byte 2 - Command processing qualifier
105 | };
106 |
107 | private static final byte[] NDEF_ID = {
108 | (byte)0xE1,
109 | (byte)0x04
110 | };
111 |
112 | private NdefRecord NDEF_URI = new NdefRecord(
113 | NdefRecord.TNF_WELL_KNOWN,
114 | NdefRecord.RTD_TEXT,
115 | NDEF_ID,
116 | "Hello world!".getBytes(Charset.forName("UTF-8"))
117 | );
118 | private byte[] NDEF_URI_BYTES = NDEF_URI.toByteArray();
119 | private byte[] NDEF_URI_LEN = BigInteger.valueOf(NDEF_URI_BYTES.length).toByteArray();
120 |
121 | @Override
122 | public int onStartCommand(Intent intent, int flags, int startId) {
123 |
124 | if (intent.hasExtra("ndefMessage")) {
125 | NDEF_URI = new NdefRecord(
126 | NdefRecord.TNF_WELL_KNOWN,
127 | NdefRecord.RTD_TEXT,
128 | NDEF_ID,
129 | intent.getStringExtra("ndefMessage").getBytes(Charset.forName("UTF-8"))
130 | );
131 |
132 | NDEF_URI_BYTES = NDEF_URI.toByteArray();
133 | NDEF_URI_LEN = BigInteger.valueOf(NDEF_URI_BYTES.length).toByteArray();
134 |
135 | Context context = getApplicationContext();
136 | CharSequence text = "Your NDEF text has been set!";
137 | int duration = Toast.LENGTH_SHORT;
138 | Toast toast = Toast.makeText(context, text, duration);
139 | toast.setGravity(Gravity.CENTER, 0, 0);
140 | toast.show();
141 | }
142 |
143 | Log.i(TAG, "onStartCommand() | NDEF" + NDEF_URI.toString());
144 |
145 | return 0;
146 | }
147 |
148 | @Override
149 | public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
150 |
151 | //
152 | // The following flow is based on Appendix E "Example of Mapping Version 2.0 Command Flow"
153 | // in the NFC Forum specification
154 | //
155 | Log.i(TAG, "processCommandApdu() | incoming commandApdu: " + utils.bytesToHex(commandApdu));
156 |
157 | //
158 | // First command: NDEF Tag Application select (Section 5.5.2 in NFC Forum spec)
159 | //
160 | if (utils.isEqual(APDU_SELECT, commandApdu)) {
161 | Log.i(TAG, "APDU_SELECT triggered. Our Response: " + utils.bytesToHex(A_OKAY));
162 | return A_OKAY;
163 | }
164 |
165 | //
166 | // Second command: Capability Container select (Section 5.5.3 in NFC Forum spec)
167 | //
168 | if (utils.isEqual(CAPABILITY_CONTAINER, commandApdu)) {
169 | Log.i(TAG, "CAPABILITY_CONTAINER triggered. Our Response: " + utils.bytesToHex(A_OKAY));
170 | return A_OKAY;
171 | }
172 |
173 | //
174 | // Third command: ReadBinary data from CC file (Section 5.5.4 in NFC Forum spec)
175 | //
176 | if (utils.isEqual(READ_CAPABILITY_CONTAINER, commandApdu) && !READ_CAPABILITY_CONTAINER_CHECK) {
177 | Log.i(TAG, "READ_CAPABILITY_CONTAINER triggered. Our Response: " + utils.bytesToHex(READ_CAPABILITY_CONTAINER_RESPONSE));
178 | READ_CAPABILITY_CONTAINER_CHECK = true;
179 | return READ_CAPABILITY_CONTAINER_RESPONSE;
180 | }
181 |
182 | //
183 | // Fourth command: NDEF Select command (Section 5.5.5 in NFC Forum spec)
184 | //
185 | if (utils.isEqual(NDEF_SELECT, commandApdu)) {
186 | Log.i(TAG, "NDEF_SELECT triggered. Our Response: " + utils.bytesToHex(A_OKAY));
187 | return A_OKAY;
188 | }
189 |
190 | //
191 | // Fifth command: ReadBinary, read NLEN field
192 | //
193 | if (utils.isEqual(NDEF_READ_BINARY_NLEN, commandApdu)) {
194 |
195 | byte[] start = {
196 | (byte)0x00
197 | };
198 |
199 | // Build our response
200 | byte[] response = new byte[start.length + NDEF_URI_LEN.length + A_OKAY.length];
201 |
202 | System.arraycopy(start, 0, response, 0, start.length);
203 | System.arraycopy(NDEF_URI_LEN, 0, response, start.length, NDEF_URI_LEN.length);
204 | System.arraycopy(A_OKAY, 0, response, start.length + NDEF_URI_LEN.length, A_OKAY.length);
205 |
206 | Log.i(TAG, response.toString());
207 | Log.i(TAG, "NDEF_READ_BINARY_NLEN triggered. Our Response: " + utils.bytesToHex(response));
208 |
209 | return response;
210 | }
211 |
212 | //
213 | // Sixth command: ReadBinary, get NDEF data
214 | //
215 | if (utils.isEqual(NDEF_READ_BINARY_GET_NDEF, commandApdu)) {
216 | Log.i(TAG, "processCommandApdu() | NDEF_READ_BINARY_GET_NDEF triggered");
217 |
218 | byte[] start = {
219 | (byte)0x00
220 | };
221 |
222 | // Build our response
223 | byte[] response = new byte[start.length + NDEF_URI_LEN.length + NDEF_URI_BYTES.length + A_OKAY.length];
224 |
225 | System.arraycopy(start, 0, response, 0, start.length);
226 | System.arraycopy(NDEF_URI_LEN, 0, response, start.length, NDEF_URI_LEN.length);
227 | System.arraycopy(NDEF_URI_BYTES, 0, response, start.length + NDEF_URI_LEN.length, NDEF_URI_BYTES.length);
228 | System.arraycopy(A_OKAY, 0, response, start.length + NDEF_URI_LEN.length + NDEF_URI_BYTES.length, A_OKAY.length);
229 |
230 | Log.i(TAG, NDEF_URI.toString());
231 | Log.i(TAG, "NDEF_READ_BINARY_GET_NDEF triggered. Our Response: " + utils.bytesToHex(response));
232 |
233 | Context context = getApplicationContext();
234 | CharSequence text = "NDEF text has been sent to the reader!";
235 | int duration = Toast.LENGTH_SHORT;
236 | Toast toast = Toast.makeText(context, text, duration);
237 | toast.setGravity(Gravity.CENTER, 0, 0);
238 | toast.show();
239 |
240 | READ_CAPABILITY_CONTAINER_CHECK = false;
241 | return response;
242 | }
243 |
244 | //
245 | // We're doing something outside our scope
246 | //
247 | Log.wtf(TAG, "processCommandApdu() | I don't know what's going on!!!.");
248 | return "Can I help you?".getBytes();
249 | }
250 |
251 | @Override
252 | public void onDeactivated(int reason) {
253 | Log.i(TAG, "onDeactivated() Fired! Reason: " + reason);
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/app/src/main/java/com/justinribeiro/demo/apps/hostcardemulation/myStartActivity.java:
--------------------------------------------------------------------------------
1 | package com.justinribeiro.demo.apps.hostcardemulation;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import androidx.appcompat.app.AppCompatActivity;
6 | import androidx.appcompat.app.ActionBar;
7 |
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.TextView;
11 |
12 | public class myStartActivity extends AppCompatActivity {
13 | private static final String TAG = "JDR HostCardEmulation";
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.activity_start);
19 |
20 | ActionBar toolbar = getSupportActionBar();
21 | toolbar.setTitle(R.string.app_name);
22 |
23 | Button setNdef = (Button) findViewById(R.id.set_ndef_button);
24 | setNdef.setOnClickListener(new View.OnClickListener() {
25 | @Override
26 | public void onClick(View view) {
27 |
28 | //
29 | // Technically, if this is past our byte limit,
30 | // it will cause issues.
31 | //
32 | // TODO: add validation
33 | //
34 | TextView getNdefString = (TextView) findViewById(R.id.ndef_text);
35 | String test = getNdefString.getText().toString();
36 |
37 | Intent intent = new Intent(view.getContext(), myHostApduService.class);
38 | intent.putExtra("ndefMessage", test);
39 | startService(intent);
40 | }
41 | }
42 | );
43 | }
44 |
45 | @Override
46 | public void onResume() {
47 | super.onResume();
48 | }
49 |
50 | @Override
51 | public void onPause() {
52 | super.onPause();
53 | }
54 |
55 | @Override
56 | public void onStop() {
57 | super.onStop();
58 | }
59 |
60 | @Override
61 | public void onDestroy() {
62 | super.onDestroy();
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/justinribeiro/demo/apps/hostcardemulation/utils.java:
--------------------------------------------------------------------------------
1 | package com.justinribeiro.demo.apps.hostcardemulation;
2 |
3 | /**
4 | * Created by justin.ribeiro on 10/28/2014.
5 | */
6 |
7 | /**
8 | * Just a tiny class to dump things that I may want to use
9 | *
10 | * AKA: you probably don't need this, but you might :-)
11 | */
12 | public class utils {
13 |
14 | final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
15 |
16 | /**
17 | * Simple way to output byte[] to hex (my readable preference)
18 | * This version quite speedy; originally from: http://stackoverflow.com/a/9855338
19 | *
20 | * @param bytes yourByteArray
21 | * @return string
22 | *
23 | */
24 | public static String bytesToHex(byte[] bytes) {
25 | char[] hexChars = new char[bytes.length * 2];
26 | for ( int j = 0; j < bytes.length; j++ ) {
27 | int v = bytes[j] & 0xFF;
28 | hexChars[j * 2] = hexArray[v >>> 4];
29 | hexChars[j * 2 + 1] = hexArray[v & 0x0F];
30 | }
31 | return new String(hexChars);
32 | }
33 |
34 | /**
35 | * Constant-time Byte Array Comparison
36 | * Less overheard, safer. Originally from: http://codahale.com/a-lesson-in-timing-attacks/
37 | *
38 | * @param bytes yourByteArrayA
39 | * @param bytes yourByteArrayB
40 | * @return boolean
41 | *
42 | */
43 | public static boolean isEqual(byte[] a, byte[] b) {
44 | if (a.length != b.length) {
45 | return false;
46 | }
47 |
48 | int result = 0;
49 | for (int i = 0; i < a.length; i++) {
50 | result |= a[i] ^ b[i];
51 | }
52 | return result == 0;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justinribeiro/android-hostcardemulation-sample/a078d90ddd59b4ba4ad7cb52ce6cc30f21780405/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justinribeiro/android-hostcardemulation-sample/a078d90ddd59b4ba4ad7cb52ce6cc30f21780405/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justinribeiro/android-hostcardemulation-sample/a078d90ddd59b4ba4ad7cb52ce6cc30f21780405/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justinribeiro/android-hostcardemulation-sample/a078d90ddd59b4ba4ad7cb52ce6cc30f21780405/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/layout-v21/activity_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
28 |
29 |
37 |
38 |
47 |
48 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
17 |
26 |
27 |
37 |
38 |
46 |
47 |
54 |
55 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/start.xml:
--------------------------------------------------------------------------------
1 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
16 |
17 |
--------------------------------------------------------------------------------
/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 |
3 |
4 | HCE Sample
5 | Emulate NFC Forum Type-4 tag
6 | This example emulates a type-4 tag with an NDEF record (RTD_TEXT). It uses an aid-filter F0394148148100 for the APDU SELECT command.
7 | Set NDEF Text
8 | Settings
9 | The Magical HCE Demo Service
10 | Chrome NFC demo
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/apduservice.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.1'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
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
19 | android.enableJetifier=true
20 | android.useAndroidX=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/justinribeiro/android-hostcardemulation-sample/a078d90ddd59b4ba4ad7cb52ce6cc30f21780405/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 02 18:41:57 PDT 2019
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-5.4.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 |
--------------------------------------------------------------------------------
/hostcardemulation.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------