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