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