1 /* 2 * Copyright (C) 2021 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 17 package com.android.nfc; 18 19 import android.sysprop.NfcProperties; 20 import android.util.Log; 21 22 import androidx.annotation.VisibleForTesting; 23 24 import java.io.PrintWriter; 25 import java.util.Arrays; 26 import java.util.Locale; 27 import java.util.Vector; 28 29 /** 30 * Parse the Routing Table from the last backup lmrt cmd and dump it with a clear typography 31 */ 32 public class RoutingTableParser { 33 static final boolean DBG = NfcProperties.debug_enabled().orElse(false); 34 private static final String TAG = "RoutingTableParser"; 35 private static int sRoutingTableSize = 0; 36 private static int sRoutingTableMaxSize = 0; 37 private static Vector<RoutingEntryInfo> sRoutingTable = new Vector<RoutingEntryInfo>(0); 38 39 // Entry types 40 static final byte TYPE_TECHNOLOGY = 0; 41 static final byte TYPE_PROTOCOL = 1; 42 static final byte TYPE_AID = 2; 43 static final byte TYPE_SYSTEMCODE = 3; 44 static final byte TYPE_UNSUPPORTED = 4; 45 46 // Commit status 47 static final int STATS_HOST_OK = 0; 48 static final int STATS_OFFHOST_OK = 1; 49 static final int STATS_NOT_FOUND = 2; 50 51 private interface GetEntryStr { getEntryStr(byte[] entry)52 String getEntryStr(byte[] entry); 53 } 54 55 private GetEntryStr[] mGetEntryStrFuncs = new GetEntryStr[] { 56 new GetEntryStr() { public String getEntryStr(byte[] entry) { 57 return getTechStr(entry); } }, 58 new GetEntryStr() { public String getEntryStr(byte[] entry) { 59 return getProtoStr(entry); } }, 60 new GetEntryStr() { public String getEntryStr(byte[] entry) { 61 return getAidStr(entry); } }, 62 new GetEntryStr() { public String getEntryStr(byte[] entry) { 63 return getSystemCodeStr(entry); } }, 64 }; 65 getTechStr(byte[] tech)66 private String getTechStr(byte[] tech) { 67 String[] tech_mask_list = { 68 "TECHNOLOGY_A", "TECHNOLOGY_B", "TECHNOLOGY_F", "TECHNOLOGY_V" 69 }; 70 71 if (tech[0] > tech_mask_list.length) { 72 return "UNSUPPORTED_TECH"; 73 } 74 return tech_mask_list[tech[0]]; 75 } 76 getProtoStr(byte[] proto)77 private String getProtoStr(byte[] proto) { 78 String[] proto_mask_list = { 79 "PROTOCOL_UNDETERMINED", "PROTOCOL_T1T", "PROTOCOL_T2T", "PROTOCOL_T3T", 80 "PROTOCOL_ISO_DEP", "PROTOCOL_NFC_DEP", "PROTOCOL_T5T", "PROTOCOL_NDEF" 81 }; 82 if (proto[0] > proto_mask_list.length) { 83 return "UNSUPPORTED_PROTO"; 84 } 85 return proto_mask_list[proto[0]]; 86 } 87 getAidStr(byte[] aid)88 private String getAidStr(byte[] aid) { 89 String aidStr = ""; 90 91 for (byte b : aid) { 92 aidStr += String.format("%02X", b); 93 } 94 95 if (aidStr.length() == 0) { 96 return "Empty_AID"; 97 } 98 return "AID_" + aidStr; 99 } 100 getSystemCodeStr(byte[] sc)101 private String getSystemCodeStr(byte[] sc) { 102 String systemCodeStr = ""; 103 for (byte b : sc) { 104 systemCodeStr += String.format("%02X", b); 105 } 106 return "SYSTEMCODE_" + systemCodeStr; 107 } 108 getBlockCtrlStr(byte mask)109 private String getBlockCtrlStr(byte mask) { 110 if ((mask & 0x40) != 0) { 111 return "True"; 112 } 113 return "False"; 114 } 115 getPrefixSubsetStr(byte mask, byte type)116 private String getPrefixSubsetStr(byte mask, byte type) { 117 if (type != TYPE_AID) { 118 return ""; 119 } 120 121 String prefix_subset_str = ""; 122 if ((mask & 0x10) != 0) { 123 prefix_subset_str += "Prefix "; 124 } 125 if ((mask & 0x20) != 0) { 126 prefix_subset_str += "Subset"; 127 } 128 if (prefix_subset_str.equals("")){ 129 return "Exact"; 130 } 131 return prefix_subset_str; 132 } 133 formatRow(String entry, String eeId, String pwrState, String blkCtrl, String extra)134 private String formatRow(String entry, String eeId, 135 String pwrState, String blkCtrl, String extra) { 136 String fmt = "\t%-36s\t%8s\t%-11s\t%-10s\t%-10s"; 137 return String.format(fmt, entry, eeId, pwrState, blkCtrl, extra); 138 } 139 140 private class RoutingEntryInfo { 141 public final byte mQualifier; 142 public final byte mType; 143 public final byte mNfceeId; 144 public final byte mPowerState; 145 public final byte[] mEntry; 146 RoutingEntryInfo(byte qualifier, byte type, byte eeId, byte pwrState, byte[] entry)147 private RoutingEntryInfo(byte qualifier, byte type, byte eeId, byte pwrState, 148 byte[] entry) { 149 mQualifier = qualifier; 150 mType = type; 151 mNfceeId = eeId; 152 mPowerState = pwrState; 153 mEntry = entry; 154 } 155 dump(PrintWriter pw)156 private void dump(PrintWriter pw) { 157 String blkCtrl = getBlockCtrlStr(mQualifier); 158 String eeId = String.format("0x%02X", mNfceeId); 159 String pwrState = String.format("0x%02X", mPowerState); 160 String entry = mGetEntryStrFuncs[mType].getEntryStr(mEntry); 161 String extra = getPrefixSubsetStr(mQualifier, mType); 162 163 pw.println(formatRow(entry, eeId, pwrState, blkCtrl, extra)); 164 } 165 } 166 validateEntryInfo(byte type, byte[] entry)167 private boolean validateEntryInfo(byte type, byte[] entry) { 168 switch(type) { 169 case TYPE_TECHNOLOGY: 170 if (entry.length != 1) return false; 171 break; 172 case TYPE_PROTOCOL: 173 if (entry.length != 1) return false; 174 break; 175 case TYPE_AID: 176 if (entry.length > 16) return false; 177 break; 178 case TYPE_SYSTEMCODE: 179 if (entry.length != 2) return false; 180 break; 181 default: 182 return false; 183 } 184 return true; 185 } 186 187 /** 188 * Check commit status by inputting type and entry 189 */ 190 @VisibleForTesting getCommitStatus(byte type, byte[] entry)191 public int getCommitStatus(byte type, byte[] entry) { 192 if (!validateEntryInfo(type, entry)) return STATS_NOT_FOUND; 193 194 for (RoutingEntryInfo routingEntry : sRoutingTable) { 195 if (routingEntry.mType != type) { 196 continue; 197 } 198 if (Arrays.equals(routingEntry.mEntry, entry)) { 199 return routingEntry.mNfceeId == 0x00 ? STATS_HOST_OK : STATS_OFFHOST_OK; 200 } 201 if (routingEntry.mType != TYPE_AID) { 202 continue; 203 } 204 if ((routingEntry.mQualifier & 0x10) != 0 205 && entry.length > routingEntry.mEntry.length) { 206 int ptr = 0; 207 while (entry[ptr] == routingEntry.mEntry[ptr]) { 208 ptr += 1; 209 } 210 if (ptr == routingEntry.mEntry.length) { 211 return routingEntry.mNfceeId == 0x00 ? STATS_HOST_OK : STATS_OFFHOST_OK; 212 } 213 } 214 if ((routingEntry.mQualifier & 0x20) != 0 215 && entry.length < routingEntry.mEntry.length) { 216 int ptr = 0; 217 while (entry[ptr] == routingEntry.mEntry[ptr]) { 218 ptr += 1; 219 } 220 if (ptr == entry.length) { 221 return routingEntry.mNfceeId == 0x00 ? STATS_HOST_OK : STATS_OFFHOST_OK; 222 } 223 } 224 } 225 return STATS_NOT_FOUND; 226 } 227 addRoutingEntry(byte[] rt, int offset)228 private void addRoutingEntry(byte[] rt, int offset) { 229 if (offset + 1 >= rt.length) return; 230 int valueLength = Byte.toUnsignedInt(rt[offset + 1]); 231 232 // Qualifier-Type(1 byte) + Length(1 byte) + Value(valueLength bytes) 233 if (offset + 2 + valueLength > rt.length) return; 234 235 byte qualifier = (byte) (rt[offset] & 0xF0); 236 byte type = (byte) (rt[offset] & 0x0F); 237 byte eeId = rt[offset + 2]; 238 byte pwrState = rt[offset + 3]; 239 byte[] entry = new byte[valueLength - 2]; 240 for (int i = 0; i < valueLength - 2; i++) { 241 entry[i] = rt[offset + 4 + i]; 242 } 243 244 if (type == TYPE_SYSTEMCODE && (entry.length & 1) == 0 && entry.length <= 64) { 245 for (int i = 0; i < entry.length; i += 2) { 246 byte[] sc_entry = {entry[i], entry[i + 1]}; 247 sRoutingTable.add(new RoutingEntryInfo(qualifier, type, eeId, pwrState, sc_entry)); 248 } 249 } else if (validateEntryInfo(type, entry)) { 250 sRoutingTable.add(new RoutingEntryInfo(qualifier, type, eeId, pwrState, entry)); 251 } 252 } 253 254 /** 255 * Parse the raw data of routing table 256 */ parse(byte[] rt)257 public void parse(byte[] rt) { 258 int offset = 0; 259 260 logRoutingTableRawData(rt); 261 262 sRoutingTable.clear(); 263 while (offset < rt.length) { 264 byte type = (byte) (rt[offset] & 0x0F); 265 if (type >= TYPE_UNSUPPORTED) { 266 // Unrecognizable entry type 267 Log.e(TAG, String.format("Unrecognizable entry type: 0x%02X, stop parsing", type)); 268 return; 269 } 270 if (offset + 1 >= rt.length) { 271 // Buffer overflow 272 Log.e(TAG, String.format("Wrong tlv length, stop parsing")); 273 return; 274 } 275 // Qualifier-Type(1 byte) + Length(1 byte) + Value(valueLength bytes) 276 int tlvLength = Byte.toUnsignedInt(rt[offset + 1]) + 2; 277 278 addRoutingEntry(rt, offset); 279 280 offset += tlvLength; 281 } 282 } 283 284 /** 285 * Get Routing Table from the last backup lmrt cmd and parse it 286 */ update(DeviceHost dh)287 public void update(DeviceHost dh) { 288 sRoutingTableMaxSize = dh.getMaxRoutingTableSize(); 289 byte[] rt = dh.getRoutingTable(); 290 sRoutingTableSize = rt.length; 291 parse(rt); 292 } 293 294 /** 295 * Get Routing Table from the last backup lmrt cmd and dump it 296 */ dump(DeviceHost dh, PrintWriter pw)297 public void dump(DeviceHost dh, PrintWriter pw) { 298 update(dh); 299 300 pw.println("--- dumpRoutingTable: start ---"); 301 pw.println(String.format(Locale.US, "RoutingTableSize: %d/%d", 302 sRoutingTableSize, sRoutingTableMaxSize)); 303 pw.println(formatRow("Entry", "NFCEE_ID", "Power State", "Block Ctrl", "Extra Info")); 304 305 for (RoutingEntryInfo routingEntry : sRoutingTable) { 306 routingEntry.dump(pw); 307 } 308 309 pw.println("--- dumpRoutingTable: end ---"); 310 } 311 logRoutingTableRawData(byte[] lmrt_cmd)312 private void logRoutingTableRawData(byte[] lmrt_cmd) { 313 if (!DBG) return; 314 String lmrt_str = ""; 315 316 for (byte b : lmrt_cmd) { 317 lmrt_str += String.format("%02X ", b); 318 } 319 Log.i(TAG, String.format("RoutingTableSize: %d", lmrt_cmd.length)); 320 Log.i(TAG, String.format("RoutingTable: %s", lmrt_str)); 321 } 322 } 323