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 org.mockito.ArgumentMatchers.any; 20 import static org.mockito.ArgumentMatchers.anyString; 21 import static org.mockito.Mockito.never; 22 import static org.mockito.Mockito.times; 23 import static org.mockito.Mockito.verify; 24 import static org.mockito.Mockito.when; 25 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.nfc.cardemulation.NfcFServiceInfo; 29 import android.os.ParcelFileDescriptor; 30 31 import androidx.test.runner.AndroidJUnit4; 32 33 import com.android.dx.mockito.inline.extended.ExtendedMockito; 34 import com.android.nfc.cardemulation.RegisteredT3tIdentifiersCache.T3tIdentifier; 35 36 import java.io.PrintWriter; 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.Locale; 40 41 import org.junit.After; 42 import org.junit.Assert; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.ArgumentCaptor; 47 import org.mockito.Captor; 48 import org.mockito.Mock; 49 import org.mockito.Mockito; 50 import org.mockito.MockitoAnnotations; 51 import org.mockito.MockitoSession; 52 import org.mockito.quality.Strictness; 53 54 @RunWith(AndroidJUnit4.class) 55 public class RegisteredT3tIdentifiersCacheTest { 56 57 private static final String NFCID2 = "nfcid2"; 58 private static final String SYSTEM_CODE = "system code"; 59 private static final String T3TPMM = "t3tpmm"; 60 private static final String ANOTHER_SYSTEM_CODE = "another system code"; 61 private static final int USER_ID = 1; 62 private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder"; 63 private static final String ON_HOST_SERVICE_NAME 64 = "com.android.test.walletroleholder.OnHostApduService"; 65 private static final String NON_PAYMENT_NFC_PACKAGE_NAME = "com.android.test.nonpaymentnfc"; 66 private static final String NON_PAYMENT_SERVICE_NAME 67 = "com.android.test.nonpaymentnfc.NonPaymentApduService"; 68 private static final ComponentName WALLET_HOLDER_SERVICE_COMPONENT = 69 new ComponentName(WALLET_HOLDER_PACKAGE_NAME, ON_HOST_SERVICE_NAME); 70 private static final ComponentName NON_PAYMENT_SERVICE_COMPONENT = 71 new ComponentName(NON_PAYMENT_NFC_PACKAGE_NAME, NON_PAYMENT_SERVICE_NAME); 72 @Mock 73 private Context mContext; 74 @Mock 75 private NfcFServiceInfo mNfcFServiceInfo; 76 @Mock 77 private PrintWriter mPw; 78 @Mock 79 private ParcelFileDescriptor mPfd; 80 @Mock 81 private SystemCodeRoutingManager mRoutingManager; 82 @Captor 83 ArgumentCaptor<List<T3tIdentifier>> identifiersCaptor; 84 85 private RegisteredT3tIdentifiersCache cache; 86 private MockitoSession mStaticMockSession; 87 88 @Before setUp()89 public void setUp() throws Exception { 90 mStaticMockSession = ExtendedMockito.mockitoSession() 91 .mockStatic(ParcelFileDescriptor.class) 92 .strictness(Strictness.LENIENT) 93 .startMocking(); 94 MockitoAnnotations.initMocks(this); 95 when(mNfcFServiceInfo.toString()).thenReturn(""); 96 when(mNfcFServiceInfo.getSystemCode()).thenReturn(SYSTEM_CODE); 97 when(mNfcFServiceInfo.getNfcid2()).thenReturn(NFCID2); 98 when(mNfcFServiceInfo.getT3tPmm()).thenReturn(T3TPMM); 99 when(mNfcFServiceInfo.getComponent()).thenReturn(NON_PAYMENT_SERVICE_COMPONENT); 100 when(ParcelFileDescriptor.dup(any())).thenReturn(mPfd); 101 } 102 103 @After tearDown()104 public void tearDown() { 105 mStaticMockSession.finishMocking(); 106 } 107 108 @Test testConstructor()109 public void testConstructor() { 110 cache = new RegisteredT3tIdentifiersCache(mContext); 111 112 Assert.assertEquals(cache.mContext, mContext); 113 Assert.assertNotNull(cache.mRoutingManager); 114 } 115 116 @Test testT3tIdentifierEquals_ReturnsTrue()117 public void testT3tIdentifierEquals_ReturnsTrue() { 118 cache = new RegisteredT3tIdentifiersCache(mContext); 119 T3tIdentifier firstIdentifier = cache.new T3tIdentifier(SYSTEM_CODE, NFCID2, T3TPMM); 120 T3tIdentifier secondIdentifier 121 = cache.new T3tIdentifier(SYSTEM_CODE.toUpperCase(Locale.ROOT), NFCID2, ""); 122 123 boolean result = firstIdentifier.equals(secondIdentifier); 124 125 Assert.assertTrue(result); 126 } 127 128 @Test testT3tIdentifierEquals_ReturnsFalse()129 public void testT3tIdentifierEquals_ReturnsFalse() { 130 cache = new RegisteredT3tIdentifiersCache(mContext); 131 T3tIdentifier firstIdentifier = cache.new T3tIdentifier(SYSTEM_CODE, NFCID2, T3TPMM); 132 T3tIdentifier secondIdentifier = cache.new T3tIdentifier(ANOTHER_SYSTEM_CODE, NFCID2, T3TPMM); 133 134 boolean result = firstIdentifier.equals(secondIdentifier); 135 136 Assert.assertFalse(result); 137 } 138 139 @Test testResolveNfcid2()140 public void testResolveNfcid2() { 141 cache = new RegisteredT3tIdentifiersCache(mContext); 142 cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); 143 144 NfcFServiceInfo result = cache.resolveNfcid2(NFCID2); 145 146 Assert.assertEquals(result, mNfcFServiceInfo); 147 } 148 149 @Test testOnSecureNfcToggledWithNfcDisabled_ReturnsEarly()150 public void testOnSecureNfcToggledWithNfcDisabled_ReturnsEarly() { 151 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 152 cache.mNfcEnabled = false; 153 154 cache.onSecureNfcToggled(); 155 156 verify(mRoutingManager, never()).configureRouting(any()); 157 } 158 159 @Test testOnSecureNfcToggledWithNfcEnabled_ConfiguresRouting()160 public void testOnSecureNfcToggledWithNfcEnabled_ConfiguresRouting() { 161 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 162 cache.mNfcEnabled = true; 163 cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); 164 165 cache.onSecureNfcToggled(); 166 167 verify(mRoutingManager, times(2)).configureRouting(identifiersCaptor.capture()); 168 List<T3tIdentifier> firstList = identifiersCaptor.getAllValues().get(0); 169 List<T3tIdentifier> secondList = identifiersCaptor.getAllValues().get(1); 170 Assert.assertEquals(1, firstList.size()); 171 T3tIdentifier identifier = firstList.get(0); 172 Assert.assertEquals(SYSTEM_CODE, identifier.systemCode); 173 Assert.assertEquals(NFCID2, identifier.nfcid2); 174 Assert.assertEquals(T3TPMM, identifier.t3tPmm); 175 Assert.assertEquals(1, secondList.size()); 176 Assert.assertEquals(secondList.get(0), identifier); 177 } 178 179 @Test testOnServicesUpdated()180 public void testOnServicesUpdated() { 181 cache = new RegisteredT3tIdentifiersCache(mContext); 182 ArrayList<NfcFServiceInfo> serviceList = getServiceList(); 183 184 cache.onServicesUpdated(USER_ID, serviceList); 185 186 Assert.assertEquals(1, cache.mUserNfcFServiceInfo.size()); 187 Assert.assertEquals(serviceList, cache.mUserNfcFServiceInfo.get(USER_ID)); 188 } 189 190 /** 191 * Tests the case where the component passed in is null and mEnabledForegroundService is also 192 * null. Ultimately, no update is performed. 193 */ 194 @Test testOnEnabledForegroundNfcFServiceChangedCase1()195 public void testOnEnabledForegroundNfcFServiceChangedCase1() { 196 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 197 cache.mEnabledForegroundService = null; 198 cache.mEnabledForegroundServiceUserId = USER_ID; 199 cache.mNfcEnabled = true; 200 201 cache.onEnabledForegroundNfcFServiceChanged(USER_ID, /* component = */ null); 202 203 Assert.assertEquals(USER_ID, cache.mEnabledForegroundServiceUserId); 204 Assert.assertNull(cache.mEnabledForegroundService); 205 verify(mRoutingManager, never()).configureRouting(any()); 206 } 207 208 /** 209 * Tests the case where the component passed in is null and mEnabledForegroundService is non-null. 210 * Ultimately, routing is configured. 211 */ 212 @Test testOnEnabledForegroundNfcFServiceChangedCase2()213 public void testOnEnabledForegroundNfcFServiceChangedCase2() { 214 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 215 cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; 216 cache.mEnabledForegroundServiceUserId = USER_ID; 217 cache.mNfcEnabled = true; 218 219 cache.onEnabledForegroundNfcFServiceChanged(USER_ID, /* component = */ null); 220 221 Assert.assertNull(cache.mEnabledForegroundService); 222 Assert.assertEquals(-1, cache.mEnabledForegroundServiceUserId); 223 verify(mRoutingManager).configureRouting(identifiersCaptor.capture()); 224 Assert.assertTrue(identifiersCaptor.getValue().isEmpty()); 225 } 226 227 /** 228 * Tests the case where the component passed in is non-null and mEnabledForegroundService is null. 229 * Ultimately, routing is configured. 230 */ 231 @Test testOnEnabledForegroundNfcFServiceChangedCase3()232 public void testOnEnabledForegroundNfcFServiceChangedCase3() { 233 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 234 cache.mEnabledForegroundService = null; 235 cache.mEnabledForegroundServiceUserId = -1; 236 cache.mUserNfcFServiceInfo.put(USER_ID, getServiceList()); 237 cache.mNfcEnabled = true; 238 239 cache.onEnabledForegroundNfcFServiceChanged(USER_ID, NON_PAYMENT_SERVICE_COMPONENT); 240 241 Assert.assertEquals(NON_PAYMENT_SERVICE_COMPONENT, cache.mEnabledForegroundService); 242 Assert.assertEquals(USER_ID, cache.mEnabledForegroundServiceUserId); 243 verify(mRoutingManager).configureRouting(identifiersCaptor.capture()); 244 Assert.assertEquals(1, identifiersCaptor.getValue().size()); 245 T3tIdentifier identifier = identifiersCaptor.getValue().get(0); 246 Assert.assertEquals(SYSTEM_CODE, identifier.systemCode); 247 Assert.assertEquals(NFCID2, identifier.nfcid2); 248 Assert.assertEquals(T3TPMM, identifier.t3tPmm); 249 } 250 251 /** 252 * Tests the case where the component passed in is non-null and mEnabledForegroundService is also 253 * non-null. Ultimately, no action is taken. 254 */ 255 @Test testOnEnabledForegroundNfcFServiceChangedCase4()256 public void testOnEnabledForegroundNfcFServiceChangedCase4() { 257 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 258 cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; 259 cache.mEnabledForegroundServiceUserId = USER_ID; 260 cache.mNfcEnabled = true; 261 262 cache.onEnabledForegroundNfcFServiceChanged(USER_ID, NON_PAYMENT_SERVICE_COMPONENT); 263 264 Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, cache.mEnabledForegroundService); 265 Assert.assertEquals(USER_ID, cache.mEnabledForegroundServiceUserId); 266 verify(mRoutingManager, never()).configureRouting(any()); 267 } 268 269 @Test testOnNfcEnabled()270 public void testOnNfcEnabled() { 271 cache = new RegisteredT3tIdentifiersCache(mContext); 272 273 cache.onNfcEnabled(); 274 275 Assert.assertTrue(cache.mNfcEnabled); 276 } 277 278 @Test testOnNfcDisabled()279 public void testOnNfcDisabled() { 280 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 281 cache.mNfcEnabled = true; 282 cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); 283 cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; 284 cache.mEnabledForegroundServiceUserId = USER_ID; 285 286 cache.onNfcDisabled(); 287 288 Assert.assertFalse(cache.mNfcEnabled); 289 Assert.assertTrue(cache.mForegroundT3tIdentifiersCache.isEmpty()); 290 Assert.assertNull(cache.mEnabledForegroundService); 291 Assert.assertEquals(-1, cache.mEnabledForegroundServiceUserId); 292 verify(mRoutingManager).onNfccRoutingTableCleared(); 293 } 294 295 @Test testOnUserSwitched()296 public void testOnUserSwitched() { 297 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 298 cache.mNfcEnabled = true; 299 cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); 300 cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; 301 cache.mEnabledForegroundServiceUserId = USER_ID; 302 303 cache.onUserSwitched(); 304 305 Assert.assertTrue(cache.mForegroundT3tIdentifiersCache.isEmpty()); 306 Assert.assertNull(cache.mEnabledForegroundService); 307 Assert.assertEquals(-1, cache.mEnabledForegroundServiceUserId); 308 verify(mRoutingManager).configureRouting(identifiersCaptor.capture()); 309 Assert.assertTrue(identifiersCaptor.getValue().isEmpty()); 310 } 311 312 @Test testDump()313 public void testDump() throws Exception { 314 cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); 315 cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); 316 317 cache.dump(/* fd = */ null, mPw, /* args = */ null); 318 319 verify(mPw, times(5)).println(anyString()); 320 verify(mPfd).close(); 321 verify(mNfcFServiceInfo).dump(any(), any(), any()); 322 verify(mRoutingManager).dump(any(), any(), any()); 323 } 324 getServiceList()325 private ArrayList<NfcFServiceInfo> getServiceList() { 326 ArrayList<NfcFServiceInfo> list = new ArrayList<NfcFServiceInfo>(); 327 list.add(mNfcFServiceInfo); 328 return list; 329 } 330 }