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