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.anyInt; 21 import static org.mockito.ArgumentMatchers.anyString; 22 import static org.mockito.ArgumentMatchers.eq; 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.times; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.app.ActivityManager; 29 import android.content.BroadcastReceiver; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.PackageManager; 35 import android.content.pm.ResolveInfo; 36 import android.content.pm.ServiceInfo; 37 import android.net.Uri; 38 import android.nfc.cardemulation.NfcFCardEmulation; 39 import android.nfc.cardemulation.NfcFServiceInfo; 40 import android.os.Handler; 41 import android.os.ParcelFileDescriptor; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.util.AtomicFile; 45 import android.util.Xml; 46 47 import androidx.test.platform.app.InstrumentationRegistry; 48 import androidx.test.runner.AndroidJUnit4; 49 50 import com.android.dx.mockito.inline.extended.ExtendedMockito; 51 import com.android.nfc.cardemulation.RegisteredNfcFServicesCache.DynamicNfcid2; 52 import com.android.nfc.cardemulation.RegisteredNfcFServicesCache.DynamicSystemCode; 53 import com.android.nfc.cardemulation.RegisteredNfcFServicesCache.UserServices; 54 55 import java.io.File; 56 import java.io.FileInputStream; 57 import java.io.FileOutputStream; 58 import java.io.IOException; 59 import java.io.PrintWriter; 60 import java.util.ArrayList; 61 import java.util.List; 62 import java.util.Locale; 63 64 import org.junit.After; 65 import org.junit.Assert; 66 import org.junit.Before; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 import org.mockito.ArgumentCaptor; 70 import org.mockito.Captor; 71 import org.mockito.Mock; 72 import org.mockito.Mockito; 73 import org.mockito.MockitoAnnotations; 74 import org.mockito.MockitoSession; 75 import org.mockito.quality.Strictness; 76 import org.xmlpull.v1.XmlPullParser; 77 import org.xmlpull.v1.XmlSerializer; 78 79 @RunWith(AndroidJUnit4.class) 80 public class RegisteredNfcFServicesCacheTest { 81 82 private static final File DIR = new File("/"); 83 private static final int USER_ID = 1; 84 private static final int ANOTHER_USER_ID = 2; 85 private static final UserHandle USER_HANDLE = UserHandle.of(USER_ID); 86 private static final UserHandle USER_HANDLE_QUIET_MODE = UserHandle.of(ANOTHER_USER_ID); 87 private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder"; 88 private static final String NON_PAYMENT_NFC_PACKAGE_NAME = "com.android.test.nonpaymentnfc"; 89 private static final String ON_HOST_SERVICE_NAME 90 = "com.android.test.walletroleholder.OnHostApduService"; 91 private static final String NON_PAYMENT_SERVICE_NAME 92 = "com.android.test.nonpaymentnfc.NonPaymentApduService"; 93 private static final int SERVICE_UID = 4; 94 private static final int SYSTEM_CODE_UID = 5; 95 private static final int NFCID2_UID = 6; 96 private static final String SYSTEM_CODE = "dynamic system code"; 97 private static final String NFCID2 = "RANDOM"; 98 private static final ComponentName WALLET_COMPONENT 99 = new ComponentName(WALLET_HOLDER_PACKAGE_NAME, 100 "com.android.test.walletroleholder.WalletRoleHolderApduService"); 101 102 private RegisteredNfcFServicesCache cache; 103 private MockitoSession mStaticMockSession; 104 @Mock 105 private Context mContext; 106 @Mock 107 private RegisteredNfcFServicesCache.Callback mCallback; 108 @Mock 109 private UserManager mUserManager; 110 @Mock 111 private NfcFServiceInfo mNfcFServiceInfo; 112 @Mock 113 private PackageManager mPackageManager; 114 @Mock 115 private AtomicFile mAtomicFile; 116 @Mock 117 private FileOutputStream mFos; 118 @Mock 119 private FileInputStream mFis; 120 @Mock 121 private XmlSerializer mSerializer; 122 @Mock 123 private XmlPullParser mParser; 124 @Mock 125 private PrintWriter mPw; 126 @Mock 127 private ParcelFileDescriptor mPfd; 128 @Mock 129 private File mFile; 130 131 @Captor 132 private ArgumentCaptor<BroadcastReceiver> receiverCaptor; 133 @Captor 134 private ArgumentCaptor<IntentFilter> intentFilterCaptor; 135 @Captor 136 private ArgumentCaptor<String> broadcastPermissionCaptor; 137 @Captor 138 private ArgumentCaptor<Handler> schedulerCaptor; 139 @Captor 140 private ArgumentCaptor<List<NfcFServiceInfo>> servicesCaptor; 141 @Captor 142 private ArgumentCaptor<Integer> userIdCaptor; 143 @Captor 144 private ArgumentCaptor<String> systemCodeCaptor; 145 @Captor 146 private ArgumentCaptor<String> nfcid2Captor; 147 148 @Before setUp()149 public void setUp() throws Exception { 150 mStaticMockSession = ExtendedMockito.mockitoSession() 151 .mockStatic(ActivityManager.class) 152 .mockStatic(UserHandle.class) 153 .mockStatic(NfcFCardEmulation.class) 154 .mockStatic(Xml.class) 155 .mockStatic(ParcelFileDescriptor.class) 156 .strictness(Strictness.LENIENT) 157 .startMocking(); 158 MockitoAnnotations.initMocks(this); 159 when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager); 160 when(mContext.createContextAsUser(any(), anyInt())).thenReturn(mContext); 161 when(mContext.getFilesDir()).thenReturn(DIR); 162 when(mContext.createPackageContextAsUser(any(), anyInt(), any())).thenReturn(mContext); 163 when(mContext.getPackageManager()).thenReturn(mPackageManager); 164 ArrayList<UserHandle> userHandles = new ArrayList<UserHandle>(); 165 userHandles.add(USER_HANDLE); 166 userHandles.add(USER_HANDLE_QUIET_MODE); 167 when(mUserManager.getEnabledProfiles()).thenReturn(userHandles); 168 when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE))).thenReturn(false); 169 when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE_QUIET_MODE))).thenReturn(true); 170 when(ParcelFileDescriptor.dup(any())).thenReturn(mPfd); 171 when(UserHandle.getUserHandleForUid(anyInt())).thenReturn(USER_HANDLE); 172 when(Xml.newSerializer()).thenReturn(mSerializer); 173 when(Xml.newPullParser()).thenReturn(mParser); 174 when(mAtomicFile.startWrite()).thenReturn(mFos); 175 when(mAtomicFile.openRead()).thenReturn(mFis); 176 when(mAtomicFile.getBaseFile()).thenReturn(mFile); 177 when(mFile.exists()).thenReturn(true); 178 } 179 180 @After tearDown()181 public void tearDown() { 182 mStaticMockSession.finishMocking(); 183 } 184 185 @Test testConstructor()186 public void testConstructor() { 187 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 188 189 verify(mContext, times(2)) 190 .registerReceiverForAllUsers(receiverCaptor.capture(), intentFilterCaptor.capture(), 191 broadcastPermissionCaptor.capture(), schedulerCaptor.capture()); 192 Assert.assertEquals(receiverCaptor.getAllValues().get(0), cache.mReceiver.get()); 193 Assert.assertEquals(receiverCaptor.getAllValues().get(1), cache.mReceiver.get()); 194 Assert.assertNotNull(cache.mReceiver.get()); 195 IntentFilter intentFilter = intentFilterCaptor.getAllValues().get(0); 196 IntentFilter sdFilter = intentFilterCaptor.getAllValues().get(1); 197 Assert.assertTrue(intentFilter.hasAction(Intent.ACTION_PACKAGE_ADDED)); 198 Assert.assertTrue(intentFilter.hasAction(Intent.ACTION_PACKAGE_CHANGED)); 199 Assert.assertTrue(intentFilter.hasAction(Intent.ACTION_PACKAGE_REMOVED)); 200 Assert.assertTrue(intentFilter.hasAction(Intent.ACTION_PACKAGE_REPLACED)); 201 Assert.assertTrue(intentFilter.hasAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH)); 202 Assert.assertTrue(intentFilter.hasAction(Intent.ACTION_PACKAGE_RESTARTED)); 203 Assert.assertTrue(intentFilter.hasDataScheme("package")); 204 Assert.assertTrue(sdFilter.hasAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE)); 205 Assert.assertTrue(sdFilter.hasAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)); 206 Assert.assertNull(broadcastPermissionCaptor.getAllValues().get(0)); 207 Assert.assertNull(broadcastPermissionCaptor.getAllValues().get(1)); 208 Assert.assertNull(schedulerCaptor.getAllValues().get(0)); 209 Assert.assertNull(schedulerCaptor.getAllValues().get(1)); 210 synchronized (cache.mLock) { 211 Assert.assertEquals(cache.mUserHandles.get(0), USER_HANDLE); 212 } 213 Assert.assertEquals(cache.mContext, mContext); 214 Assert.assertEquals(cache.mCallback, mCallback); 215 Assert.assertEquals(cache.mDynamicSystemCodeNfcid2File.getBaseFile().getParentFile(), DIR); 216 Assert.assertEquals(cache.mDynamicSystemCodeNfcid2File.getBaseFile().getAbsolutePath(), 217 DIR + "dynamic_systemcode_nfcid2.xml"); 218 } 219 220 @Test testBroadcastReceiverOnReceive_DoesNothing()221 public void testBroadcastReceiverOnReceive_DoesNothing() { 222 when(ActivityManager.getCurrentUser()).thenReturn(ANOTHER_USER_ID); 223 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 224 setResolveInfoList(); 225 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 226 227 cache.mReceiver.get().onReceive(mContext, getBroadcastReceiverIntent()); 228 229 verify(mCallback, never()).onNfcFServicesUpdated(anyInt(), any()); 230 } 231 232 @Test testBroadcastReceiverOnReceive_CommitsCache()233 public void testBroadcastReceiverOnReceive_CommitsCache() { 234 when(ActivityManager.getCurrentUser()).thenReturn(USER_ID); 235 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 236 setResolveInfoList(); 237 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 238 239 cache.mReceiver.get().onReceive(mContext, getBroadcastReceiverIntent()); 240 241 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 242 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 243 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 244 } 245 246 @Test testHasService_ReturnsFalse()247 public void testHasService_ReturnsFalse() { 248 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 249 250 boolean result = cache.hasService(USER_ID, WALLET_COMPONENT); 251 252 Assert.assertFalse(result); 253 } 254 255 @Test testHasService_ReturnsTrue()256 public void testHasService_ReturnsTrue() throws Exception { 257 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 258 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 259 260 boolean result = cache.hasService(USER_ID, WALLET_COMPONENT); 261 262 Assert.assertTrue(result); 263 } 264 265 @Test testGetService_ReturnsNull()266 public void testGetService_ReturnsNull() throws Exception { 267 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 268 269 NfcFServiceInfo result = cache.getService(USER_ID, WALLET_COMPONENT); 270 271 Assert.assertNull(result); 272 } 273 274 @Test testGetService_ReturnsService()275 public void testGetService_ReturnsService() throws Exception { 276 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 277 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 278 279 NfcFServiceInfo result = cache.getService(USER_ID, WALLET_COMPONENT); 280 281 Assert.assertEquals(result, mNfcFServiceInfo); 282 } 283 284 @Test testGetServices()285 public void testGetServices() throws Exception { 286 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 287 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 288 289 List<NfcFServiceInfo> result = cache.getServices(USER_ID); 290 291 Assert.assertEquals(result.get(0), mNfcFServiceInfo); 292 } 293 294 295 @Test testInvalidateCacheWithNoValidServices_ReturnsEarly()296 public void testInvalidateCacheWithNoValidServices_ReturnsEarly() throws Exception { 297 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 298 299 cache.invalidateCache(USER_ID); 300 301 verify(mCallback, never()).onNfcFServicesUpdated(anyInt(), any()); 302 } 303 304 /** 305 * Tests the case wherein: 306 * -) All three objects in mUserServices (one each in services, dynamicSystemCode, and 307 * dynamicNfcid2) have unique UIDs. 308 * -) A random NFCID2 is not requested 309 * 310 * Ultimately, an update (consisting of mNfcFService) is committed through the callback class, 311 * and the contents of dynamicSystemCode and dynamicNfcid2 are erased. 312 */ 313 @Test testInvalidateCacheWithValidServicesCase1_CommitsCache()314 public void testInvalidateCacheWithValidServicesCase1_CommitsCache() { 315 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 316 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 317 setResolveInfoList(); 318 319 cache.invalidateCache(USER_ID); 320 321 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 322 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 323 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 324 UserServices userServicesResult = cache.mUserServices.get(USER_ID); 325 Assert.assertEquals(userServicesResult.services.get(WALLET_COMPONENT), mNfcFServiceInfo); 326 Assert.assertTrue(userServicesResult.dynamicSystemCode.isEmpty()); 327 Assert.assertTrue(userServicesResult.dynamicNfcid2.isEmpty()); 328 verify(mNfcFServiceInfo, never()).setDynamicSystemCode(anyString()); 329 verify(mNfcFServiceInfo, never()).setDynamicNfcid2(anyString()); 330 } 331 332 /** 333 * Tests the case wherein: 334 * -) The mNfcServiceInfo object in mUserServices.services shares the same UID as the object 335 * in dynamicSystemCode. 336 * -) A random NFCID2 is not requested 337 * 338 * Ultimately, an update (consisting of mNfcFService) is committed through the callback class, 339 * and the contents of dynamicNfcid2 are erased. 340 */ 341 @Test testInvalidateCacheWithValidServicesCase2_CommitsCache()342 public void testInvalidateCacheWithValidServicesCase2_CommitsCache() { 343 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 344 setUserServices(/* serviceInfoUid = */ SYSTEM_CODE_UID, /* serviceInfoNfcid2 = */ ""); 345 setResolveInfoList(); 346 347 cache.invalidateCache(USER_ID); 348 349 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 350 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 351 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 352 UserServices userServicesResult = cache.mUserServices.get(USER_ID); 353 Assert.assertEquals(userServicesResult.services.get(WALLET_COMPONENT), mNfcFServiceInfo); 354 Assert.assertEquals(1, userServicesResult.dynamicSystemCode.size()); 355 verify(mNfcFServiceInfo).setDynamicSystemCode(systemCodeCaptor.capture()); 356 Assert.assertEquals(SYSTEM_CODE, systemCodeCaptor.getValue()); 357 Assert.assertTrue(userServicesResult.dynamicNfcid2.isEmpty()); 358 verify(mNfcFServiceInfo, never()).setDynamicNfcid2(anyString()); 359 } 360 361 /** 362 * Tests the case wherein: 363 * -) The mNfcServiceInfo object in mUserServices.services shares the same UID as the object 364 * in dynamicNfcid2. 365 * -) A random NFCID2 is not requested 366 * 367 * Ultimately, an update (consisting of mNfcFService) is committed through the callback class, 368 * and the contents of dynamicSystemCode are erased. 369 */ 370 @Test testInvalidateCacheWithValidServicesCase3_CommitsCache()371 public void testInvalidateCacheWithValidServicesCase3_CommitsCache() { 372 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 373 setUserServices(/* serviceInfoUid = */ NFCID2_UID, /* serviceInfoNfcid2 = */ ""); 374 setResolveInfoList(); 375 376 cache.invalidateCache(USER_ID); 377 378 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 379 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 380 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 381 UserServices userServicesResult = cache.mUserServices.get(USER_ID); 382 Assert.assertEquals(userServicesResult.services.get(WALLET_COMPONENT), mNfcFServiceInfo); 383 Assert.assertTrue(userServicesResult.dynamicSystemCode.isEmpty()); 384 verify(mNfcFServiceInfo, never()).setDynamicSystemCode(anyString()); 385 Assert.assertEquals(1, userServicesResult.dynamicNfcid2.size()); 386 verify(mNfcFServiceInfo).setDynamicNfcid2(nfcid2Captor.capture()); 387 Assert.assertEquals(NFCID2, nfcid2Captor.getValue()); 388 } 389 390 /** 391 * Tests the case wherein: 392 * -) All three objects in mUserServices (one each in services, dynamicSystemCode, and 393 * dynamicNfcid2) have unique UIDs. 394 * -) A random NFCID2 is requested 395 * 396 * Ultimately, an update (consisting of mNfcFService) is committed through the callback class, 397 * and the contents of dynamicSystemCode are erased. 398 */ 399 @Test testInvalidateCacheWithValidServicesCase4_CommitsCache()400 public void testInvalidateCacheWithValidServicesCase4_CommitsCache() { 401 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 402 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ "RANDOM"); 403 setResolveInfoList(); 404 405 cache.invalidateCache(USER_ID); 406 407 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 408 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 409 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 410 UserServices userServicesResult = cache.mUserServices.get(USER_ID); 411 Assert.assertEquals(userServicesResult.services.get(WALLET_COMPONENT), mNfcFServiceInfo); 412 Assert.assertTrue(userServicesResult.dynamicSystemCode.isEmpty()); 413 verify(mNfcFServiceInfo, never()).setDynamicSystemCode(anyString()); 414 Assert.assertEquals(1, userServicesResult.dynamicNfcid2.size()); 415 verify(mNfcFServiceInfo).setDynamicNfcid2(anyString()); 416 } 417 418 @Test testRegisterSystemCodeForServiceWhenActivated_ReturnsFalse()419 public void testRegisterSystemCodeForServiceWhenActivated_ReturnsFalse() { 420 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 421 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 422 cache.mActivated = true; 423 424 boolean result 425 = cache.registerSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, SYSTEM_CODE); 426 427 Assert.assertFalse(result); 428 } 429 430 @Test testRegisterSystemCodeForServiceWithNullService_ReturnsFalse()431 public void testRegisterSystemCodeForServiceWithNullService_ReturnsFalse() { 432 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 433 cache.mActivated = false; 434 435 boolean result 436 = cache.registerSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, SYSTEM_CODE); 437 438 Assert.assertFalse(result); 439 } 440 441 @Test testRegisterSystemCodeForServiceWithUidMismatch_ReturnsFalse()442 public void testRegisterSystemCodeForServiceWithUidMismatch_ReturnsFalse() { 443 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 444 setUserServices(/* serviceInfoUid = */ NFCID2_UID, /* serviceInfoNfcid2 = */ ""); 445 cache.mActivated = false; 446 447 boolean result 448 = cache.registerSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, SYSTEM_CODE); 449 450 Assert.assertFalse(result); 451 } 452 453 @Test testRegisterSystemCodeForServiceWithInvalidSystemCode_ReturnsFalse()454 public void testRegisterSystemCodeForServiceWithInvalidSystemCode_ReturnsFalse() { 455 when(NfcFCardEmulation.isValidSystemCode(eq(SYSTEM_CODE))).thenReturn(false); 456 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 457 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 458 cache.mActivated = false; 459 460 boolean result 461 = cache.registerSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, SYSTEM_CODE); 462 463 Assert.assertFalse(result); 464 } 465 466 @Test testRegisterSystemCodeForServiceWhenFailedtoPersistSystemCode_ReturnsFalse()467 public void testRegisterSystemCodeForServiceWhenFailedtoPersistSystemCode_ReturnsFalse() { 468 when(NfcFCardEmulation.isValidSystemCode(eq(SYSTEM_CODE))).thenReturn(true); 469 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 470 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 471 cache.mActivated = false; 472 473 boolean result 474 = cache.registerSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, SYSTEM_CODE); 475 476 Assert.assertFalse(result); 477 verify(mCallback, never()).onNfcFServicesUpdated(anyInt(), any()); 478 } 479 480 @Test testRegisterSystemCodeForService_ReturnsTrue()481 public void testRegisterSystemCodeForService_ReturnsTrue() throws Exception { 482 when(NfcFCardEmulation.isValidSystemCode(eq(SYSTEM_CODE))).thenReturn(true); 483 cache = new RegisteredNfcFServicesCache(mContext, mCallback, mAtomicFile); 484 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 485 cache.mActivated = false; 486 487 boolean result 488 = cache.registerSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, SYSTEM_CODE); 489 490 Assert.assertTrue(result); 491 verify(mNfcFServiceInfo).setDynamicSystemCode(systemCodeCaptor.capture()); 492 Assert.assertEquals(SYSTEM_CODE.toUpperCase(Locale.ROOT), systemCodeCaptor.getValue()); 493 UserServices userServicesResult = cache.mUserServices.get(USER_ID); 494 Assert.assertEquals(1, userServicesResult.dynamicSystemCode.size()); 495 DynamicSystemCode resultSystemCode = userServicesResult.dynamicSystemCode.get(WALLET_COMPONENT); 496 Assert.assertEquals(SERVICE_UID, resultSystemCode.uid); 497 Assert.assertEquals(SYSTEM_CODE.toUpperCase(Locale.ROOT), resultSystemCode.systemCode); 498 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 499 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 500 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 501 } 502 503 @Test testGetSystemCodeForServiceWithNullService_ReturnsNull()504 public void testGetSystemCodeForServiceWithNullService_ReturnsNull() { 505 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 506 507 String result = cache.getSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT); 508 509 Assert.assertNull(result); 510 } 511 512 @Test testGetSystemCodeForServiceWithMismatchedUid_ReturnsNull()513 public void testGetSystemCodeForServiceWithMismatchedUid_ReturnsNull() { 514 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 515 setUserServices(/* serviceInfoUid = */ SYSTEM_CODE_UID, /* serviceInfoNfcid2 = */ ""); 516 517 String result = cache.getSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT); 518 519 Assert.assertNull(result); 520 } 521 522 @Test testGetSystemCodeForService_ReturnsSystemCode()523 public void testGetSystemCodeForService_ReturnsSystemCode() { 524 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 525 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 526 527 String result = cache.getSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT); 528 529 Assert.assertEquals(SYSTEM_CODE, result); 530 } 531 532 @Test testRemoveSystemCodeForService_ReturnsTrue()533 public void testRemoveSystemCodeForService_ReturnsTrue() throws Exception { 534 when(NfcFCardEmulation.isValidSystemCode(any())).thenReturn(true); 535 cache = new RegisteredNfcFServicesCache(mContext, mCallback, mAtomicFile); 536 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 537 cache.mActivated = false; 538 539 boolean result = cache.removeSystemCodeForService(USER_ID, SERVICE_UID, WALLET_COMPONENT); 540 541 Assert.assertTrue(result); 542 verify(mNfcFServiceInfo).setDynamicSystemCode(systemCodeCaptor.capture()); 543 Assert.assertEquals("NULL", systemCodeCaptor.getValue()); 544 DynamicSystemCode resultSystemCode 545 = cache.mUserServices.get(USER_ID).dynamicSystemCode.get(WALLET_COMPONENT); 546 Assert.assertEquals(SERVICE_UID, resultSystemCode.uid); 547 Assert.assertEquals("NULL", resultSystemCode.systemCode); 548 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 549 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 550 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 551 } 552 553 554 @Test testSetNfcid2ForServiceWhenActivated_ReturnsFalse()555 public void testSetNfcid2ForServiceWhenActivated_ReturnsFalse() { 556 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 557 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 558 cache.mActivated = true; 559 560 boolean result = cache.setNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, NFCID2); 561 562 Assert.assertFalse(result); 563 } 564 565 @Test testSetNfcid2ForServiceWithNullService_ReturnsFalse()566 public void testSetNfcid2ForServiceWithNullService_ReturnsFalse() { 567 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 568 cache.mActivated = false; 569 570 boolean result = cache.setNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, NFCID2); 571 572 Assert.assertFalse(result); 573 } 574 575 @Test testSetNfcid2ForServiceWithUidMismatch_ReturnsFalse()576 public void testSetNfcid2ForServiceWithUidMismatch_ReturnsFalse() { 577 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 578 setUserServices(/* serviceInfoUid = */ NFCID2_UID, /* serviceInfoNfcid2 = */ ""); 579 cache.mActivated = false; 580 581 boolean result = cache.setNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, NFCID2); 582 583 Assert.assertFalse(result); 584 } 585 586 @Test testSetNfcid2ForServiceWithInvalidSystemCode_ReturnsFalse()587 public void testSetNfcid2ForServiceWithInvalidSystemCode_ReturnsFalse() { 588 when(NfcFCardEmulation.isValidNfcid2(eq(NFCID2))).thenReturn(false); 589 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 590 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 591 cache.mActivated = false; 592 593 boolean result = cache.setNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, NFCID2); 594 595 Assert.assertFalse(result); 596 } 597 598 @Test testSetNfcid2ForServiceWhenFailedtoPersistSystemCode_ReturnsFalse()599 public void testSetNfcid2ForServiceWhenFailedtoPersistSystemCode_ReturnsFalse() { 600 when(NfcFCardEmulation.isValidNfcid2(eq(NFCID2))).thenReturn(true); 601 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 602 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 603 cache.mActivated = false; 604 605 boolean result = cache.setNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, NFCID2); 606 607 Assert.assertFalse(result); 608 verify(mCallback, never()).onNfcFServicesUpdated(anyInt(), any()); 609 } 610 611 @Test testSetNfcid2ForService_ReturnsTrue()612 public void testSetNfcid2ForService_ReturnsTrue() throws Exception { 613 when(NfcFCardEmulation.isValidNfcid2(eq(NFCID2))).thenReturn(true); 614 cache = new RegisteredNfcFServicesCache(mContext, mCallback, mAtomicFile); 615 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 616 cache.mActivated = false; 617 618 boolean result = cache.setNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT, NFCID2); 619 620 Assert.assertTrue(result); 621 verify(mNfcFServiceInfo).setDynamicNfcid2(nfcid2Captor.capture()); 622 Assert.assertEquals(NFCID2.toUpperCase(Locale.ROOT), nfcid2Captor.getValue()); 623 DynamicNfcid2 resultNfcid2 624 = cache.mUserServices.get(USER_ID).dynamicNfcid2.get(WALLET_COMPONENT); 625 Assert.assertEquals(SERVICE_UID, resultNfcid2.uid); 626 Assert.assertEquals(NFCID2.toUpperCase(Locale.ROOT), resultNfcid2.nfcid2); 627 verify(mCallback).onNfcFServicesUpdated(userIdCaptor.capture(), servicesCaptor.capture()); 628 Assert.assertEquals(userIdCaptor.getValue(), Integer.valueOf(USER_ID)); 629 Assert.assertEquals(servicesCaptor.getValue().get(0), mNfcFServiceInfo); 630 } 631 632 @Test testgetNfcid2ForServiceWithNullService_ReturnsNull()633 public void testgetNfcid2ForServiceWithNullService_ReturnsNull() { 634 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 635 636 String result = cache.getNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT); 637 638 Assert.assertNull(result); 639 } 640 641 @Test testGetNfcid2ForServiceWithMismatchedUid_ReturnsNull()642 public void testGetNfcid2ForServiceWithMismatchedUid_ReturnsNull() { 643 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 644 setUserServices(/* serviceInfoUid = */ SYSTEM_CODE_UID, /* serviceInfoNfcid2 = */ ""); 645 646 String result = cache.getNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT); 647 648 Assert.assertNull(result); 649 } 650 651 @Test testGetNfcid2ForService_ReturnsSystemCode()652 public void testGetNfcid2ForService_ReturnsSystemCode() { 653 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 654 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ NFCID2); 655 656 String result = cache.getNfcid2ForService(USER_ID, SERVICE_UID, WALLET_COMPONENT); 657 658 Assert.assertEquals(NFCID2, result); 659 } 660 661 @Test testOnHostEmulationActivated()662 public void testOnHostEmulationActivated() { 663 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 664 665 cache.onHostEmulationActivated(); 666 667 Assert.assertTrue(cache.mActivated); 668 } 669 670 @Test testOnHostEmulationDeactivated()671 public void testOnHostEmulationDeactivated() { 672 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 673 674 cache.onHostEmulationDeactivated(); 675 676 Assert.assertFalse(cache.mActivated); 677 } 678 679 @Test testOnNfcDisabled()680 public void testOnNfcDisabled() { 681 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 682 683 cache.onNfcDisabled(); 684 685 Assert.assertFalse(cache.mActivated); 686 } 687 688 @Test testOnUserSwitched()689 public void testOnUserSwitched() { 690 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 691 692 cache.onUserSwitched(); 693 694 Assert.assertTrue(cache.mUserSwitched); 695 synchronized (cache.mLock) { 696 Assert.assertEquals(cache.mUserHandles.get(0), USER_HANDLE); 697 } 698 } 699 700 @Test testOnManagedProfileChanged()701 public void testOnManagedProfileChanged() { 702 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 703 704 cache.onManagedProfileChanged(); 705 706 synchronized (cache.mLock) { 707 Assert.assertEquals(cache.mUserHandles.get(0), USER_HANDLE); 708 } 709 } 710 711 @Test testDump()712 public void testDump() { 713 cache = new RegisteredNfcFServicesCache(mContext, mCallback); 714 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 715 716 cache.dump(/* fd = */ null, mPw, /* args = */ null); 717 718 verify(mPw, times(4)).println(anyString()); 719 verify(mNfcFServiceInfo).dump(any(), any(), any()); 720 } 721 722 @Test testWriteDynamicSystemCodeNfcid2LockedWithInvalidSerializer_ReturnsFalse()723 public void testWriteDynamicSystemCodeNfcid2LockedWithInvalidSerializer_ReturnsFalse() 724 throws Exception { 725 when(mAtomicFile.startWrite()).thenThrow(IOException.class); 726 cache = new RegisteredNfcFServicesCache(mContext, mCallback, mAtomicFile); 727 728 boolean result = cache.writeDynamicSystemCodeNfcid2Locked(); 729 730 Assert.assertFalse(result); 731 verify(mAtomicFile, never()).failWrite(any(FileOutputStream.class)); 732 } 733 734 @Test testWriteDynamicSystemCodeNfcid2LockedWithNoServices_ReturnsTrue()735 public void testWriteDynamicSystemCodeNfcid2LockedWithNoServices_ReturnsTrue() throws Exception { 736 cache = new RegisteredNfcFServicesCache(mContext, mCallback, mAtomicFile); 737 738 boolean result = cache.writeDynamicSystemCodeNfcid2Locked(); 739 740 Assert.assertTrue(result); 741 verify(mAtomicFile).startWrite(); 742 verify(mAtomicFile).finishWrite(any(FileOutputStream.class)); 743 verify(mAtomicFile, never()).failWrite(any(FileOutputStream.class)); 744 } 745 746 @Test testWriteDynamicSystemCodeNfcid2LockedWithServices_ReturnsTrue()747 public void testWriteDynamicSystemCodeNfcid2LockedWithServices_ReturnsTrue() throws Exception { 748 cache = new RegisteredNfcFServicesCache(mContext, mCallback, mAtomicFile); 749 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 750 751 boolean result = cache.writeDynamicSystemCodeNfcid2Locked(); 752 753 Assert.assertTrue(result); 754 verify(mAtomicFile).startWrite(); 755 verify(mAtomicFile).finishWrite(any(FileOutputStream.class)); 756 verify(mAtomicFile, never()).failWrite(any(FileOutputStream.class)); 757 verify(mSerializer, times(4)).attribute(any(), anyString(), anyString()); 758 } 759 760 @Test testReadDynamicSystemCodeNfcid2Locked()761 public void testReadDynamicSystemCodeNfcid2Locked() throws Exception { 762 when(mParser.getEventType()).thenReturn(XmlPullParser.START_TAG); 763 when(mParser.next()).thenReturn(XmlPullParser.END_TAG).thenReturn(XmlPullParser.END_DOCUMENT); 764 when(mParser.getName()).thenReturn("services").thenReturn("service"); 765 when(mParser.getDepth()).thenReturn(2); 766 when(mParser.getAttributeValue(any(), eq("component"))) 767 .thenReturn(WALLET_COMPONENT.flattenToString()); 768 when(mParser.getAttributeValue(any(), eq("uid"))).thenReturn("1"); 769 when(mParser.getAttributeValue(any(), eq("system-code"))).thenReturn(SYSTEM_CODE); 770 when(mParser.getAttributeValue(any(), eq("nfcid2"))).thenReturn(NFCID2); 771 cache = new RegisteredNfcFServicesCache(mContext, mCallback, mAtomicFile); 772 setUserServices(/* serviceInfoUid = */ SERVICE_UID, /* serviceInfoNfcid2 = */ ""); 773 774 cache.readDynamicSystemCodeNfcid2Locked(); 775 776 verify(mAtomicFile, never()).delete(); 777 verify(mParser, times(2)).next(); 778 verify(mParser, times(5)).getAttributeValue(any(), anyString()); 779 Assert.assertEquals(1, cache.mUserServices.get(USER_ID).dynamicSystemCode.size()); 780 Assert.assertEquals(1, cache.mUserServices.get(USER_ID).dynamicNfcid2.size()); 781 } 782 setResolveInfoList()783 private void setResolveInfoList() { 784 ArrayList<ResolveInfo> list = new ArrayList<ResolveInfo>(); 785 list.add(getResolveInfo(true, WALLET_HOLDER_PACKAGE_NAME, ON_HOST_SERVICE_NAME)); 786 list.add( 787 getResolveInfo(false, NON_PAYMENT_NFC_PACKAGE_NAME, NON_PAYMENT_SERVICE_NAME)); 788 when(mPackageManager.queryIntentServicesAsUser(any(), any(), any())).thenReturn(list); 789 } 790 getResolveInfo(boolean hasPermission, String packageName, String className)791 private ResolveInfo getResolveInfo(boolean hasPermission, String packageName, String className) { 792 when(mPackageManager.checkPermission(any(), eq(packageName))) 793 .thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED 794 : PackageManager.PERMISSION_DENIED); 795 796 ResolveInfo resolveInfo = new ResolveInfo(); 797 resolveInfo.serviceInfo = new ServiceInfo(); 798 resolveInfo.serviceInfo.permission = android.Manifest.permission.BIND_NFC_SERVICE; 799 resolveInfo.serviceInfo.exported = true; 800 resolveInfo.serviceInfo.packageName = packageName; 801 resolveInfo.serviceInfo.name = className; 802 return resolveInfo; 803 } 804 setUserServices(int serviceInfoUid, String serviceInfoNfcid2)805 private void setUserServices(int serviceInfoUid, String serviceInfoNfcid2) { 806 when(mNfcFServiceInfo.getSystemCode()).thenReturn(SYSTEM_CODE); 807 when(mNfcFServiceInfo.getUid()).thenReturn(serviceInfoUid); 808 when(mNfcFServiceInfo.getNfcid2()).thenReturn(serviceInfoNfcid2); 809 810 UserServices userServices = new UserServices(); 811 userServices.services.put(WALLET_COMPONENT, mNfcFServiceInfo); 812 userServices.dynamicSystemCode.put( 813 WALLET_COMPONENT, new DynamicSystemCode(SYSTEM_CODE_UID, SYSTEM_CODE)); 814 userServices.dynamicNfcid2.put(WALLET_COMPONENT, new DynamicNfcid2(NFCID2_UID, NFCID2)); 815 cache.mUserServices.put(USER_ID, userServices); 816 } 817 getBroadcastReceiverIntent()818 private Intent getBroadcastReceiverIntent() { 819 Intent intent = new Intent(InstrumentationRegistry.getInstrumentation().getTargetContext(), 820 RegisteredNfcFServicesCache.class); 821 intent.putExtra(Intent.EXTRA_UID, SERVICE_UID); 822 intent.putExtra(Intent.EXTRA_REPLACING, false); 823 intent.setData(Uri.EMPTY); 824 return intent; 825 } 826 }