1 package com.android.cts.verifier.nfc.hce;
2 
3 import android.annotation.TargetApi;
4 import android.nfc.NfcAdapter;
5 import android.nfc.NfcAdapter.ReaderCallback;
6 import android.nfc.tech.IsoDep;
7 import android.nfc.tech.NfcA;
8 import android.nfc.Tag;
9 import android.os.Bundle;
10 import android.widget.TextView;
11 
12 import com.android.cts.verifier.PassFailButtons;
13 import com.android.cts.verifier.R;
14 
15 import java.io.IOException;
16 
17 @TargetApi(19)
18 public class ProtocolParamsReaderActivity extends PassFailButtons.Activity implements ReaderCallback {
19     public static final String TAG = "ProtocolParamsReaderActivity";
20 
21     NfcAdapter mAdapter;
22 
23     TextView mTextView;
24 
25     @Override
onCreate(Bundle savedInstanceState)26     protected void onCreate(Bundle savedInstanceState) {
27         super.onCreate(savedInstanceState);
28         setContentView(R.layout.pass_fail_text);
29         setPassFailButtonClickListeners();
30 
31         setTitle(R.string.nfc_hce_protocol_params_reader);
32 
33         mAdapter = NfcAdapter.getDefaultAdapter(this);
34         mTextView = (TextView) findViewById(R.id.text);
35         mTextView.setTextSize(12.0f);
36     }
37 
38     @Override
onResume()39     protected void onResume() {
40         super.onResume();
41         mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A |
42                 NfcAdapter.FLAG_READER_NFC_BARCODE | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
43     }
44 
parseProtocolParameters(StringBuilder sb, byte[] uid, short sak, byte[] atqa, byte[] ats)45     public boolean parseProtocolParameters(StringBuilder sb, byte[] uid,
46             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 " + Integer.toString(fsc) + " is > 8\n");
103         } else if (fsc < 2) {
104             sb.append("        (FAIL EMVCO) FSC " + Integer.toString(fsc) + " is < 2\n");
105         } else {
106             sb.append("        (OK) FSC = " + Integer.toString(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("        (FAIL EMVCO) bit 8 NOT set, indicating support for assymetric " +
116                         "bit rate divisors. EMVCo requires bit 8 set.\n");
117             }
118             if ((ats[atsIndex] & 0x70) != 0) {
119                 sb.append("        (FAIL EMVCO) EMVCo requires bits 7 to 5 set to 0.\n");
120             } else {
121                 sb.append("        (OK) bits 7 to 5 indicating only 106 kbit/s L->P supported.\n");
122             }
123             if ((ats[atsIndex] & 0x7) != 0) {
124                 sb.append("        (FAIL EMVCO) EMVCo requires bits 3 to 1 set to 0.\n");
125             } else {
126                 sb.append("        (OK) bits 3 to 1 indicating only 106 kbit/s P->L supported.\n");
127             }
128             atsIndex++;
129         }
130 
131         if (tb_present) {
132             sb.append("    TB: 0x" + Integer.toHexString(ats[3] & 0xFF) + "\n");
133             int fwi = (ats[atsIndex] & 0xF0) >> 4;
134             if (fwi > 8) {
135                 success = false;
136                 sb.append("        (FAIL) FWI=" + Integer.toString(fwi) + ", should be <= 8\n");
137             } else if (fwi == 8) {
138                 sb.append("        (FAIL EMVCO) FWI=" + Integer.toString(fwi) +
139                         ", EMVCo requires <= 7\n");
140             } else {
141                 sb.append("        (OK) FWI=" + Integer.toString(fwi) + "\n");
142             }
143             int sfgi = ats[atsIndex] & 0x0F;
144             if (sfgi > 8) {
145                 success = false;
146                 sb.append("        (FAIL) SFGI=" + Integer.toString(sfgi) + ", should be <= 8\n");
147             } else {
148                 sb.append("        (OK) SFGI=" + Integer.toString(sfgi) + "\n");
149             }
150             atsIndex++;
151         }
152 
153         if (tc_present) {
154             sb.append("    TC: 0x" + Integer.toHexString(ats[atsIndex] & 0xFF) + "\n");
155             boolean apSupported = (ats[atsIndex] & 0x10) != 0;
156             boolean didSupported = (ats[atsIndex] & 0x02) != 0;
157             boolean nadSupported = (ats[atsIndex] & 0x01) != 0;
158             if (nadSupported) {
159                 success = false;
160                 sb.append("        (FAIL) NAD bit is not allowed to be set.\n");
161             } else {
162                 sb.append("        (OK) NAD bit is not set.\n");
163             }
164             atsIndex++;
165             // See if there's any bytes left for general bytes
166             if (atsIndex + 1 < ats.length) {
167                 int bytesToCopy = ats.length - atsIndex;
168                 byte[] historical_bytes = new byte[bytesToCopy];
169                 System.arraycopy(ats, atsIndex, historical_bytes, 0, bytesToCopy);
170                 sb.append("\n(OK) Historical bytes: " +
171                         HceUtils.getHexBytes(null, historical_bytes));
172             }
173         }
174         return success;
175     }
176 
177     @Override
onTagDiscovered(Tag tag)178     public void onTagDiscovered(Tag tag) {
179         final StringBuilder sb = new StringBuilder();
180         IsoDep isoDep = IsoDep.get(tag);
181         NfcA nfcA = NfcA.get(tag);
182         boolean success = false;
183         if (nfcA == null || isoDep == null) {
184             return;
185         }
186         try {
187             nfcA.connect();
188             byte[] ats = nfcA.transceive(new byte[] { (byte) 0xE0, (byte)0xF0});
189             success = parseProtocolParameters(sb, tag.getId(), nfcA.getSak(), nfcA.getAtqa(), ats);
190         } catch (IOException e) {
191             sb.insert(0, "Test failed. IOException (did you keep the devices in range?)\n\n.");
192         } finally {
193             if (success) {
194                 runOnUiThread(new Runnable() {
195                     @Override
196                     public void run() {
197                         mTextView.setText(sb.toString());
198                         getPassButton().setEnabled(true);
199                     }
200                 });
201             } else {
202                 runOnUiThread(new Runnable() {
203                     @Override
204                     public void run() {
205                         mTextView.setText(sb.toString());
206                         getPassButton().setEnabled(false);
207                     }
208                 });
209             }
210             try {
211                 nfcA.transceive(new byte[] {(byte) 0xC2});
212                 nfcA.close();
213                 isoDep.connect();
214             } catch (IOException e) {
215             }
216         }
217     }
218 }
219