1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.nfc.reader;
17 
18 import android.nfc.NfcAdapter;
19 import android.nfc.NfcAdapter.ReaderCallback;
20 import android.nfc.Tag;
21 import android.nfc.tech.IsoDep;
22 import android.nfc.tech.NfcA;
23 import android.util.Log;
24 
25 import com.android.nfc.utils.HceUtils;
26 
27 import java.io.IOException;
28 
29 public class ProtocolParamsReaderActivity extends BaseReaderActivity implements ReaderCallback {
30     public static final String TAG = "ProtocolParamsReaderActivity";
31 
32     @Override
onResume()33     protected void onResume() {
34         super.onResume();
35         mAdapter.enableReaderMode(
36                 this,
37                 this,
38                 NfcAdapter.FLAG_READER_NFC_A
39                         | NfcAdapter.FLAG_READER_NFC_BARCODE
40                         | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
41                 null);
42     }
43 
44     /** Parses protocol params from tag */
parseProtocolParameters( StringBuilder sb, byte[] uid, short sak, byte[] atqa, byte[] ats)45     public boolean parseProtocolParameters(
46             StringBuilder sb, byte[] uid, short sak, byte[] atqa, byte[] ats) {
47 
48         boolean success = true;
49 
50         sb.append("UID: " + HceUtils.getHexBytes(null, uid) + "\n\n");
51         sb.append("SAK: 0x" + Integer.toHexString(sak & 0xFF) + "\n");
52 
53         if ((sak & 0x20) != 0) {
54             sb.append("    (OK) ISO-DEP bit (0x20) is set.\n");
55         } else {
56             success = false;
57             sb.append("    (FAIL) ISO-DEP bit (0x20) is NOT set.\n");
58         }
59 
60         if ((sak & 0x40) != 0) {
61             sb.append("    (OK) P2P bit (0x40) is set.\n");
62         } else {
63             sb.append("    (WARN) P2P bit (0x40) is NOT set.\n");
64         }
65 
66         sb.append("\n");
67         sb.append("ATQA: " + HceUtils.getHexBytes(null, atqa) + "\n");
68         sb.append("\n");
69 
70         sb.append("ATS: " + HceUtils.getHexBytes(null, ats) + "\n");
71         sb.append("    TL: 0x" + Integer.toHexString(ats[0] & 0xFF) + "\n");
72         sb.append("    T0: 0x" + Integer.toHexString(ats[1] & 0xFF) + "\n");
73 
74         boolean ta_present = false;
75         boolean tb_present = false;
76         boolean tc_present = false;
77         int atsIndex = 1;
78         if ((ats[atsIndex] & 0x40) != 0) {
79             sb.append("        (OK) T(C) is present (bit 7 is set).\n");
80             tc_present = true;
81         } else {
82             success = false;
83             sb.append("        (FAIL) T(C) is not present (bit 7 is NOT set).\n");
84         }
85         if ((ats[atsIndex] & 0x20) != 0) {
86             sb.append("        (OK) T(B) is present (bit 6 is set).\n");
87             tb_present = true;
88         } else {
89             success = false;
90             sb.append("        (FAIL) T(B) is not present (bit 6 is NOT set).\n");
91         }
92         if ((ats[atsIndex] & 0x10) != 0) {
93             sb.append("        (OK) T(A) is present (bit 5 is set).\n");
94             ta_present = true;
95         } else {
96             success = false;
97             sb.append("        (FAIL) T(A) is not present (bit 5 is NOT set).\n");
98         }
99         int fsc = ats[atsIndex] & 0x0F;
100         if (fsc > 8) {
101             success = false;
102             sb.append("        (FAIL) FSC " + fsc + " is > 8\n");
103         } else if (fsc < 2) {
104             sb.append("        (FAIL EMVCO) FSC " + fsc + " is < 2\n");
105         } else {
106             sb.append("        (OK) FSC = " + fsc + "\n");
107         }
108 
109         atsIndex++;
110         if (ta_present) {
111             sb.append("    TA: 0x" + Integer.toHexString(ats[atsIndex] & 0xff) + "\n");
112             if ((ats[atsIndex] & 0x80) != 0) {
113                 sb.append("        (OK) bit 8 set, indicating only same bit rate divisor.\n");
114             } else {
115                 sb.append(
116                         "        (FAIL EMVCO) bit 8 NOT set, indicating support for asymmetric "
117                                 + "bit rate divisors. EMVCo requires bit 8 set.\n");
118             }
119             if ((ats[atsIndex] & 0x70) != 0) {
120                 sb.append("        (FAIL EMVCO) EMVCo requires bits 7 to 5 set to 0.\n");
121             } else {
122                 sb.append("        (OK) bits 7 to 5 indicating only 106 kbit/s L->P supported.\n");
123             }
124             if ((ats[atsIndex] & 0x7) != 0) {
125                 sb.append("        (FAIL EMVCO) EMVCo requires bits 3 to 1 set to 0.\n");
126             } else {
127                 sb.append("        (OK) bits 3 to 1 indicating only 106 kbit/s P->L supported.\n");
128             }
129             atsIndex++;
130         }
131 
132         if (tb_present) {
133             sb.append("    TB: 0x" + Integer.toHexString(ats[3] & 0xFF) + "\n");
134             int fwi = (ats[atsIndex] & 0xF0) >> 4;
135             if (fwi > 8) {
136                 success = false;
137                 sb.append("        (FAIL) FWI=" + fwi + ", should be <= 8\n");
138             } else if (fwi == 8) {
139                 sb.append("        (FAIL EMVCO) FWI=" + fwi + ", EMVCo requires <= 7\n");
140             } else {
141                 sb.append("        (OK) FWI=" + fwi + "\n");
142             }
143             int sfgi = ats[atsIndex] & 0x0F;
144             if (sfgi > 8) {
145                 success = false;
146                 sb.append("        (FAIL) SFGI=" + sfgi + ", should be <= 8\n");
147             } else {
148                 sb.append("        (OK) SFGI=" + sfgi + "\n");
149             }
150             atsIndex++;
151         }
152 
153         if (tc_present) {
154             sb.append("    TC: 0x" + Integer.toHexString(ats[atsIndex] & 0xFF) + "\n");
155             boolean nadSupported = (ats[atsIndex] & 0x01) != 0;
156             if (nadSupported) {
157                 success = false;
158                 sb.append("        (FAIL) NAD bit is not allowed to be set.\n");
159             } else {
160                 sb.append("        (OK) NAD bit is not set.\n");
161             }
162             atsIndex++;
163             // See if there's any bytes left for general bytes
164             if (atsIndex + 1 < ats.length) {
165                 int bytesToCopy = ats.length - atsIndex;
166                 byte[] historical_bytes = new byte[bytesToCopy];
167                 System.arraycopy(ats, atsIndex, historical_bytes, 0, bytesToCopy);
168                 sb.append(
169                         "\n(OK) Historical bytes: " + HceUtils.getHexBytes(null, historical_bytes));
170             }
171         }
172         return success;
173     }
174 
175     @Override
onTagDiscovered(Tag tag)176     public void onTagDiscovered(Tag tag) {
177         final StringBuilder sb = new StringBuilder();
178         IsoDep isoDep = IsoDep.get(tag);
179         NfcA nfcA = NfcA.get(tag);
180         boolean success = false;
181         if (nfcA == null || isoDep == null) {
182             return;
183         }
184         try {
185             nfcA.connect();
186             byte[] ats = nfcA.transceive(new byte[] {(byte) 0xE0, (byte) 0xF0});
187             success = parseProtocolParameters(sb, tag.getId(), nfcA.getSak(), nfcA.getAtqa(), ats);
188         } catch (IOException e) {
189             sb.insert(0, "Test failed. IOException (did you keep the devices in range?)\n\n.");
190         } finally {
191             if (success) {
192                 Log.d(TAG, "Success:\n" + sb);
193                 setTestPassed();
194             } else {
195                 Log.e(TAG, "Test Failed:\n" + sb);
196             }
197             try {
198                 nfcA.transceive(new byte[] {(byte) 0xC2});
199                 nfcA.close();
200                 isoDep.connect();
201             } catch (IOException e) {
202                 Log.e(TAG, "IO Exception", e);
203             }
204         }
205     }
206 }
207