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 
17 package com.android.nfc.cardemulation;
18 
19 import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_EXACT_ONLY;
20 import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_EXACT_OR_PREFIX;
21 import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX;
22 import static com.android.nfc.cardemulation.AidRoutingManager.AID_MATCHING_PREFIX_ONLY;
23 import static com.android.nfc.cardemulation.AidRoutingManager.ROUTE_HOST;
24 import static com.google.common.truth.Truth.assertThat;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.anyString;
27 import static org.mockito.Mockito.never;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import com.android.dx.mockito.inline.extended.ExtendedMockito;
35 import com.android.nfc.NfcService;
36 import com.android.nfc.NfcStatsLog;
37 import com.android.nfc.cardemulation.AidRoutingManager.AidEntry;
38 
39 import java.io.PrintWriter;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 
43 import org.junit.After;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Mock;
48 import org.mockito.Mockito;
49 import org.mockito.MockitoAnnotations;
50 import org.mockito.ArgumentCaptor;
51 import org.mockito.Captor;
52 import org.mockito.MockitoSession;
53 import org.mockito.quality.Strictness;
54 
55 @RunWith(AndroidJUnit4.class)
56 public class AidRoutingManagerTest {
57 
58   private AidRoutingManager manager;
59   private MockitoSession mStaticMockSession;
60 
61   @Mock
62   private RoutingOptionManager mRoutingOptionManager;
63   @Mock
64   private NfcService mNfcService;
65   @Mock
66   private PrintWriter mPw;
67 
68   @Captor
69   private ArgumentCaptor<String> unroutedAidsCaptor;
70   @Captor
71   private ArgumentCaptor<String> routedAidsCaptor;
72   @Captor
73   private ArgumentCaptor<Integer> routeCaptor;
74   @Captor
75   private ArgumentCaptor<Integer> aidTypeCaptor;
76   @Captor
77   private ArgumentCaptor<Integer> powerCaptor;
78   @Captor
79   private ArgumentCaptor<Integer> codeCaptor;
80   @Captor
81   private ArgumentCaptor<Integer> arg1Captor;
82   @Captor
83   private ArgumentCaptor<Integer> arg2Captor;
84   @Captor
85   private ArgumentCaptor<Integer> arg3Captor;
86 
87   private static final int DEFAULT_ROUTE = 0;
88   private static final int OVERRIDE_DEFAULT_ROUTE = 10;
89   private static final int DEFAULT_OFFHOST_ROUTE = 20;
90   private static final int OVERRIDE_ISODEP_ROUTE = 30;
91   private static final byte[] OFFHOST_ROUTE_UICC = new byte[] {5, 6, 7, 8};
92   private static final byte[] OFFHOST_ROUTE_ESE = new byte[] {1, 2, 3, 4};
93   private static final int FIRST_AID_ENTRY_POWER = 1;
94   private static final int FIRST_AID_ENTRY_AID_INFO = 2;
95   private static final int SECOND_AID_ENTRY_POWER = 3;
96   private static final int SECOND_AID_ENTRY_AID_INFO = 4;
97   private static final int THIRD_AID_ENTRY_POWER = 5;
98   private static final int THIRD_AID_ENTRY_AID_INFO = 6;
99   private static final int FOURTH_AID_ENTRY_POWER = 7;
100   private static final int FOURTH_AID_ENTRY_AID_INFO = 8;
101 
102   @Before
setUp()103   public void setUp() {
104     mStaticMockSession = ExtendedMockito.mockitoSession()
105         .mockStatic(RoutingOptionManager.class)
106         .mockStatic(NfcService.class)
107         .mockStatic(NfcStatsLog.class)
108         .strictness(Strictness.LENIENT)
109         .startMocking();
110     MockitoAnnotations.initMocks(this);
111     when(RoutingOptionManager.getInstance()).thenReturn(mRoutingOptionManager);
112     when(NfcService.getInstance()).thenReturn(mNfcService);
113   }
114 
115   @After
tearDown()116   public void tearDown() {
117     mStaticMockSession.finishMocking();
118   }
119 
120   @Test
testConstructor()121   public void testConstructor() {
122     manager = new AidRoutingManager();
123   }
124 
125   @Test
testSupportsAidPrefixRouting_ReturnsTrue()126   public void testSupportsAidPrefixRouting_ReturnsTrue() {
127     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_OR_PREFIX);
128     manager = new AidRoutingManager();
129 
130     boolean result = manager.supportsAidPrefixRouting();
131 
132     assertThat(result).isTrue();
133   }
134 
135   @Test
testSupportsAidPrefixRouting_ReturnsFalse()136   public void testSupportsAidPrefixRouting_ReturnsFalse() {
137     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_ONLY);
138     manager = new AidRoutingManager();
139 
140     boolean result = manager.supportsAidPrefixRouting();
141 
142     assertThat(result).isFalse();
143   }
144 
145   @Test
testSupportsAidSubsetRouting_ReturnsTrue()146   public void testSupportsAidSubsetRouting_ReturnsTrue() {
147     when(mRoutingOptionManager.getAidMatchingSupport())
148         .thenReturn(AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX);
149     manager = new AidRoutingManager();
150 
151     boolean result = manager.supportsAidSubsetRouting();
152 
153     assertThat(result).isTrue();
154   }
155 
156   @Test
testSupportsAidSubsetRouting_ReturnsFalse()157   public void testSupportsAidSubsetRouting_ReturnsFalse() {
158     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_OR_PREFIX);
159     manager = new AidRoutingManager();
160 
161     boolean result = manager.supportsAidSubsetRouting();
162 
163     assertThat(result).isFalse();
164   }
165 
166   @Test
testCalculateAidRouteSizeWithEmptyCache()167   public void testCalculateAidRouteSizeWithEmptyCache() {
168     manager = new AidRoutingManager();
169 
170     int result = manager.calculateAidRouteSize(new HashMap<String, AidEntry>());
171 
172     assertThat(result).isEqualTo(0);
173   }
174 
175   @Test
testCalculateAidRouteSizeWithNonEmptyCache()176   public void testCalculateAidRouteSizeWithNonEmptyCache() {
177     String firstAidEntry = "0000000000";
178     String secondAidEntry = "000000000000000";
179     HashMap<String, AidEntry> cache = new HashMap<>();
180     cache.put(firstAidEntry + "*", null);
181     cache.put(secondAidEntry, null);
182     manager = new AidRoutingManager();
183 
184     int result = manager.calculateAidRouteSize(cache);
185 
186     int expected = (firstAidEntry.length() / 2) + 4 + (secondAidEntry.length() / 2) + 4;
187     assertThat(result).isEqualTo(expected);
188   }
189 
190   @Test
testConfigureRoutingWithEmptyMap_ReturnsFalse()191   public void testConfigureRoutingWithEmptyMap_ReturnsFalse() {
192     manager = new AidRoutingManager();
193 
194     boolean result = manager.configureRouting(/* aidMap = */ new HashMap<String, AidEntry>(),
195         /* force = */ false);
196 
197     assertThat(result).isFalse();
198   }
199 
200   /**
201    * Tests the case wherein:
202    *  (1) The default route (mDefaultRoute) is overridden to the value OVERRIDE_DEFAULT_ROUTE.
203    *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
204    *  (3) mAidMatchingSupport is equal to AID_MATCHING_PREFIX_ONLY
205    *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
206    *  (5) NCI Version 2 is used.
207    *
208    *  Ultimately, the contents of aidMap should be committed.
209    */
210   @Test
testConfigureRoutingTestCase1_CommitsCache()211   public void testConfigureRoutingTestCase1_CommitsCache() {
212     when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(true);
213     when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
214     when(mRoutingOptionManager.getOverrideDefaultRoute()).thenReturn(OVERRIDE_DEFAULT_ROUTE);
215     when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
216     when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
217     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_PREFIX_ONLY);
218     when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
219     when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_2_0);
220     when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
221     manager = new AidRoutingManager();
222     manager.mRouteForAid.put("first*", 0);
223     manager.mRouteForAid.put("second#", 0);
224     manager.mRouteForAid.put("third", 0);
225 
226     boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
227 
228     assertThat(result).isTrue();
229     verify(mNfcService, times(4)).unrouteAids(unroutedAidsCaptor.capture());
230     assertThat(unroutedAidsCaptor.getAllValues().contains("first")).isTrue();
231     assertThat(unroutedAidsCaptor.getAllValues().contains("second#")).isTrue();
232     assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
233     assertThat(unroutedAidsCaptor.getAllValues().contains("")).isTrue();
234     verify(mNfcService, times(3)).routeAids(routedAidsCaptor.capture(),
235                                             routeCaptor.capture(),
236                                             aidTypeCaptor.capture(),
237                                             powerCaptor.capture());
238     assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("");
239     assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("firstAidEntry");
240     assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("fourthAidEntry");
241     assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
242     assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(OFFHOST_ROUTE_ESE[1]);
243     assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(OFFHOST_ROUTE_UICC[0]);
244     assertThat(aidTypeCaptor.getAllValues().get(0))
245         .isEqualTo(RegisteredAidCache.AID_ROUTE_QUAL_PREFIX);
246     assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
247     assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
248     assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(RegisteredAidCache.POWER_STATE_ALL);
249     assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_POWER);
250     assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_POWER);
251     verify(mNfcService).commitRouting();
252     assertThat(manager.mDefaultRoute).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
253     assertThat(manager.mRouteForAid.size()).isEqualTo(3);
254     assertThat(manager.mPowerForAid.size()).isEqualTo(3);
255     assertThat(manager.mAidRoutingTable.size()).isEqualTo(3);
256   }
257 
258   /**
259    * Tests the case wherein:
260    *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
261    *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
262    *  (3) mAidMatchingSupport is equal to AID_MATCHING_PREFIX_ONLY
263    *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
264    *  (5) NCI Version 1 is used.
265    *
266    *  Ultimately, nothing is committed and an error message is written to NfcStatsLog.
267    */
268   @Test
testConfigureRoutingTestCase2_WritesError()269   public void testConfigureRoutingTestCase2_WritesError() {
270     when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
271     when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
272     when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
273     when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
274     when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
275     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_PREFIX_ONLY);
276     when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
277     when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
278     when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
279     manager = new AidRoutingManager();
280 
281     boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
282 
283     assertThat(result).isTrue();
284     verify(mNfcService, never()).unrouteAids(anyString());
285     verify(mNfcService, never()).routeAids(anyString(), anyInt(), anyInt(), anyInt());
286     verify(mNfcService, never()).commitRouting();
287     ExtendedMockito.verify(() -> NfcStatsLog.write(codeCaptor.capture(),
288                                                    arg1Captor.capture(),
289                                                    arg2Captor.capture(),
290                                                    arg3Captor.capture()));
291     assertThat(codeCaptor.getValue()).isEqualTo(NfcStatsLog.NFC_ERROR_OCCURRED);
292     assertThat(arg1Captor.getValue()).isEqualTo(NfcStatsLog.NFC_ERROR_OCCURRED__TYPE__AID_OVERFLOW);
293     assertThat(arg2Captor.getValue()).isEqualTo(0);
294     assertThat(arg3Captor.getValue()).isEqualTo(0);
295     assertThat(manager.mDefaultRoute).isEqualTo(OFFHOST_ROUTE_ESE[1]);
296     assertThat(manager.mRouteForAid.size()).isEqualTo(3);
297     assertThat(manager.mPowerForAid.size()).isEqualTo(3);
298     assertThat(manager.mAidRoutingTable.size()).isEqualTo(3);
299   }
300 
301   /**
302    * Tests the case wherein:
303    *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
304    *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
305    *  (3) mAidMatchingSupport is equal to AID_MATCHING_ONLY
306    *  (4) mDefaultIsoDepRoute is equal to OVERRIDE_ISODEP_ROUTE (so that the default route is not
307    *  registered)
308    *  (5) NCI Version 2 is used.
309    *
310    *  Ultimately, the routing table is not updated and no other action is taken.
311    */
312   @Test
testConfigureRoutingTestCase3_DoNothing()313   public void testConfigureRoutingTestCase3_DoNothing() {
314     when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
315     when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
316     when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
317     when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
318     when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
319     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_ONLY);
320     when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(OVERRIDE_ISODEP_ROUTE);
321     when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
322     when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
323     manager = new AidRoutingManager();
324     manager.mRouteForAid.put("first*", 0);
325     manager.mRouteForAid.put("second#", 0);
326     manager.mRouteForAid.put("third", 0);
327     // Create a HashMap with only one AidEntry
328     HashMap<String, AidEntry> aidMap = new HashMap<>();
329     AidEntry aidEntry = manager.new AidEntry();
330     aidEntry.isOnHost = false;
331     aidEntry.offHostSE = "eSE2";
332     aidEntry.power = 1;
333     aidEntry.aidInfo = 2;
334     aidMap.put("firstAidEntry*", aidEntry);
335 
336     boolean result = manager.configureRouting(aidMap, /* force = */ false);
337 
338     assertThat(result).isTrue();
339 
340     verify(mNfcService, times(3)).unrouteAids(unroutedAidsCaptor.capture());
341     assertThat(unroutedAidsCaptor.getAllValues().contains("first*")).isTrue();
342     assertThat(unroutedAidsCaptor.getAllValues().contains("second#")).isTrue();
343     assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
344     ExtendedMockito.verify(() ->
345         NfcStatsLog.write(anyInt(), anyInt(), anyInt(), anyInt()), times(0));
346     assertThat(manager.mDefaultRoute).isEqualTo(DEFAULT_ROUTE);
347     assertThat(manager.mRouteForAid.size()).isEqualTo(1);
348     assertThat(manager.mPowerForAid.size()).isEqualTo(1);
349     assertThat(manager.mAidRoutingTable.size()).isEqualTo(1);
350   }
351 
352   /**
353    * Tests the case wherein:
354    *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
355    *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are non-null.
356    *  (3) mAidMatchingSupport is equal to AID_MATCHING_ONLY
357    *  (4) mDefaultIsoDepRoute is equal to OVERRIDE_ISODEP_ROUTE (so that the default route is not
358    *  registered)
359    *  (5) NCI Version 2 is used.
360    *
361    *  This case is identical to Test Case 3, with the exception of the value of the force variable,
362    *  which causes the cache to be committed.
363    */
364   @Test
testConfigureRoutingTestCase4_CommitsCache()365   public void testConfigureRoutingTestCase4_CommitsCache() {
366     when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
367     when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
368     when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
369     when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(OFFHOST_ROUTE_UICC);
370     when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(OFFHOST_ROUTE_ESE);
371     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_ONLY);
372     when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(OVERRIDE_ISODEP_ROUTE);
373     when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
374     when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
375     manager = new AidRoutingManager();
376     manager.mRouteForAid.put("first*", 0);
377     manager.mRouteForAid.put("second#", 0);
378     manager.mRouteForAid.put("third", 0);
379     // Create a HashMap with only one AidEntry
380     HashMap<String, AidEntry> aidMap = new HashMap<>();
381     AidEntry aidEntry = manager.new AidEntry();
382     aidEntry.isOnHost = false;
383     aidEntry.offHostSE = "eSE2";
384     aidEntry.power = 1;
385     aidEntry.aidInfo = 2;
386     aidMap.put("firstAidEntry*", aidEntry);
387 
388     boolean result = manager.configureRouting(aidMap, /* force = */ true);
389 
390     assertThat(result).isTrue();
391     verify(mNfcService).commitRouting();
392   }
393 
394   /**
395    * Tests the case wherein:
396    *  (1) The default route (mDefaultRoute) is overridden to the value OVERRIDE_DEFAULT_ROUTE.
397    *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are null.
398    *  (3) mAidMatchingSupport is equal to AID_MATCHING_EXACT_OR_PREFIX
399    *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
400    *  (5) NCI Version 2 is used.
401    *
402    *  Ultimately, the contents of aidMap should be committed.
403    */
404   @Test
testConfigureRoutingTestCase5_CommitsCache()405   public void testConfigureRoutingTestCase5_CommitsCache() {
406     when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(true);
407     when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
408     when(mRoutingOptionManager.getOverrideDefaultRoute()).thenReturn(OVERRIDE_DEFAULT_ROUTE);
409     when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(null);
410     when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(null);
411     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_EXACT_OR_PREFIX);
412     when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
413     when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_2_0);
414     when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
415     manager = new AidRoutingManager();
416     manager.mRouteForAid.put("first*", 0);
417     manager.mRouteForAid.put("second#", 0);
418     manager.mRouteForAid.put("third", 0);
419 
420     boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
421 
422     assertThat(result).isTrue();
423     verify(mNfcService, times(4)).unrouteAids(unroutedAidsCaptor.capture());
424     assertThat(unroutedAidsCaptor.getAllValues().contains("first")).isTrue();
425     assertThat(unroutedAidsCaptor.getAllValues().contains("second#")).isTrue();
426     assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
427     assertThat(unroutedAidsCaptor.getAllValues().contains("")).isTrue();
428     verify(mNfcService, times(4)).routeAids(routedAidsCaptor.capture(),
429                                             routeCaptor.capture(),
430                                             aidTypeCaptor.capture(),
431                                             powerCaptor.capture());
432     assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("");
433     assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("fourthAidEntry");
434     assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("thirdAidEntry");
435     assertThat(routedAidsCaptor.getAllValues().get(3)).isEqualTo("firstAidEntry");
436     assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
437     assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
438     assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
439     assertThat(routeCaptor.getAllValues().get(3)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
440     assertThat(aidTypeCaptor.getAllValues().get(0))
441         .isEqualTo(RegisteredAidCache.AID_ROUTE_QUAL_PREFIX);
442     assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
443     assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_AID_INFO);
444     assertThat(aidTypeCaptor.getAllValues().get(3)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
445     assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(RegisteredAidCache.POWER_STATE_ALL);
446     assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(FOURTH_AID_ENTRY_POWER);
447     assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_POWER);
448     assertThat(powerCaptor.getAllValues().get(3)).isEqualTo(FIRST_AID_ENTRY_POWER);
449     verify(mNfcService).commitRouting();
450     assertThat(manager.mDefaultRoute).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
451     assertThat(manager.mRouteForAid.size()).isEqualTo(4);
452     assertThat(manager.mPowerForAid.size()).isEqualTo(4);
453     assertThat(manager.mAidRoutingTable.size()).isEqualTo(2);
454   }
455 
456   /**
457    * Tests the case wherein:
458    *  (1) The default route (mDefaultRoute) is overridden to the value OVERRIDE_DEFAULT_ROUTE.
459    *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are null.
460    *  (3) mAidMatchingSupport is equal to AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX
461    *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
462    *  (5) NCI Version 2 is used.
463    *
464    *  Ultimately, the contents of aidMap should be committed.
465    */
466   @Test
testConfigureRoutingTestCase6_CommitsCache()467   public void testConfigureRoutingTestCase6_CommitsCache() {
468     when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(true);
469     when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
470     when(mRoutingOptionManager.getOverrideDefaultRoute()).thenReturn(OVERRIDE_DEFAULT_ROUTE);
471     when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(null);
472     when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(null);
473     when(mRoutingOptionManager.getAidMatchingSupport())
474         .thenReturn(AID_MATCHING_EXACT_OR_SUBSET_OR_PREFIX);
475     when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
476     when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_2_0);
477     when(mNfcService.getAidRoutingTableSize()).thenReturn(0);
478     manager = new AidRoutingManager();
479     manager.mRouteForAid.put("first*", 0);
480     manager.mRouteForAid.put("second#", 0);
481     manager.mRouteForAid.put("third", 0);
482 
483     boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
484 
485     assertThat(result).isTrue();
486     verify(mNfcService, times(4)).unrouteAids(unroutedAidsCaptor.capture());
487     assertThat(unroutedAidsCaptor.getAllValues().contains("first")).isTrue();
488     assertThat(unroutedAidsCaptor.getAllValues().contains("second")).isTrue();
489     assertThat(unroutedAidsCaptor.getAllValues().contains("third")).isTrue();
490     assertThat(unroutedAidsCaptor.getAllValues().contains("")).isTrue();
491     verify(mNfcService, times(5)).routeAids(routedAidsCaptor.capture(),
492                                             routeCaptor.capture(),
493                                             aidTypeCaptor.capture(),
494                                             powerCaptor.capture());
495     assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("");
496     assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("secondAidEntry");
497     assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("fourthAidEntry");
498     assertThat(routedAidsCaptor.getAllValues().get(3)).isEqualTo("thirdAidEntry");
499     assertThat(routedAidsCaptor.getAllValues().get(4)).isEqualTo("firstAidEntry");
500     assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
501     assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(DEFAULT_ROUTE);
502     assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
503     assertThat(routeCaptor.getAllValues().get(3)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
504     assertThat(routeCaptor.getAllValues().get(4)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
505     assertThat(aidTypeCaptor.getAllValues().get(0))
506         .isEqualTo(RegisteredAidCache.AID_ROUTE_QUAL_PREFIX);
507     assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(SECOND_AID_ENTRY_AID_INFO);
508     assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
509     assertThat(aidTypeCaptor.getAllValues().get(3)).isEqualTo(THIRD_AID_ENTRY_AID_INFO);
510     assertThat(aidTypeCaptor.getAllValues().get(4)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
511     assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(RegisteredAidCache.POWER_STATE_ALL);
512     assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(SECOND_AID_ENTRY_POWER);
513     assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(FOURTH_AID_ENTRY_POWER);
514     assertThat(powerCaptor.getAllValues().get(3)).isEqualTo(THIRD_AID_ENTRY_POWER);
515     assertThat(powerCaptor.getAllValues().get(4)).isEqualTo(FIRST_AID_ENTRY_POWER);
516     verify(mNfcService).commitRouting();
517     assertThat(manager.mDefaultRoute).isEqualTo(OVERRIDE_DEFAULT_ROUTE);
518     assertThat(manager.mRouteForAid.size()).isEqualTo(4);
519     assertThat(manager.mPowerForAid.size()).isEqualTo(4);
520     assertThat(manager.mAidRoutingTable.size()).isEqualTo(2);
521   }
522 
523   /**
524    * Tests the case wherein:
525    *  (1) The default route (mDefaultRoute) is unmodified (DEFAULT_ROUTE).
526    *  (2) Both mOffHostRouteUicc and mOffHostRouteEse are null.
527    *  (3) mAidMatchingSupport is equal to AID_MATCHING_PREFIX_ONLY
528    *  (4) mDefaultIsoDepRoute is equal to ROUTE_HOST (so that the default route is registered)
529    *  (5) NCI Version 1 is used.
530    *
531    *  Ultimately, due to the value of mAidRoutingTableSize, the contents of the cache are committed.
532    */
533   @Test
testConfigureRoutingTestCase7_CommitsCache()534   public void testConfigureRoutingTestCase7_CommitsCache() {
535     when(mRoutingOptionManager.isRoutingTableOverrided()).thenReturn(false);
536     when(mRoutingOptionManager.getDefaultOffHostRoute()).thenReturn(DEFAULT_OFFHOST_ROUTE);
537     when(mRoutingOptionManager.getDefaultRoute()).thenReturn(DEFAULT_ROUTE);
538     when(mRoutingOptionManager.getOffHostRouteUicc()).thenReturn(null);
539     when(mRoutingOptionManager.getOffHostRouteEse()).thenReturn(null);
540     when(mRoutingOptionManager.getAidMatchingSupport()).thenReturn(AID_MATCHING_PREFIX_ONLY);
541     when(mRoutingOptionManager.getDefaultIsoDepRoute()).thenReturn(ROUTE_HOST);
542     when(mNfcService.getNciVersion()).thenReturn(NfcService.NCI_VERSION_1_0);
543     when(mNfcService.getAidRoutingTableSize()).thenReturn(100);
544     manager = new AidRoutingManager();
545 
546     boolean result = manager.configureRouting(getAidMap(), /* force = */ false);
547 
548     assertThat(result).isTrue();
549     verify(mNfcService, never()).unrouteAids(anyString());
550     verify(mNfcService, times(3)).routeAids(routedAidsCaptor.capture(),
551                                             routeCaptor.capture(),
552                                             aidTypeCaptor.capture(),
553                                             powerCaptor.capture());
554     assertThat(routedAidsCaptor.getAllValues().get(0)).isEqualTo("fourthAidEntry");
555     assertThat(routedAidsCaptor.getAllValues().get(1)).isEqualTo("firstAidEntry");
556     assertThat(routedAidsCaptor.getAllValues().get(2)).isEqualTo("thirdAidEntry");
557     assertThat(routeCaptor.getAllValues().get(0)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
558     assertThat(routeCaptor.getAllValues().get(1)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
559     assertThat(routeCaptor.getAllValues().get(2)).isEqualTo(DEFAULT_OFFHOST_ROUTE);
560     assertThat(aidTypeCaptor.getAllValues().get(0)).isEqualTo(FOURTH_AID_ENTRY_AID_INFO);
561     assertThat(aidTypeCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_AID_INFO);
562     assertThat(aidTypeCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_AID_INFO);
563     assertThat(powerCaptor.getAllValues().get(0)).isEqualTo(FOURTH_AID_ENTRY_POWER);
564     assertThat(powerCaptor.getAllValues().get(1)).isEqualTo(FIRST_AID_ENTRY_POWER);
565     assertThat(powerCaptor.getAllValues().get(2)).isEqualTo(THIRD_AID_ENTRY_POWER);
566     verify(mNfcService).commitRouting();
567     assertThat(manager.mDefaultRoute).isEqualTo(DEFAULT_ROUTE);
568     assertThat(manager.mRouteForAid.size()).isEqualTo(4);
569     assertThat(manager.mPowerForAid.size()).isEqualTo(4);
570     assertThat(manager.mAidRoutingTable.size()).isEqualTo(2);
571   }
572 
573   @Test
testOnNfccRoutingTableCleared()574   public void testOnNfccRoutingTableCleared() {
575     manager = new AidRoutingManager();
576     manager.mAidRoutingTable.put(0, new HashSet<String>());
577     manager.mRouteForAid.put("", 0);
578     manager.mPowerForAid.put("", 0);
579 
580     manager.onNfccRoutingTableCleared();
581 
582     assertThat(manager.mAidRoutingTable.size()).isEqualTo(0);
583     assertThat(manager.mRouteForAid.isEmpty()).isTrue();
584     assertThat(manager.mPowerForAid.isEmpty()).isTrue();
585   }
586 
587   @Test
testDump()588   public void testDump() {
589     manager = new AidRoutingManager();
590     HashSet<String> routingTableSet = new HashSet<>();
591     routingTableSet.add("");
592     manager.mAidRoutingTable.put(0, routingTableSet);
593 
594     manager.dump(/* fd = */ null, mPw, /* args = */ null);
595 
596     verify(mPw, times(4)).println(anyString());
597   }
598 
getAidMap()599   private HashMap<String, AidEntry> getAidMap() {
600     HashMap<String, AidEntry> aidMap = new HashMap<>();
601     AidEntry firstAidEntry = manager.new AidEntry();
602     firstAidEntry.isOnHost = false;
603     firstAidEntry.offHostSE = "eSE2";
604     firstAidEntry.power = FIRST_AID_ENTRY_POWER;
605     firstAidEntry.aidInfo = FIRST_AID_ENTRY_AID_INFO;
606     aidMap.put("firstAidEntry*", firstAidEntry);
607 
608     AidEntry secondAidEntry = manager.new AidEntry();
609     secondAidEntry.isOnHost = true;
610     secondAidEntry.power = SECOND_AID_ENTRY_POWER;
611     secondAidEntry.aidInfo = SECOND_AID_ENTRY_AID_INFO;
612     aidMap.put("secondAidEntry#", secondAidEntry);
613 
614     AidEntry thirdAidEntry = manager.new AidEntry();
615     thirdAidEntry.isOnHost = false;
616     thirdAidEntry.offHostSE = "invalid SE";
617     thirdAidEntry.power = THIRD_AID_ENTRY_POWER;
618     thirdAidEntry.aidInfo = THIRD_AID_ENTRY_AID_INFO;
619     aidMap.put("thirdAidEntry", thirdAidEntry);
620 
621     AidEntry fourthAidEntry = manager.new AidEntry();
622     fourthAidEntry.isOnHost = false;
623     fourthAidEntry.offHostSE = "SIM1";
624     fourthAidEntry.power = FOURTH_AID_ENTRY_POWER;
625     fourthAidEntry.aidInfo = FOURTH_AID_ENTRY_AID_INFO;
626     aidMap.put("fourthAidEntry", fourthAidEntry);
627 
628     return aidMap;
629   }
630 }