• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }