• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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