/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.nfc.cardemulation; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.Context; import android.nfc.cardemulation.NfcFServiceInfo; import android.os.ParcelFileDescriptor; import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.nfc.cardemulation.RegisteredT3tIdentifiersCache.T3tIdentifier; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Locale; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; @RunWith(AndroidJUnit4.class) public class RegisteredT3tIdentifiersCacheTest { private static final String NFCID2 = "nfcid2"; private static final String SYSTEM_CODE = "system code"; private static final String T3TPMM = "t3tpmm"; private static final String ANOTHER_SYSTEM_CODE = "another system code"; private static final int USER_ID = 1; private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder"; private static final String ON_HOST_SERVICE_NAME = "com.android.test.walletroleholder.OnHostApduService"; private static final String NON_PAYMENT_NFC_PACKAGE_NAME = "com.android.test.nonpaymentnfc"; private static final String NON_PAYMENT_SERVICE_NAME = "com.android.test.nonpaymentnfc.NonPaymentApduService"; private static final ComponentName WALLET_HOLDER_SERVICE_COMPONENT = new ComponentName(WALLET_HOLDER_PACKAGE_NAME, ON_HOST_SERVICE_NAME); private static final ComponentName NON_PAYMENT_SERVICE_COMPONENT = new ComponentName(NON_PAYMENT_NFC_PACKAGE_NAME, NON_PAYMENT_SERVICE_NAME); @Mock private Context mContext; @Mock private NfcFServiceInfo mNfcFServiceInfo; @Mock private PrintWriter mPw; @Mock private ParcelFileDescriptor mPfd; @Mock private SystemCodeRoutingManager mRoutingManager; @Captor ArgumentCaptor<List<T3tIdentifier>> identifiersCaptor; private RegisteredT3tIdentifiersCache cache; private MockitoSession mStaticMockSession; @Before public void setUp() throws Exception { mStaticMockSession = ExtendedMockito.mockitoSession() .mockStatic(ParcelFileDescriptor.class) .strictness(Strictness.LENIENT) .startMocking(); MockitoAnnotations.initMocks(this); when(mNfcFServiceInfo.toString()).thenReturn(""); when(mNfcFServiceInfo.getSystemCode()).thenReturn(SYSTEM_CODE); when(mNfcFServiceInfo.getNfcid2()).thenReturn(NFCID2); when(mNfcFServiceInfo.getT3tPmm()).thenReturn(T3TPMM); when(mNfcFServiceInfo.getComponent()).thenReturn(NON_PAYMENT_SERVICE_COMPONENT); when(ParcelFileDescriptor.dup(any())).thenReturn(mPfd); } @After public void tearDown() { mStaticMockSession.finishMocking(); } @Test public void testConstructor() { cache = new RegisteredT3tIdentifiersCache(mContext); Assert.assertEquals(cache.mContext, mContext); Assert.assertNotNull(cache.mRoutingManager); } @Test public void testT3tIdentifierEquals_ReturnsTrue() { cache = new RegisteredT3tIdentifiersCache(mContext); T3tIdentifier firstIdentifier = cache.new T3tIdentifier(SYSTEM_CODE, NFCID2, T3TPMM); T3tIdentifier secondIdentifier = cache.new T3tIdentifier(SYSTEM_CODE.toUpperCase(Locale.ROOT), NFCID2, ""); boolean result = firstIdentifier.equals(secondIdentifier); Assert.assertTrue(result); } @Test public void testT3tIdentifierEquals_ReturnsFalse() { cache = new RegisteredT3tIdentifiersCache(mContext); T3tIdentifier firstIdentifier = cache.new T3tIdentifier(SYSTEM_CODE, NFCID2, T3TPMM); T3tIdentifier secondIdentifier = cache.new T3tIdentifier(ANOTHER_SYSTEM_CODE, NFCID2, T3TPMM); boolean result = firstIdentifier.equals(secondIdentifier); Assert.assertFalse(result); } @Test public void testResolveNfcid2() { cache = new RegisteredT3tIdentifiersCache(mContext); cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); NfcFServiceInfo result = cache.resolveNfcid2(NFCID2); Assert.assertEquals(result, mNfcFServiceInfo); } @Test public void testOnSecureNfcToggledWithNfcDisabled_ReturnsEarly() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mNfcEnabled = false; cache.onSecureNfcToggled(); verify(mRoutingManager, never()).configureRouting(any()); } @Test public void testOnSecureNfcToggledWithNfcEnabled_ConfiguresRouting() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mNfcEnabled = true; cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); cache.onSecureNfcToggled(); verify(mRoutingManager, times(2)).configureRouting(identifiersCaptor.capture()); List<T3tIdentifier> firstList = identifiersCaptor.getAllValues().get(0); List<T3tIdentifier> secondList = identifiersCaptor.getAllValues().get(1); Assert.assertEquals(1, firstList.size()); T3tIdentifier identifier = firstList.get(0); Assert.assertEquals(SYSTEM_CODE, identifier.systemCode); Assert.assertEquals(NFCID2, identifier.nfcid2); Assert.assertEquals(T3TPMM, identifier.t3tPmm); Assert.assertEquals(1, secondList.size()); Assert.assertEquals(secondList.get(0), identifier); } @Test public void testOnServicesUpdated() { cache = new RegisteredT3tIdentifiersCache(mContext); ArrayList<NfcFServiceInfo> serviceList = getServiceList(); cache.onServicesUpdated(USER_ID, serviceList); Assert.assertEquals(1, cache.mUserNfcFServiceInfo.size()); Assert.assertEquals(serviceList, cache.mUserNfcFServiceInfo.get(USER_ID)); } /** * Tests the case where the component passed in is null and mEnabledForegroundService is also * null. Ultimately, no update is performed. */ @Test public void testOnEnabledForegroundNfcFServiceChangedCase1() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mEnabledForegroundService = null; cache.mEnabledForegroundServiceUserId = USER_ID; cache.mNfcEnabled = true; cache.onEnabledForegroundNfcFServiceChanged(USER_ID, /* component = */ null); Assert.assertEquals(USER_ID, cache.mEnabledForegroundServiceUserId); Assert.assertNull(cache.mEnabledForegroundService); verify(mRoutingManager, never()).configureRouting(any()); } /** * Tests the case where the component passed in is null and mEnabledForegroundService is non-null. * Ultimately, routing is configured. */ @Test public void testOnEnabledForegroundNfcFServiceChangedCase2() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; cache.mEnabledForegroundServiceUserId = USER_ID; cache.mNfcEnabled = true; cache.onEnabledForegroundNfcFServiceChanged(USER_ID, /* component = */ null); Assert.assertNull(cache.mEnabledForegroundService); Assert.assertEquals(-1, cache.mEnabledForegroundServiceUserId); verify(mRoutingManager).configureRouting(identifiersCaptor.capture()); Assert.assertTrue(identifiersCaptor.getValue().isEmpty()); } /** * Tests the case where the component passed in is non-null and mEnabledForegroundService is null. * Ultimately, routing is configured. */ @Test public void testOnEnabledForegroundNfcFServiceChangedCase3() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mEnabledForegroundService = null; cache.mEnabledForegroundServiceUserId = -1; cache.mUserNfcFServiceInfo.put(USER_ID, getServiceList()); cache.mNfcEnabled = true; cache.onEnabledForegroundNfcFServiceChanged(USER_ID, NON_PAYMENT_SERVICE_COMPONENT); Assert.assertEquals(NON_PAYMENT_SERVICE_COMPONENT, cache.mEnabledForegroundService); Assert.assertEquals(USER_ID, cache.mEnabledForegroundServiceUserId); verify(mRoutingManager).configureRouting(identifiersCaptor.capture()); Assert.assertEquals(1, identifiersCaptor.getValue().size()); T3tIdentifier identifier = identifiersCaptor.getValue().get(0); Assert.assertEquals(SYSTEM_CODE, identifier.systemCode); Assert.assertEquals(NFCID2, identifier.nfcid2); Assert.assertEquals(T3TPMM, identifier.t3tPmm); } /** * Tests the case where the component passed in is non-null and mEnabledForegroundService is also * non-null. Ultimately, no action is taken. */ @Test public void testOnEnabledForegroundNfcFServiceChangedCase4() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; cache.mEnabledForegroundServiceUserId = USER_ID; cache.mNfcEnabled = true; cache.onEnabledForegroundNfcFServiceChanged(USER_ID, NON_PAYMENT_SERVICE_COMPONENT); Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, cache.mEnabledForegroundService); Assert.assertEquals(USER_ID, cache.mEnabledForegroundServiceUserId); verify(mRoutingManager, never()).configureRouting(any()); } @Test public void testOnNfcEnabled() { cache = new RegisteredT3tIdentifiersCache(mContext); cache.onNfcEnabled(); Assert.assertTrue(cache.mNfcEnabled); } @Test public void testOnNfcDisabled() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mNfcEnabled = true; cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; cache.mEnabledForegroundServiceUserId = USER_ID; cache.onNfcDisabled(); Assert.assertFalse(cache.mNfcEnabled); Assert.assertTrue(cache.mForegroundT3tIdentifiersCache.isEmpty()); Assert.assertNull(cache.mEnabledForegroundService); Assert.assertEquals(-1, cache.mEnabledForegroundServiceUserId); verify(mRoutingManager).onNfccRoutingTableCleared(); } @Test public void testOnUserSwitched() { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mNfcEnabled = true; cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); cache.mEnabledForegroundService = WALLET_HOLDER_SERVICE_COMPONENT; cache.mEnabledForegroundServiceUserId = USER_ID; cache.onUserSwitched(); Assert.assertTrue(cache.mForegroundT3tIdentifiersCache.isEmpty()); Assert.assertNull(cache.mEnabledForegroundService); Assert.assertEquals(-1, cache.mEnabledForegroundServiceUserId); verify(mRoutingManager).configureRouting(identifiersCaptor.capture()); Assert.assertTrue(identifiersCaptor.getValue().isEmpty()); } @Test public void testDump() throws Exception { cache = new RegisteredT3tIdentifiersCache(mContext, mRoutingManager); cache.mForegroundT3tIdentifiersCache.put(NFCID2, mNfcFServiceInfo); cache.dump(/* fd = */ null, mPw, /* args = */ null); verify(mPw, times(5)).println(anyString()); verify(mPfd).close(); verify(mNfcFServiceInfo).dump(any(), any(), any()); verify(mRoutingManager).dump(any(), any(), any()); } private ArrayList<NfcFServiceInfo> getServiceList() { ArrayList<NfcFServiceInfo> list = new ArrayList<NfcFServiceInfo>(); list.add(mNfcFServiceInfo); return list; } }